From 5d238771c602bd37e18c584e9532852ad248447d Mon Sep 17 00:00:00 2001 From: jinzh Date: Fri, 20 Dec 2024 12:29:51 +0800 Subject: [PATCH] second name --- .bowerrc | 11 + .env.sample | 11 + .gitignore | 18 + LICENSE | 191 + addons/.gitkeep | 1 + application/.htaccess | 1 + application/admin/behavior/AdminLog.php | 14 + application/admin/command/Addon.php | 344 + .../admin/command/Addon/stubs/addon.stub | 54 + .../admin/command/Addon/stubs/config.stub | 44 + .../admin/command/Addon/stubs/controller.stub | 15 + .../admin/command/Addon/stubs/info.stub | 7 + application/admin/command/Api.php | 195 + application/admin/command/Api/lang/zh-cn.php | 25 + .../admin/command/Api/library/Builder.php | 253 + .../admin/command/Api/library/Extractor.php | 544 + .../admin/command/Api/template/index.html | 654 + application/admin/command/Crud.php | 1770 + application/admin/command/Crud/stubs/add.stub | 11 + .../admin/command/Crud/stubs/controller.stub | 37 + .../command/Crud/stubs/controllerindex.stub | 34 + .../admin/command/Crud/stubs/edit.stub | 11 + .../command/Crud/stubs/html/checkbox.stub | 6 + .../Crud/stubs/html/fieldlist-template.stub | 20 + .../command/Crud/stubs/html/fieldlist.stub | 10 + .../command/Crud/stubs/html/heading-html.stub | 10 + .../admin/command/Crud/stubs/html/radio.stub | 6 + .../Crud/stubs/html/recyclebin-html.stub | 1 + .../admin/command/Crud/stubs/html/select.stub | 6 + .../admin/command/Crud/stubs/html/switch.stub | 5 + .../admin/command/Crud/stubs/index.stub | 35 + .../admin/command/Crud/stubs/javascript.stub | 48 + .../admin/command/Crud/stubs/lang.stub | 5 + .../command/Crud/stubs/mixins/checkbox.stub | 8 + .../command/Crud/stubs/mixins/datetime.stub | 6 + .../admin/command/Crud/stubs/mixins/enum.stub | 1 + .../command/Crud/stubs/mixins/import.stub | 4 + .../command/Crud/stubs/mixins/modelinit.stub | 8 + .../mixins/modelrelationmethod-hasmany.stub | 5 + .../stubs/mixins/modelrelationmethod.stub | 5 + .../command/Crud/stubs/mixins/multiple.stub | 8 + .../command/Crud/stubs/mixins/radio.stub | 7 + .../Crud/stubs/mixins/recyclebinjs.stub | 60 + .../command/Crud/stubs/mixins/select.stub | 7 + .../admin/command/Crud/stubs/model.stub | 40 + .../admin/command/Crud/stubs/recyclebin.stub | 25 + .../command/Crud/stubs/relationmodel.stub | 12 + .../admin/command/Crud/stubs/validate.stub | 27 + application/admin/command/Install.php | 329 + .../admin/command/Install/fastadmin.sql | 605 + .../admin/command/Install/install.html | 316 + .../admin/command/Install/install.lock | 1 + application/admin/command/Install/zh-cn.php | 35 + application/admin/command/Menu.php | 327 + application/admin/command/Min.php | 162 + application/admin/command/Min/r.js | 27959 ++++++++++++++++ application/admin/command/Min/stubs/css.stub | 6 + application/admin/command/Min/stubs/js.stub | 11 + application/admin/common.php | 197 + application/admin/config.php | 8 + application/admin/controller/Addon.php | 454 + application/admin/controller/Ajax.php | 313 + application/admin/controller/Category.php | 158 + application/admin/controller/Command.php | 248 + application/admin/controller/Contestants.php | 329 + application/admin/controller/Countryflag.php | 75 + application/admin/controller/Dashboard.php | 84 + application/admin/controller/Docs.php | 432 + application/admin/controller/Epay.php | 39 + application/admin/controller/Index.php | 138 + application/admin/controller/Match.php | 28 + application/admin/controller/Renzheng.php | 37 + application/admin/controller/Renzheng/Php.php | 37 + application/admin/controller/Schedule | 0 application/admin/controller/Schedule.php | 215 + application/admin/controller/Screen.php | 88 + application/admin/controller/Third.php | 66 + application/admin/controller/auth/Admin.php | 297 + .../admin/controller/auth/Adminlog.php | 133 + application/admin/controller/auth/Group.php | 317 + application/admin/controller/auth/Rule.php | 159 + .../admin/controller/club/Complain.php | 37 + application/admin/controller/club/Invate.php | 189 + application/admin/controller/cms/Ajax.php | 300 + application/admin/controller/cms/Archives.php | 993 + application/admin/controller/cms/Autolink.php | 35 + application/admin/controller/cms/Block.php | 104 + application/admin/controller/cms/Builder.php | 111 + application/admin/controller/cms/Channel.php | 316 + application/admin/controller/cms/Comment.php | 138 + application/admin/controller/cms/Config.php | 70 + application/admin/controller/cms/Diydata.php | 174 + application/admin/controller/cms/Diyform.php | 117 + application/admin/controller/cms/Fields.php | 403 + application/admin/controller/cms/League.php | 690 + .../admin/controller/cms/Leaguemaster.php | 563 + .../admin/controller/cms/Leagueround.php | 831 + .../admin/controller/cms/Leaguesort.php | 322 + .../admin/controller/cms/Leaguexport.php | 296 + application/admin/controller/cms/Modelx.php | 85 + application/admin/controller/cms/Order.php | 65 + application/admin/controller/cms/Page.php | 203 + application/admin/controller/cms/Ranking.php | 776 + .../admin/controller/cms/SearchLog.php | 35 + application/admin/controller/cms/Special.php | 123 + .../admin/controller/cms/SpiderLog.php | 41 + .../admin/controller/cms/Statistics.php | 402 + application/admin/controller/cms/Tag.php | 90 + .../admin/controller/fastim/Config.php | 65 + .../admin/controller/fastim/CsrGroup.php | 33 + .../admin/controller/fastim/FastReply.php | 177 + .../admin/controller/fastim/Groupchat.php | 133 + application/admin/controller/fastim/Kbs.php | 144 + .../admin/controller/fastim/Records.php | 145 + .../admin/controller/fastim/Report.php | 89 + .../admin/controller/fastim/Session.php | 155 + application/admin/controller/fastim/User.php | 268 + .../admin/controller/general/Attachment.php | 160 + .../admin/controller/general/Config.php | 311 + .../admin/controller/general/Profile.php | 83 + .../admin/controller/shopro/Category.php | 218 + .../admin/controller/shopro/Common.php | 31 + .../admin/controller/shopro/Config.php | 341 + .../admin/controller/shopro/Coupon.php | 292 + .../admin/controller/shopro/Dashboard.php | 241 + .../admin/controller/shopro/Feedback.php | 112 + .../admin/controller/shopro/PayConfig.php | 223 + .../admin/controller/shopro/Paylog.php | 37 + application/admin/controller/shopro/Share.php | 30 + application/admin/controller/shopro/Vip.php | 37 + .../admin/controller/shopro/Withdraw.php | 126 + .../controller/shopro/activity/Activity.php | 321 + .../controller/shopro/activity/Groupon.php | 124 + .../admin/controller/shopro/app/ScoreShop.php | 313 + .../controller/shopro/app/mplive/Goods.php | 163 + .../controller/shopro/app/mplive/Index.php | 81 + .../controller/shopro/app/mplive/Room.php | 201 + .../controller/shopro/chat/CommonWord.php | 129 + .../shopro/chat/CustomerService.php | 205 + .../admin/controller/shopro/chat/Index.php | 38 + .../admin/controller/shopro/chat/Question.php | 133 + .../admin/controller/shopro/chat/Record.php | 37 + .../admin/controller/shopro/chat/User.php | 62 + .../controller/shopro/commission/Agent.php | 250 + .../controller/shopro/commission/Goods.php | 89 + .../controller/shopro/commission/Level.php | 144 + .../controller/shopro/commission/Log.php | 50 + .../controller/shopro/commission/Order.php | 323 + .../controller/shopro/commission/Reward.php | 108 + .../admin/controller/shopro/data/Area.php | 170 + .../admin/controller/shopro/data/Express.php | 156 + .../admin/controller/shopro/data/FakeUser.php | 215 + .../admin/controller/shopro/data/Faq.php | 138 + .../admin/controller/shopro/data/Page.php | 156 + .../admin/controller/shopro/data/Richtext.php | 170 + .../controller/shopro/decorate/Designer.php | 47 + .../admin/controller/shopro/decorate/Page.php | 76 + .../controller/shopro/decorate/Template.php | 286 + .../controller/shopro/dispatch/Dispatch.php | 232 + .../admin/controller/shopro/goods/Comment.php | 247 + .../admin/controller/shopro/goods/Goods.php | 444 + .../admin/controller/shopro/goods/Service.php | 157 + .../controller/shopro/goods/SkuPrice.php | 57 + .../controller/shopro/goods/StockLog.php | 48 + .../controller/shopro/goods/StockWarning.php | 120 + .../controller/shopro/notification/Config.php | 227 + .../shopro/notification/Notification.php | 128 + .../notification/traits/Notification.php | 183 + .../controller/shopro/order/Aftersale.php | 338 + .../admin/controller/shopro/order/Invoice.php | 62 + .../admin/controller/shopro/order/Order.php | 1225 + .../admin/controller/shopro/trade/Order.php | 199 + .../controller/shopro/traits/SkuPrice.php | 303 + .../admin/controller/shopro/user/Coupon.php | 33 + .../admin/controller/shopro/user/Group.php | 27 + .../admin/controller/shopro/user/User.php | 138 + .../controller/shopro/user/WalletLog.php | 90 + .../admin/controller/shopro/wechat/Admin.php | 269 + .../admin/controller/shopro/wechat/Config.php | 30 + .../controller/shopro/wechat/Material.php | 178 + .../admin/controller/shopro/wechat/Menu.php | 223 + .../admin/controller/shopro/wechat/Reply.php | 143 + application/admin/controller/user/Club.php | 160 + application/admin/controller/user/Group.php | 52 + application/admin/controller/user/Player.php | 235 + application/admin/controller/user/Rule.php | 108 + application/admin/controller/user/User.php | 107 + application/admin/lang/zh-cn.php | 228 + application/admin/lang/zh-cn/addon.php | 118 + application/admin/lang/zh-cn/ajax.php | 3 + application/admin/lang/zh-cn/auth/admin.php | 14 + application/admin/lang/zh-cn/auth/group.php | 12 + application/admin/lang/zh-cn/auth/rule.php | 28 + application/admin/lang/zh-cn/category.php | 18 + application/admin/lang/zh-cn/cms/ajax.php | 7 + application/admin/lang/zh-cn/cms/archives.php | 85 + application/admin/lang/zh-cn/cms/autolink.php | 14 + application/admin/lang/zh-cn/cms/block.php | 16 + application/admin/lang/zh-cn/cms/channel.php | 46 + application/admin/lang/zh-cn/cms/comment.php | 21 + application/admin/lang/zh-cn/cms/config.php | 6 + application/admin/lang/zh-cn/cms/diydata.php | 15 + application/admin/lang/zh-cn/cms/diyform.php | 31 + application/admin/lang/zh-cn/cms/fields.php | 64 + application/admin/lang/zh-cn/cms/modelx.php | 23 + application/admin/lang/zh-cn/cms/order.php | 25 + application/admin/lang/zh-cn/cms/page.php | 33 + .../admin/lang/zh-cn/cms/search_log.php | 8 + application/admin/lang/zh-cn/cms/special.php | 31 + .../admin/lang/zh-cn/cms/spider_log.php | 20 + application/admin/lang/zh-cn/cms/tag.php | 12 + application/admin/lang/zh-cn/command.php | 16 + application/admin/lang/zh-cn/config.php | 9 + application/admin/lang/zh-cn/countryflag.php | 6 + application/admin/lang/zh-cn/dashboard.php | 50 + application/admin/lang/zh-cn/docs.php | 19 + .../admin/lang/zh-cn/fastim/config.php | 36 + .../admin/lang/zh-cn/fastim/csr_group.php | 14 + .../admin/lang/zh-cn/fastim/fast_reply.php | 16 + .../admin/lang/zh-cn/fastim/groupchat.php | 25 + application/admin/lang/zh-cn/fastim/kbs.php | 20 + .../admin/lang/zh-cn/fastim/records.php | 36 + .../admin/lang/zh-cn/fastim/report.php | 22 + .../admin/lang/zh-cn/fastim/session.php | 18 + application/admin/lang/zh-cn/fastim/user.php | 41 + .../admin/lang/zh-cn/general/attachment.php | 41 + .../admin/lang/zh-cn/general/config.php | 83 + .../admin/lang/zh-cn/general/profile.php | 14 + application/admin/lang/zh-cn/index.php | 64 + application/admin/lang/zh-cn/renzheng.php | 5 + application/admin/lang/zh-cn/renzheng/php.php | 5 + application/admin/lang/zh-cn/shopro/vip.php | 9 + .../admin/lang/zh-cn/shopro/vip/paylog.php | 9 + application/admin/lang/zh-cn/third.php | 17 + application/admin/lang/zh-cn/user/club.php | 7 + application/admin/lang/zh-cn/user/group.php | 10 + application/admin/lang/zh-cn/user/player.php | 42 + application/admin/lang/zh-cn/user/rule.php | 16 + application/admin/lang/zh-cn/user/user.php | 34 + application/admin/library/Auth.php | 530 + application/admin/library/traits/Backend.php | 481 + application/admin/model/Addonnews.php | 20 + application/admin/model/Admin.php | 34 + application/admin/model/AdminLog.php | 114 + application/admin/model/AuthGroup.php | 21 + application/admin/model/AuthGroupAccess.php | 10 + application/admin/model/AuthRule.php | 62 + application/admin/model/Club.php | 29 + application/admin/model/ClubComplain.php | 26 + application/admin/model/ClubInvate.php | 26 + application/admin/model/ClubMatchApply.php | 26 + application/admin/model/ClubPlayercheck.php | 26 + application/admin/model/Combine.php | 22 + application/admin/model/CombineViews.php | 22 + application/admin/model/Command.php | 59 + application/admin/model/Countryflag.php | 40 + .../admin/model/LeagueClubIntegral.php | 28 + application/admin/model/LeagueIntegral.php | 28 + application/admin/model/LeagueMaster.php | 28 + application/admin/model/MatchCompetition.php | 19 + application/admin/model/MatchContestant.php | 42 + application/admin/model/MatchRanking | 0 application/admin/model/MatchRanking.php | 21 + application/admin/model/MatchSchedule.php | 15 + application/admin/model/MatchScreen.php | 15 + application/admin/model/MessageLog.php | 25 + application/admin/model/PlayerCheck.php | 38 + application/admin/model/Players.php | 108 + application/admin/model/Renzheng.php | 49 + application/admin/model/Renzheng/Php.php | 49 + application/admin/model/Third.php | 56 + application/admin/model/User.php | 119 + application/admin/model/UserGroup.php | 34 + application/admin/model/UserRule.php | 67 + application/admin/model/Userwechat.php | 24 + application/admin/model/club/Complain.php | 41 + application/admin/model/club/Invate.php | 49 + application/admin/model/cms/Archives.php | 331 + application/admin/model/cms/Autolink.php | 60 + application/admin/model/cms/Block.php | 81 + application/admin/model/cms/Channel.php | 325 + application/admin/model/cms/ChannelAdmin.php | 38 + application/admin/model/cms/Comment.php | 159 + application/admin/model/cms/Diyform.php | 192 + application/admin/model/cms/Fields.php | 185 + .../admin/model/cms/Leaguearchives.php | 40 + application/admin/model/cms/Modelx.php | 85 + application/admin/model/cms/Order.php | 57 + application/admin/model/cms/Page.php | 120 + application/admin/model/cms/SearchLog.php | 40 + application/admin/model/cms/Special.php | 198 + application/admin/model/cms/SpiderLog.php | 124 + application/admin/model/cms/Tag.php | 91 + application/admin/model/cms/Taggable.php | 26 + application/admin/model/fastim/Config.php | 121 + application/admin/model/fastim/CsrGroup.php | 59 + application/admin/model/fastim/FastReply.php | 49 + application/admin/model/fastim/Groupchat.php | 86 + application/admin/model/fastim/Kbs.php | 54 + application/admin/model/fastim/Records.php | 107 + application/admin/model/fastim/Report.php | 83 + application/admin/model/fastim/Session.php | 65 + application/admin/model/fastim/User.php | 82 + application/admin/model/shopro/Admin.php | 53 + application/admin/model/shopro/Cart.php | 75 + application/admin/model/shopro/Category.php | 35 + .../admin/model/shopro/ClubThirdOauth.php | 9 + application/admin/model/shopro/Common.php | 84 + application/admin/model/shopro/Config.php | 192 + application/admin/model/shopro/Coupon.php | 322 + application/admin/model/shopro/Feedback.php | 36 + application/admin/model/shopro/Pay.php | 81 + application/admin/model/shopro/PayConfig.php | 46 + application/admin/model/shopro/Refund.php | 38 + .../admin/model/shopro/SearchHistory.php | 15 + application/admin/model/shopro/Share.php | 142 + application/admin/model/shopro/ThirdOauth.php | 9 + application/admin/model/shopro/Vip.php | 73 + application/admin/model/shopro/Withdraw.php | 83 + .../admin/model/shopro/WithdrawLog.php | 13 + .../admin/model/shopro/activity/Activity.php | 407 + .../admin/model/shopro/activity/GiftLog.php | 73 + .../admin/model/shopro/activity/Groupon.php | 98 + .../model/shopro/activity/GrouponLog.php | 43 + .../admin/model/shopro/activity/Order.php | 93 + .../admin/model/shopro/activity/Signin.php | 21 + .../admin/model/shopro/activity/SkuPrice.php | 128 + .../admin/model/shopro/app/ScoreSkuPrice.php | 63 + .../admin/model/shopro/app/mplive/Goods.php | 53 + .../admin/model/shopro/app/mplive/Room.php | 99 + .../admin/model/shopro/chat/CommonWord.php | 23 + .../model/shopro/chat/CustomerService.php | 57 + .../model/shopro/chat/CustomerServiceUser.php | 62 + .../admin/model/shopro/chat/Question.php | 23 + .../admin/model/shopro/chat/Record.php | 94 + .../admin/model/shopro/chat/ServiceLog.php | 22 + application/admin/model/shopro/chat/User.php | 33 + .../model/shopro/chat/traits/ChatCommon.php | 31 + .../admin/model/shopro/commission/Agent.php | 83 + .../shopro/commission/CommissionGoods.php | 47 + .../admin/model/shopro/commission/Level.php | 20 + .../admin/model/shopro/commission/Log.php | 232 + .../admin/model/shopro/commission/Order.php | 116 + .../admin/model/shopro/commission/Reward.php | 112 + application/admin/model/shopro/data/Area.php | 19 + .../admin/model/shopro/data/Express.php | 15 + .../admin/model/shopro/data/FakeUser.php | 54 + application/admin/model/shopro/data/Faq.php | 20 + application/admin/model/shopro/data/Page.php | 21 + .../admin/model/shopro/data/Richtext.php | 18 + .../admin/model/shopro/data/WechatExpress.php | 15 + .../admin/model/shopro/decorate/Decorate.php | 75 + .../admin/model/shopro/decorate/Page.php | 37 + .../admin/model/shopro/dispatch/Dispatch.php | 31 + .../shopro/dispatch/DispatchAutosend.php | 35 + .../model/shopro/dispatch/DispatchExpress.php | 28 + .../admin/model/shopro/goods/Comment.php | 94 + .../admin/model/shopro/goods/Goods.php | 535 + .../admin/model/shopro/goods/Service.php | 13 + application/admin/model/shopro/goods/Sku.php | 21 + .../admin/model/shopro/goods/SkuPrice.php | 38 + .../admin/model/shopro/goods/StockLog.php | 33 + .../admin/model/shopro/goods/StockWarning.php | 34 + .../model/shopro/notification/Config.php | 20 + .../shopro/notification/Notification.php | 66 + .../admin/model/shopro/order/Action.php | 40 + .../admin/model/shopro/order/Address.php | 19 + .../admin/model/shopro/order/Aftersale.php | 286 + .../admin/model/shopro/order/AftersaleLog.php | 78 + .../admin/model/shopro/order/Express.php | 49 + .../admin/model/shopro/order/ExpressLog.php | 37 + .../admin/model/shopro/order/Invoice.php | 184 + .../admin/model/shopro/order/Order.php | 361 + .../admin/model/shopro/order/OrderItem.php | 227 + .../shopro/order/traits/OrderItemStatus.php | 268 + .../model/shopro/order/traits/OrderScope.php | 211 + .../model/shopro/order/traits/OrderStatus.php | 411 + .../admin/model/shopro/trade/Order.php | 179 + .../admin/model/shopro/traits/ModelAttr.php | 222 + .../admin/model/shopro/user/Account.php | 26 + .../admin/model/shopro/user/Address.php | 24 + .../admin/model/shopro/user/CheckInfo.php | 24 + .../admin/model/shopro/user/Coupon.php | 308 + .../admin/model/shopro/user/GoodsLog.php | 62 + .../admin/model/shopro/user/Invoice.php | 25 + application/admin/model/shopro/user/User.php | 136 + .../admin/model/shopro/user/WalletLog.php | 79 + application/admin/model/shopro/vip/Paylog.php | 48 + .../admin/model/shopro/wechat/Material.php | 28 + .../admin/model/shopro/wechat/Menu.php | 31 + .../admin/model/shopro/wechat/Reply.php | 63 + application/admin/model/user/Club.php | 40 + application/admin/service/LeagueService.php | 577 + application/admin/service/RankingService.php | 1712 + application/admin/service/ScheduleService.php | 88 + application/admin/service/ScreenService.php | 60 + application/admin/service/Service.php | 22 + application/admin/tags.php | 18 + application/admin/validate/Admin.php | 57 + application/admin/validate/AuthRule.php | 52 + application/admin/validate/Category.php | 27 + application/admin/validate/Command.php | 27 + application/admin/validate/Countryflag.php | 27 + application/admin/validate/Renzheng.php | 27 + application/admin/validate/Renzheng/Php.php | 27 + application/admin/validate/Third.php | 26 + application/admin/validate/User.php | 50 + application/admin/validate/UserGroup.php | 27 + application/admin/validate/UserRule.php | 27 + application/admin/validate/cms/Archives.php | 26 + application/admin/validate/cms/Autolink.php | 27 + application/admin/validate/cms/Block.php | 26 + application/admin/validate/cms/Channel.php | 26 + application/admin/validate/cms/Comment.php | 26 + application/admin/validate/cms/Fields.php | 58 + application/admin/validate/cms/Modelx.php | 26 + application/admin/validate/cms/Order.php | 26 + application/admin/validate/cms/Page.php | 26 + application/admin/validate/cms/SearchLog.php | 27 + application/admin/validate/cms/Special.php | 27 + application/admin/validate/cms/SpiderLog.php | 27 + application/admin/validate/cms/Tag.php | 26 + .../admin/validate/fastim/CsrGroup.php | 25 + .../admin/validate/fastim/FastReply.php | 25 + .../admin/validate/fastim/Groupchat.php | 25 + application/admin/validate/fastim/Kbs.php | 25 + application/admin/validate/fastim/Records.php | 27 + application/admin/validate/fastim/Report.php | 25 + application/admin/validate/fastim/Session.php | 25 + application/admin/validate/fastim/User.php | 25 + .../admin/validate/shopro/Category.php | 31 + application/admin/validate/shopro/Coupon.php | 44 + .../admin/validate/shopro/PayConfig.php | 71 + application/admin/validate/shopro/Vip.php | 27 + .../validate/shopro/activity/Activity.php | 30 + .../admin/validate/shopro/app/ScoreShop.php | 27 + .../admin/validate/shopro/chat/CommonWord.php | 25 + .../validate/shopro/chat/CustomerService.php | 29 + .../admin/validate/shopro/chat/Question.php | 25 + .../admin/validate/shopro/data/FakeUser.php | 36 + .../admin/validate/shopro/data/Faq.php | 33 + .../validate/shopro/dispatch/Dispatch.php | 41 + .../admin/validate/shopro/goods/Comment.php | 30 + .../admin/validate/shopro/goods/Goods.php | 45 + .../admin/validate/shopro/goods/Service.php | 21 + .../validate/shopro/goods/StockWarning.php | 23 + .../admin/validate/shopro/order/Aftersale.php | 29 + .../admin/validate/shopro/order/Order.php | 75 + .../validate/shopro/traits/CustomRule.php | 31 + .../admin/validate/shopro/user/User.php | 130 + .../admin/validate/shopro/vip/Paylog.php | 27 + application/admin/validate/user/Club.php | 27 + application/admin/view/addon/add.html | 0 application/admin/view/addon/config.html | 136 + application/admin/view/addon/index.html | 302 + application/admin/view/auth/admin/add.html | 52 + application/admin/view/auth/admin/edit.html | 58 + application/admin/view/auth/admin/index.html | 21 + .../admin/view/auth/adminlog/detail.html | 27 + .../admin/view/auth/adminlog/index.html | 21 + application/admin/view/auth/group/add.html | 38 + application/admin/view/auth/group/edit.html | 38 + application/admin/view/auth/group/index.html | 21 + application/admin/view/auth/rule/add.html | 87 + application/admin/view/auth/rule/edit.html | 87 + application/admin/view/auth/rule/index.html | 35 + application/admin/view/auth/rule/tpl.html | 43 + application/admin/view/category/add.html | 100 + application/admin/view/category/edit.html | 96 + application/admin/view/category/index.html | 36 + application/admin/view/club/complain/add.html | 28 + .../admin/view/club/complain/edit.html | 28 + .../admin/view/club/complain/index.html | 35 + application/admin/view/club/invate/add.html | 40 + application/admin/view/club/invate/edit.html | 49 + application/admin/view/club/invate/index.html | 45 + .../admin/view/club/invate/recyclebin.html | 25 + application/admin/view/cms/archives/add.html | 289 + .../view/cms/archives/club_match_list.html | 105 + .../admin/view/cms/archives/common.html | 71 + .../admin/view/cms/archives/content.html | 42 + .../admin/view/cms/archives/contestant.html | 105 + .../admin/view/cms/archives/course.html | 1662 + application/admin/view/cms/archives/edit.html | 300 + .../admin/view/cms/archives/index.html | 211 + .../admin/view/cms/archives/league.html | 432 + .../view/cms/archives/league_allplayer.html | 105 + .../view/cms/archives/league_course.html | 1656 + .../view/cms/archives/league_wholeplayer.html | 153 + .../view/cms/archives/leaguecontestant.html | 146 + .../admin/view/cms/archives/recyclebin.html | 23 + .../admin/view/cms/archives/search.html | 45 + application/admin/view/cms/archives/test.html | 432 + .../admin/view/cms/archives/uploadfile.html | 139 + .../view/cms/archives/visualization.html | 417 + application/admin/view/cms/autolink/add.html | 64 + application/admin/view/cms/autolink/edit.html | 64 + .../admin/view/cms/autolink/index.html | 44 + .../admin/view/cms/autolink/recyclebin.html | 25 + application/admin/view/cms/block/add.html | 114 + application/admin/view/cms/block/edit.html | 120 + application/admin/view/cms/block/index.html | 39 + application/admin/view/cms/builder/index.html | 898 + application/admin/view/cms/channel/add.html | 204 + application/admin/view/cms/channel/admin.html | 30 + application/admin/view/cms/channel/edit.html | 219 + application/admin/view/cms/channel/index.html | 44 + application/admin/view/cms/comment/add.html | 82 + application/admin/view/cms/comment/edit.html | 82 + application/admin/view/cms/comment/index.html | 42 + .../admin/view/cms/comment/recyclebin.html | 23 + application/admin/view/cms/common/fields.html | 157 + application/admin/view/cms/common/links.html | 106 + application/admin/view/cms/diydata/add.html | 29 + application/admin/view/cms/diydata/edit.html | 29 + .../admin/view/cms/diydata/fields.html | 102 + application/admin/view/cms/diydata/index.html | 37 + application/admin/view/cms/diyform/add.html | 151 + application/admin/view/cms/diyform/edit.html | 151 + application/admin/view/cms/diyform/index.html | 34 + application/admin/view/cms/diyform/tpl.html | 66 + application/admin/view/cms/fields/add.html | 250 + .../admin/view/cms/fields/archives.html | 60 + application/admin/view/cms/fields/edit.html | 207 + application/admin/view/cms/fields/index.html | 38 + application/admin/view/cms/modelx/add.html | 39 + application/admin/view/cms/modelx/edit.html | 43 + application/admin/view/cms/modelx/index.html | 25 + application/admin/view/cms/modelx/tpl.html | 66 + application/admin/view/cms/order/add.html | 82 + application/admin/view/cms/order/edit.html | 82 + application/admin/view/cms/order/index.html | 43 + application/admin/view/cms/page/add.html | 126 + application/admin/view/cms/page/edit.html | 138 + application/admin/view/cms/page/index.html | 42 + .../admin/view/cms/page/recyclebin.html | 23 + application/admin/view/cms/page/select.html | 29 + .../admin/view/cms/search_log/add.html | 32 + .../admin/view/cms/search_log/edit.html | 32 + .../admin/view/cms/search_log/index.html | 32 + application/admin/view/cms/special/add.html | 144 + application/admin/view/cms/special/edit.html | 144 + application/admin/view/cms/special/index.html | 48 + .../admin/view/cms/special/recyclebin.html | 23 + .../admin/view/cms/spider_log/add.html | 58 + .../admin/view/cms/spider_log/edit.html | 64 + .../admin/view/cms/spider_log/index.html | 36 + .../admin/view/cms/statistics/index.html | 537 + application/admin/view/cms/tag/add.html | 31 + application/admin/view/cms/tag/edit.html | 49 + application/admin/view/cms/tag/index.html | 21 + application/admin/view/command/add.html | 430 + application/admin/view/command/detail.html | 42 + application/admin/view/command/index.html | 25 + application/admin/view/common/control.html | 77 + application/admin/view/common/header.html | 128 + application/admin/view/common/menu.html | 39 + application/admin/view/common/meta.html | 26 + application/admin/view/common/script.html | 1 + application/admin/view/countryflag/add.html | 59 + application/admin/view/countryflag/edit.html | 59 + application/admin/view/countryflag/index.html | 35 + application/admin/view/dashboard/index.html | 403 + application/admin/view/docs/add.html | 79 + application/admin/view/docs/edit.html | 87 + application/admin/view/docs/index.html | 32 + .../admin/view/fastim/config/index.html | 123 + .../admin/view/fastim/csr_group/add.html | 40 + .../admin/view/fastim/csr_group/edit.html | 40 + .../admin/view/fastim/csr_group/index.html | 45 + .../view/fastim/csr_group/recyclebin.html | 25 + .../admin/view/fastim/fast_reply/add.html | 41 + .../admin/view/fastim/fast_reply/edit.html | 41 + .../admin/view/fastim/fast_reply/index.html | 44 + .../view/fastim/fast_reply/recyclebin.html | 25 + .../admin/view/fastim/groupchat/edit.html | 96 + .../admin/view/fastim/groupchat/index.html | 25 + .../admin/view/fastim/groupchat/record.html | 28 + .../view/fastim/groupchat/recyclebin.html | 25 + application/admin/view/fastim/kbs/add.html | 71 + application/admin/view/fastim/kbs/edit.html | 71 + application/admin/view/fastim/kbs/index.html | 45 + .../admin/view/fastim/kbs/recyclebin.html | 25 + .../admin/view/fastim/records/edit.html | 161 + .../admin/view/fastim/records/index.html | 24 + .../admin/view/fastim/report/edit.html | 78 + .../admin/view/fastim/report/index.html | 44 + .../admin/view/fastim/session/add.html | 46 + .../admin/view/fastim/session/edit.html | 46 + .../admin/view/fastim/session/index.html | 22 + .../admin/view/fastim/session/record.html | 47 + application/admin/view/fastim/user/add.html | 124 + application/admin/view/fastim/user/edit.html | 141 + application/admin/view/fastim/user/index.html | 35 + .../admin/view/general/attachment/add.html | 62 + .../admin/view/general/attachment/edit.html | 89 + .../admin/view/general/attachment/index.html | 53 + .../admin/view/general/attachment/select.html | 47 + .../admin/view/general/config/index.html | 356 + .../admin/view/general/profile/index.html | 118 + application/admin/view/index/index.html | 56 + application/admin/view/index/login.html | 143 + application/admin/view/layout/default.html | 45 + application/admin/view/renzheng/add.html | 28 + application/admin/view/renzheng/edit.html | 35 + application/admin/view/renzheng/index.html | 45 + application/admin/view/renzheng/php/add.html | 28 + application/admin/view/renzheng/php/edit.html | 28 + .../admin/view/renzheng/php/index.html | 45 + .../shopro/activity/activity/activity.html | 87 + .../view/shopro/activity/activity/add.html | 674 + .../view/shopro/activity/activity/index.html | 106 + .../shopro/activity/activity/recyclebin.html | 20 + .../view/shopro/activity/activity/select.html | 33 + .../view/shopro/activity/activity/skus.html | 171 + .../view/shopro/activity/groupon/detail.html | 96 + .../view/shopro/activity/groupon/index.html | 105 + .../view/shopro/app/mplive/goods/add.html | 118 + .../view/shopro/app/mplive/index/index.html | 252 + .../view/shopro/app/mplive/room/add.html | 134 + .../view/shopro/app/mplive/room/playback.html | 68 + .../view/shopro/app/mplive/room/push_url.html | 61 + .../view/shopro/app/mplive/room/qrcode.html | 64 + .../view/shopro/app/mplive/room/select.html | 79 + .../admin/view/shopro/app/score_shop/add.html | 102 + .../view/shopro/app/score_shop/index.html | 158 + .../shopro/app/score_shop/recyclebin.html | 54 + .../view/shopro/app/score_shop/select.html | 68 + .../admin/view/shopro/category/add.html | 275 + .../admin/view/shopro/category/index.html | 75 + .../admin/view/shopro/category/select.html | 21 + .../view/shopro/chat/common_word/add.html | 42 + .../view/shopro/chat/common_word/index.html | 97 + .../shopro/chat/customer_service/add.html | 42 + .../shopro/chat/customer_service/index.html | 108 + .../admin/view/shopro/chat/question/add.html | 42 + .../view/shopro/chat/question/index.html | 97 + .../admin/view/shopro/chat/record/index.html | 197 + .../admin/view/shopro/chat/user/index.html | 109 + .../view/shopro/commission/agent/detail.html | 601 + .../view/shopro/commission/agent/index.html | 204 + .../view/shopro/commission/agent/select.html | 81 + .../view/shopro/commission/agent/team.html | 159 + .../view/shopro/commission/goods/edit.html | 353 + .../view/shopro/commission/goods/index.html | 116 + .../view/shopro/commission/level/add.html | 187 + .../view/shopro/commission/level/index.html | 60 + .../view/shopro/commission/level/select.html | 29 + .../view/shopro/commission/log/index.html | 56 + .../view/shopro/commission/order/index.html | 513 + .../view/shopro/commission/reward/index.html | 59 + .../admin/view/shopro/common/script.html | 22 + .../admin/view/shopro/config/index.html | 1135 + .../admin/view/shopro/config/platform.html | 181 + application/admin/view/shopro/coupon/add.html | 210 + .../admin/view/shopro/coupon/index.html | 206 + .../admin/view/shopro/coupon/recyclebin.html | 54 + .../admin/view/shopro/coupon/select.html | 79 + .../admin/view/shopro/dashboard/index.html | 382 + .../admin/view/shopro/data/area/add.html | 29 + .../admin/view/shopro/data/area/index.html | 108 + .../admin/view/shopro/data/area/select.html | 19 + .../admin/view/shopro/data/express/add.html | 24 + .../admin/view/shopro/data/express/index.html | 77 + .../admin/view/shopro/data/fake_user/add.html | 45 + .../view/shopro/data/fake_user/index.html | 96 + .../view/shopro/data/fake_user/random.html | 18 + .../view/shopro/data/fake_user/select.html | 31 + .../admin/view/shopro/data/faq/add.html | 28 + .../admin/view/shopro/data/faq/index.html | 97 + .../admin/view/shopro/data/page/add.html | 24 + .../admin/view/shopro/data/page/index.html | 86 + .../admin/view/shopro/data/page/select.html | 123 + .../admin/view/shopro/data/richtext/add.html | 23 + .../view/shopro/data/richtext/index.html | 75 + .../view/shopro/data/richtext/select.html | 43 + .../view/shopro/decorate/designer/index.html | 118 + .../view/shopro/decorate/page/index.html | 4290 +++ .../view/shopro/decorate/page/preview.html | 152 + .../view/shopro/decorate/template/add.html | 26 + .../view/shopro/decorate/template/index.html | 167 + .../shopro/decorate/template/recyclebin.html | 54 + .../view/shopro/decorate/template/select.html | 24 + .../view/shopro/dispatch/dispatch/add.html | 178 + .../view/shopro/dispatch/dispatch/index.html | 180 + .../admin/view/shopro/feedback/detail.html | 46 + .../admin/view/shopro/feedback/index.html | 123 + .../admin/view/shopro/goods/comment/add.html | 248 + .../view/shopro/goods/comment/index.html | 178 + .../view/shopro/goods/comment/recyclebin.html | 54 + .../view/shopro/goods/comment/reply.html | 18 + .../admin/view/shopro/goods/goods/add.html | 928 + .../view/shopro/goods/goods/add_stock.html | 153 + .../admin/view/shopro/goods/goods/index.html | 364 + .../view/shopro/goods/goods/recyclebin.html | 54 + .../admin/view/shopro/goods/goods/select.html | 168 + .../admin/view/shopro/goods/service/add.html | 24 + .../view/shopro/goods/service/index.html | 83 + .../view/shopro/goods/stock_log/index.html | 51 + .../shopro/goods/stock_warning/add_stock.html | 23 + .../shopro/goods/stock_warning/index.html | 60 + .../goods/stock_warning/recyclebin.html | 20 + .../view/shopro/notification/config/edit.html | 154 + .../shopro/notification/config/index.html | 286 + .../view/shopro/order/aftersale/add_log.html | 22 + .../view/shopro/order/aftersale/detail.html | 417 + .../view/shopro/order/aftersale/index.html | 199 + .../view/shopro/order/aftersale/refund.html | 28 + .../view/shopro/order/aftersale/refuse.html | 18 + .../view/shopro/order/invoice/confirm.html | 36 + .../view/shopro/order/invoice/index.html | 126 + .../admin/view/shopro/order/order/action.html | 17 + .../shopro/order/order/batch_dispatch.html | 91 + .../view/shopro/order/order/change_fee.html | 30 + .../admin/view/shopro/order/order/detail.html | 1011 + .../view/shopro/order/order/dispatch.html | 201 + .../shopro/order/order/dispatch_list.html | 98 + .../view/shopro/order/order/full_refund.html | 26 + .../admin/view/shopro/order/order/index.html | 951 + .../admin/view/shopro/order/order/refund.html | 28 + .../admin/view/shopro/pay_config/add.html | 180 + .../view/shopro/pay_config/recyclebin.html | 54 + application/admin/view/shopro/paylog/add.html | 46 + .../admin/view/shopro/paylog/edit.html | 46 + .../admin/view/shopro/paylog/index.html | 35 + .../admin/view/shopro/trade/order/detail.html | 56 + .../admin/view/shopro/trade/order/index.html | 97 + .../admin/view/shopro/user/coupon/index.html | 56 + .../admin/view/shopro/user/user/detail.html | 378 + .../admin/view/shopro/user/user/index.html | 56 + .../admin/view/shopro/user/user/recharge.html | 19 + application/admin/view/shopro/vip/add.html | 52 + application/admin/view/shopro/vip/edit.html | 52 + application/admin/view/shopro/vip/index.html | 45 + .../view/shopro/wechat/config/index.html | 80 + .../view/shopro/wechat/material/add.html | 57 + .../view/shopro/wechat/material/index.html | 304 + .../admin/view/shopro/wechat/menu/add.html | 293 + .../admin/view/shopro/wechat/menu/index.html | 122 + .../admin/view/shopro/wechat/reply/add.html | 91 + .../admin/view/shopro/wechat/reply/index.html | 106 + .../admin/view/shopro/withdraw/handle.html | 17 + .../admin/view/shopro/withdraw/index.html | 146 + .../admin/view/shopro/withdraw/log.html | 17 + application/admin/view/third/index.html | 23 + application/admin/view/user/club/add.html | 190 + application/admin/view/user/club/edit.html | 418 + application/admin/view/user/club/index.html | 35 + application/admin/view/user/club/show.html | 319 + application/admin/view/user/group/add.html | 41 + application/admin/view/user/group/edit.html | 41 + application/admin/view/user/group/index.html | 28 + application/admin/view/user/player/edit.html | 262 + application/admin/view/user/player/index.html | 28 + application/admin/view/user/rule/add.html | 53 + application/admin/view/user/rule/edit.html | 52 + application/admin/view/user/rule/index.html | 28 + application/admin/view/user/user/edit.html | 151 + application/admin/view/user/user/index.html | 28 + application/api/common.php | 1 + application/api/config.php | 6 + application/api/controller/Club.php | 151 + application/api/controller/Common.php | 134 + application/api/controller/Demo.php | 108 + application/api/controller/Ems.php | 96 + application/api/controller/Fastimadd.php | 125 + application/api/controller/Government.php | 250 + application/api/controller/Index.php | 40 + application/api/controller/Leaguepapply.php | 24 + application/api/controller/Match.php | 171 + application/api/controller/MatchServer.php | 131 + application/api/controller/News.php | 35 + application/api/controller/Players.php | 193 + application/api/controller/Ranking.php | 52 + application/api/controller/Share.php | 77 + application/api/controller/Sms.php | 123 + application/api/controller/Submail.php | 102 + application/api/controller/Tiktok.php | 270 + application/api/controller/Timerenctry.php | 0 application/api/controller/Token.php | 42 + application/api/controller/User.php | 568 + application/api/controller/Validate.php | 172 + application/api/lang/zh-cn.php | 102 + application/api/lang/zh-cn/common.php | 3 + application/api/lang/zh-cn/user.php | 39 + application/api/library/ExceptionHandle.php | 37 + application/build.php | 25 + application/command.php | 20 + application/common.php | 514 + application/common/behavior/Common.php | 87 + application/common/controller/Api.php | 330 + application/common/controller/Backend.php | 627 + application/common/controller/Frontend.php | 173 + .../common/exception/UploadException.php | 17 + application/common/lang/zh-cn/addon.php | 97 + application/common/library/Auth.php | 671 + application/common/library/Email.php | 236 + application/common/library/Ems.php | 159 + application/common/library/Log.php | 29 + application/common/library/Menu.php | 225 + application/common/library/Security.php | 874 + application/common/library/Sms.php | 135 + application/common/library/Token.php | 161 + application/common/library/Upload.php | 449 + application/common/library/token/Driver.php | 91 + .../common/library/token/driver/Mysql.php | 118 + .../common/library/token/driver/Redis.php | 167 + application/common/model/Area.php | 88 + application/common/model/Attachment.php | 98 + application/common/model/Category.php | 87 + application/common/model/Club.php | 29 + application/common/model/Config.php | 227 + application/common/model/Ems.php | 22 + application/common/model/MatchContestant.php | 30 + application/common/model/MatchRanking.php | 29 + application/common/model/MatchSchedule.php | 19 + application/common/model/MatchScreen.php | 19 + application/common/model/MoneyLog.php | 23 + .../common/model/PlayerCertificate.php | 13 + application/common/model/Players.php | 109 + application/common/model/ScoreLog.php | 23 + application/common/model/Sms.php | 22 + application/common/model/User.php | 151 + application/common/model/UserGroup.php | 21 + application/common/model/UserRule.php | 21 + application/common/model/Version.php | 50 + .../common/view/tpl/dispatch_jump - 副本.tpl | 63 + application/common/view/tpl/dispatch_jump.tpl | 63 + .../common/view/tpl/think_exception.tpl | 101 + application/config.php | 304 + application/database.php | 56 + application/extra/addons.php | 100 + application/extra/chat.php | 22 + application/extra/queue.php | 10 + application/extra/redis.php | 10 + application/extra/site.php | 44 + application/extra/upload.php | 45 + application/index/controller/Ajax.php | 65 + application/index/controller/Archives.php | 980 + application/index/controller/Broadcast.php | 122 + application/index/controller/Certificate | 0 application/index/controller/Certificate.php | 41 + application/index/controller/Club.php | 379 + application/index/controller/Clubinvate.php | 99 + application/index/controller/Combine.php | 30 + application/index/controller/Complain.php | 62 + application/index/controller/Index.php | 214 + application/index/controller/League.php | 405 + application/index/controller/Leagueqr.php | 194 + application/index/controller/Live.php | 83 + application/index/controller/Match.php | 203 + application/index/controller/Players.php | 682 + application/index/controller/Ranking.php | 465 + application/index/controller/Schedule.php | 54 + application/index/controller/Screen.php | 76 + application/index/controller/Third.php | 149 + application/index/controller/Tiktok.php | 42 + application/index/controller/User.php | 1094 + application/index/controller/Wrj.php | 87 + application/index/controller/Wxmb.php | 270 + application/index/controller/Zhinan.php | 94 + application/index/controller/cms/Archives.php | 325 + .../index/controller/cms/Collection.php | 84 + application/index/controller/cms/Comment.php | 33 + application/index/controller/cms/Order.php | 33 + application/index/controller/cms/Ranking.php | 16 + application/index/controllertzaaa.txt | 1 + application/index/controllertzooo.txt | 1 + application/index/controllertztem.txt | 1 + application/index/controllertztoken.txt | 1 + application/index/lang/en/index.php | 5 + application/index/lang/zh-cn.php | 142 + application/index/lang/zh-cn/ajax.php | 3 + application/index/lang/zh-cn/cms/archives.php | 15 + application/index/lang/zh-cn/cms/diyform.php | 7 + application/index/lang/zh-cn/index.php | 5 + application/index/lang/zh-cn/user.php | 89 + .../index/service/CertificateService.php | 164 + application/index/service/MatchService.php | 186 + application/index/service/PlayerService.php | 131 + application/index/service/RankingService.php | 1172 + application/index/service/ScheduleService.php | 85 + application/index/service/ScreenService.php | 210 + application/index/service/Service.php | 17 + application/index/view/club - 副本/index.html | 315 + .../index/view/club - 副本/myplayer.html | 332 + .../index/view/club - 副本/registerclub.html | 1007 + application/index/view/club/index - 副本.html | 233 + application/index/view/club/index.html | 410 + .../index/view/club/myplayer - 副本.html | 332 + application/index/view/club/myplayer.html | 425 + .../club/registerclub - 备份2024-03-15.html | 1500 + application/index/view/club/registerclub.html | 1760 + .../index/view/club/sociaty - 副本.html | 167 + application/index/view/club/sociaty.html | 329 + application/index/view/cms/archives/my.html | 116 + application/index/view/cms/archives/post.html | 207 + .../index/view/cms/collection/index.html | 79 + application/index/view/cms/comment/index.html | 77 + application/index/view/cms/common/fields.html | 156 + application/index/view/cms/order/index.html | 89 + application/index/view/common/captcha.html | 27 + application/index/view/common/meta.html | 26 + application/index/view/common/script.html | 1 + .../index/view/common/sidenav - ╕▒▒╛.html | 12 + .../index/view/common/sidenav - 副本.html | 12 + application/index/view/common/sidenav.html | 11 + application/index/view/index/index.html | 34 + .../index/view/layout - 副本/default.html | 297 + .../index/view/layout - 副本/default2.html | 63 + .../index/view/layout - 副本/defaults.html | 387 + .../view/layout - 副本/defaultsRegister.html | 303 + application/index/view/layout/default.html | 309 + application/index/view/layout/default2.html | 63 + .../layout/defaults - 备份_2024-03-15.html | 453 + application/index/view/layout/defaults.html | 436 + .../index/view/layout/defaultsRegister.html | 315 + application/index/view/layout/defaults_1.html | 410 + application/index/view/live/live.html | 702 + application/index/view/live/login.html | 127 + application/index/view/match/club_match.html | 356 + application/index/view/match/user_match.html | 315 + .../players - 副本/authenticationflayers.html | 192 + .../index/view/players - 副本/edit.html | 766 + .../index/view/players - 副本/index.html | 176 + .../index/view/players - 副本/joinclub.html | 132 + .../index/view/players - 副本/myclub.html | 356 + .../index/view/players - 副本/reapply.html | 762 + .../view/players - 副本/registeredflyers.html | 1185 + .../view/players/authenticationflayers.html | 192 + application/index/view/players/edit.html | 872 + application/index/view/players/index.html | 173 + application/index/view/players/joinclub.html | 191 + .../index/view/players/joinclub111.html | 132 + .../index/view/players/myclub - 副本.html | 386 + application/index/view/players/myclub.html | 376 + application/index/view/players/reapply.html | 762 + .../view/players/registeredflyers - 3-17 | 1215 + .../view/players/registeredflyers - 副本.html | 1191 + .../registeredflyers - 备份_2024-03-15.html | 1186 + .../index/view/players/registeredflyers.html | 856 + application/index/view/players/sociaty.html | 77 + application/index/view/third/prepare.html | 132 + .../index/view/user - 副本/attachment.html | 61 + .../user - 副本/authenticationflayers.html | 171 + .../index/view/user - 副本/changepwd.html | 47 + .../index/view/user - 副本/identitylist.html | 86 + application/index/view/user - 副本/index.html | 272 + application/index/view/user - 副本/login.html | 133 + .../index/view/user - 副本/profile.html | 202 + .../index/view/user - 副本/register.html | 84 + application/index/view/user/attachment.html | 61 + .../view/user/authenticationflayers.html | 171 + application/index/view/user/changepwd.html | 47 + application/index/view/user/identitylist.html | 86 + application/index/view/user/index.html | 454 + application/index/view/user/login.html | 221 + application/index/view/user/profile.html | 202 + application/index/view/user/register.html | 110 + application/index/view/wrj/index.html | 247 + application/index/view/wrj/login.html | 127 + application/route.php | 25 + application/service/MatchService.php | 232 + application/shopro/Category.php | 216 + application/shopro/Common.php | 31 + application/shopro/Config.php | 319 + application/shopro/Coupon.php | 292 + application/shopro/Dashboard.php | 241 + application/shopro/Feedback.php | 112 + application/shopro/PayConfig.php | 223 + application/shopro/Share.php | 30 + application/shopro/Withdraw.php | 126 + application/shopro/activity/Activity.php | 321 + application/shopro/activity/Groupon.php | 124 + application/shopro/app/ScoreShop.php | 313 + application/shopro/app/mplive/Goods.php | 163 + application/shopro/app/mplive/Index.php | 81 + application/shopro/app/mplive/Room.php | 201 + application/shopro/chat/CommonWord.php | 129 + application/shopro/chat/CustomerService.php | 205 + application/shopro/chat/Index.php | 38 + application/shopro/chat/Question.php | 133 + application/shopro/chat/Record.php | 37 + application/shopro/chat/User.php | 62 + application/shopro/commission/Agent.php | 250 + application/shopro/commission/Goods.php | 89 + application/shopro/commission/Level.php | 144 + application/shopro/commission/Log.php | 50 + application/shopro/commission/Order.php | 175 + application/shopro/commission/Reward.php | 31 + application/shopro/data/Area.php | 170 + application/shopro/data/Express.php | 156 + application/shopro/data/FakeUser.php | 215 + application/shopro/data/Faq.php | 138 + application/shopro/data/Page.php | 156 + application/shopro/data/Richtext.php | 170 + application/shopro/decorate/Designer.php | 47 + application/shopro/decorate/Page.php | 76 + application/shopro/decorate/Template.php | 286 + application/shopro/dispatch/Dispatch.php | 232 + application/shopro/goods/Comment.php | 247 + application/shopro/goods/Goods.php | 443 + application/shopro/goods/Service.php | 157 + application/shopro/goods/SkuPrice.php | 57 + application/shopro/goods/StockLog.php | 48 + application/shopro/goods/StockWarning.php | 120 + application/shopro/notification/Config.php | 211 + .../shopro/notification/Notification.php | 128 + .../notification/traits/Notification.php | 183 + application/shopro/order/Aftersale.php | 338 + application/shopro/order/Invoice.php | 62 + application/shopro/order/Order.php | 961 + application/shopro/trade/Order.php | 199 + application/shopro/traits/SkuPrice.php | 303 + application/shopro/user/Coupon.php | 33 + application/shopro/user/Group.php | 27 + application/shopro/user/User.php | 138 + application/shopro/user/WalletLog.php | 90 + application/shopro/wechat/Admin.php | 139 + application/shopro/wechat/Config.php | 30 + application/shopro/wechat/Material.php | 178 + application/shopro/wechat/Menu.php | 223 + application/shopro/wechat/Reply.php | 143 + application/tags.php | 40 + bower.json | 34 + cert/fpvone.key | 27 + cert/fpvone.pem | 86 + composer.json | 47 + extend/.htaccess | 1 + extend/fast/Auth.php | 265 + extend/fast/Date.php | 226 + extend/fast/Form.php | 1289 + extend/fast/Http.php | 185 + extend/fast/Pinyin.php | 36 + extend/fast/Random.php | 171 + extend/fast/Rsa.php | 179 + extend/fast/Tree.php | 438 + extend/fast/Version.php | 79 + sites.txt | 1 + think | 17 + 1040 files changed, 179312 insertions(+) create mode 100644 .bowerrc create mode 100644 .env.sample create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 addons/.gitkeep create mode 100644 application/.htaccess create mode 100644 application/admin/behavior/AdminLog.php create mode 100644 application/admin/command/Addon.php create mode 100644 application/admin/command/Addon/stubs/addon.stub create mode 100644 application/admin/command/Addon/stubs/config.stub create mode 100644 application/admin/command/Addon/stubs/controller.stub create mode 100644 application/admin/command/Addon/stubs/info.stub create mode 100644 application/admin/command/Api.php create mode 100644 application/admin/command/Api/lang/zh-cn.php create mode 100644 application/admin/command/Api/library/Builder.php create mode 100644 application/admin/command/Api/library/Extractor.php create mode 100644 application/admin/command/Api/template/index.html create mode 100644 application/admin/command/Crud.php create mode 100644 application/admin/command/Crud/stubs/add.stub create mode 100644 application/admin/command/Crud/stubs/controller.stub create mode 100644 application/admin/command/Crud/stubs/controllerindex.stub create mode 100644 application/admin/command/Crud/stubs/edit.stub create mode 100644 application/admin/command/Crud/stubs/html/checkbox.stub create mode 100644 application/admin/command/Crud/stubs/html/fieldlist-template.stub create mode 100644 application/admin/command/Crud/stubs/html/fieldlist.stub create mode 100644 application/admin/command/Crud/stubs/html/heading-html.stub create mode 100644 application/admin/command/Crud/stubs/html/radio.stub create mode 100644 application/admin/command/Crud/stubs/html/recyclebin-html.stub create mode 100644 application/admin/command/Crud/stubs/html/select.stub create mode 100644 application/admin/command/Crud/stubs/html/switch.stub create mode 100644 application/admin/command/Crud/stubs/index.stub create mode 100644 application/admin/command/Crud/stubs/javascript.stub create mode 100644 application/admin/command/Crud/stubs/lang.stub create mode 100644 application/admin/command/Crud/stubs/mixins/checkbox.stub create mode 100644 application/admin/command/Crud/stubs/mixins/datetime.stub create mode 100644 application/admin/command/Crud/stubs/mixins/enum.stub create mode 100644 application/admin/command/Crud/stubs/mixins/import.stub create mode 100644 application/admin/command/Crud/stubs/mixins/modelinit.stub create mode 100644 application/admin/command/Crud/stubs/mixins/modelrelationmethod-hasmany.stub create mode 100644 application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub create mode 100644 application/admin/command/Crud/stubs/mixins/multiple.stub create mode 100644 application/admin/command/Crud/stubs/mixins/radio.stub create mode 100644 application/admin/command/Crud/stubs/mixins/recyclebinjs.stub create mode 100644 application/admin/command/Crud/stubs/mixins/select.stub create mode 100644 application/admin/command/Crud/stubs/model.stub create mode 100644 application/admin/command/Crud/stubs/recyclebin.stub create mode 100644 application/admin/command/Crud/stubs/relationmodel.stub create mode 100644 application/admin/command/Crud/stubs/validate.stub create mode 100644 application/admin/command/Install.php create mode 100644 application/admin/command/Install/fastadmin.sql create mode 100644 application/admin/command/Install/install.html create mode 100644 application/admin/command/Install/install.lock create mode 100644 application/admin/command/Install/zh-cn.php create mode 100644 application/admin/command/Menu.php create mode 100644 application/admin/command/Min.php create mode 100644 application/admin/command/Min/r.js create mode 100644 application/admin/command/Min/stubs/css.stub create mode 100644 application/admin/command/Min/stubs/js.stub create mode 100644 application/admin/common.php create mode 100644 application/admin/config.php create mode 100644 application/admin/controller/Addon.php create mode 100644 application/admin/controller/Ajax.php create mode 100644 application/admin/controller/Category.php create mode 100644 application/admin/controller/Command.php create mode 100644 application/admin/controller/Contestants.php create mode 100644 application/admin/controller/Countryflag.php create mode 100644 application/admin/controller/Dashboard.php create mode 100644 application/admin/controller/Docs.php create mode 100644 application/admin/controller/Epay.php create mode 100644 application/admin/controller/Index.php create mode 100644 application/admin/controller/Match.php create mode 100644 application/admin/controller/Renzheng.php create mode 100644 application/admin/controller/Renzheng/Php.php create mode 100644 application/admin/controller/Schedule create mode 100644 application/admin/controller/Schedule.php create mode 100644 application/admin/controller/Screen.php create mode 100644 application/admin/controller/Third.php create mode 100644 application/admin/controller/auth/Admin.php create mode 100644 application/admin/controller/auth/Adminlog.php create mode 100644 application/admin/controller/auth/Group.php create mode 100644 application/admin/controller/auth/Rule.php create mode 100644 application/admin/controller/club/Complain.php create mode 100644 application/admin/controller/club/Invate.php create mode 100644 application/admin/controller/cms/Ajax.php create mode 100644 application/admin/controller/cms/Archives.php create mode 100644 application/admin/controller/cms/Autolink.php create mode 100644 application/admin/controller/cms/Block.php create mode 100644 application/admin/controller/cms/Builder.php create mode 100644 application/admin/controller/cms/Channel.php create mode 100644 application/admin/controller/cms/Comment.php create mode 100644 application/admin/controller/cms/Config.php create mode 100644 application/admin/controller/cms/Diydata.php create mode 100644 application/admin/controller/cms/Diyform.php create mode 100644 application/admin/controller/cms/Fields.php create mode 100644 application/admin/controller/cms/League.php create mode 100644 application/admin/controller/cms/Leaguemaster.php create mode 100644 application/admin/controller/cms/Leagueround.php create mode 100644 application/admin/controller/cms/Leaguesort.php create mode 100644 application/admin/controller/cms/Leaguexport.php create mode 100644 application/admin/controller/cms/Modelx.php create mode 100644 application/admin/controller/cms/Order.php create mode 100644 application/admin/controller/cms/Page.php create mode 100644 application/admin/controller/cms/Ranking.php create mode 100644 application/admin/controller/cms/SearchLog.php create mode 100644 application/admin/controller/cms/Special.php create mode 100644 application/admin/controller/cms/SpiderLog.php create mode 100644 application/admin/controller/cms/Statistics.php create mode 100644 application/admin/controller/cms/Tag.php create mode 100644 application/admin/controller/fastim/Config.php create mode 100644 application/admin/controller/fastim/CsrGroup.php create mode 100644 application/admin/controller/fastim/FastReply.php create mode 100644 application/admin/controller/fastim/Groupchat.php create mode 100644 application/admin/controller/fastim/Kbs.php create mode 100644 application/admin/controller/fastim/Records.php create mode 100644 application/admin/controller/fastim/Report.php create mode 100644 application/admin/controller/fastim/Session.php create mode 100644 application/admin/controller/fastim/User.php create mode 100644 application/admin/controller/general/Attachment.php create mode 100644 application/admin/controller/general/Config.php create mode 100644 application/admin/controller/general/Profile.php create mode 100644 application/admin/controller/shopro/Category.php create mode 100644 application/admin/controller/shopro/Common.php create mode 100644 application/admin/controller/shopro/Config.php create mode 100644 application/admin/controller/shopro/Coupon.php create mode 100644 application/admin/controller/shopro/Dashboard.php create mode 100644 application/admin/controller/shopro/Feedback.php create mode 100644 application/admin/controller/shopro/PayConfig.php create mode 100644 application/admin/controller/shopro/Paylog.php create mode 100644 application/admin/controller/shopro/Share.php create mode 100644 application/admin/controller/shopro/Vip.php create mode 100644 application/admin/controller/shopro/Withdraw.php create mode 100644 application/admin/controller/shopro/activity/Activity.php create mode 100644 application/admin/controller/shopro/activity/Groupon.php create mode 100644 application/admin/controller/shopro/app/ScoreShop.php create mode 100644 application/admin/controller/shopro/app/mplive/Goods.php create mode 100644 application/admin/controller/shopro/app/mplive/Index.php create mode 100644 application/admin/controller/shopro/app/mplive/Room.php create mode 100644 application/admin/controller/shopro/chat/CommonWord.php create mode 100644 application/admin/controller/shopro/chat/CustomerService.php create mode 100644 application/admin/controller/shopro/chat/Index.php create mode 100644 application/admin/controller/shopro/chat/Question.php create mode 100644 application/admin/controller/shopro/chat/Record.php create mode 100644 application/admin/controller/shopro/chat/User.php create mode 100644 application/admin/controller/shopro/commission/Agent.php create mode 100644 application/admin/controller/shopro/commission/Goods.php create mode 100644 application/admin/controller/shopro/commission/Level.php create mode 100644 application/admin/controller/shopro/commission/Log.php create mode 100644 application/admin/controller/shopro/commission/Order.php create mode 100644 application/admin/controller/shopro/commission/Reward.php create mode 100644 application/admin/controller/shopro/data/Area.php create mode 100644 application/admin/controller/shopro/data/Express.php create mode 100644 application/admin/controller/shopro/data/FakeUser.php create mode 100644 application/admin/controller/shopro/data/Faq.php create mode 100644 application/admin/controller/shopro/data/Page.php create mode 100644 application/admin/controller/shopro/data/Richtext.php create mode 100644 application/admin/controller/shopro/decorate/Designer.php create mode 100644 application/admin/controller/shopro/decorate/Page.php create mode 100644 application/admin/controller/shopro/decorate/Template.php create mode 100644 application/admin/controller/shopro/dispatch/Dispatch.php create mode 100644 application/admin/controller/shopro/goods/Comment.php create mode 100644 application/admin/controller/shopro/goods/Goods.php create mode 100644 application/admin/controller/shopro/goods/Service.php create mode 100644 application/admin/controller/shopro/goods/SkuPrice.php create mode 100644 application/admin/controller/shopro/goods/StockLog.php create mode 100644 application/admin/controller/shopro/goods/StockWarning.php create mode 100644 application/admin/controller/shopro/notification/Config.php create mode 100644 application/admin/controller/shopro/notification/Notification.php create mode 100644 application/admin/controller/shopro/notification/traits/Notification.php create mode 100644 application/admin/controller/shopro/order/Aftersale.php create mode 100644 application/admin/controller/shopro/order/Invoice.php create mode 100644 application/admin/controller/shopro/order/Order.php create mode 100644 application/admin/controller/shopro/trade/Order.php create mode 100644 application/admin/controller/shopro/traits/SkuPrice.php create mode 100644 application/admin/controller/shopro/user/Coupon.php create mode 100644 application/admin/controller/shopro/user/Group.php create mode 100644 application/admin/controller/shopro/user/User.php create mode 100644 application/admin/controller/shopro/user/WalletLog.php create mode 100644 application/admin/controller/shopro/wechat/Admin.php create mode 100644 application/admin/controller/shopro/wechat/Config.php create mode 100644 application/admin/controller/shopro/wechat/Material.php create mode 100644 application/admin/controller/shopro/wechat/Menu.php create mode 100644 application/admin/controller/shopro/wechat/Reply.php create mode 100644 application/admin/controller/user/Club.php create mode 100644 application/admin/controller/user/Group.php create mode 100644 application/admin/controller/user/Player.php create mode 100644 application/admin/controller/user/Rule.php create mode 100644 application/admin/controller/user/User.php create mode 100644 application/admin/lang/zh-cn.php create mode 100644 application/admin/lang/zh-cn/addon.php create mode 100644 application/admin/lang/zh-cn/ajax.php create mode 100644 application/admin/lang/zh-cn/auth/admin.php create mode 100644 application/admin/lang/zh-cn/auth/group.php create mode 100644 application/admin/lang/zh-cn/auth/rule.php create mode 100644 application/admin/lang/zh-cn/category.php create mode 100644 application/admin/lang/zh-cn/cms/ajax.php create mode 100644 application/admin/lang/zh-cn/cms/archives.php create mode 100644 application/admin/lang/zh-cn/cms/autolink.php create mode 100644 application/admin/lang/zh-cn/cms/block.php create mode 100644 application/admin/lang/zh-cn/cms/channel.php create mode 100644 application/admin/lang/zh-cn/cms/comment.php create mode 100644 application/admin/lang/zh-cn/cms/config.php create mode 100644 application/admin/lang/zh-cn/cms/diydata.php create mode 100644 application/admin/lang/zh-cn/cms/diyform.php create mode 100644 application/admin/lang/zh-cn/cms/fields.php create mode 100644 application/admin/lang/zh-cn/cms/modelx.php create mode 100644 application/admin/lang/zh-cn/cms/order.php create mode 100644 application/admin/lang/zh-cn/cms/page.php create mode 100644 application/admin/lang/zh-cn/cms/search_log.php create mode 100644 application/admin/lang/zh-cn/cms/special.php create mode 100644 application/admin/lang/zh-cn/cms/spider_log.php create mode 100644 application/admin/lang/zh-cn/cms/tag.php create mode 100644 application/admin/lang/zh-cn/command.php create mode 100644 application/admin/lang/zh-cn/config.php create mode 100644 application/admin/lang/zh-cn/countryflag.php create mode 100644 application/admin/lang/zh-cn/dashboard.php create mode 100644 application/admin/lang/zh-cn/docs.php create mode 100644 application/admin/lang/zh-cn/fastim/config.php create mode 100644 application/admin/lang/zh-cn/fastim/csr_group.php create mode 100644 application/admin/lang/zh-cn/fastim/fast_reply.php create mode 100644 application/admin/lang/zh-cn/fastim/groupchat.php create mode 100644 application/admin/lang/zh-cn/fastim/kbs.php create mode 100644 application/admin/lang/zh-cn/fastim/records.php create mode 100644 application/admin/lang/zh-cn/fastim/report.php create mode 100644 application/admin/lang/zh-cn/fastim/session.php create mode 100644 application/admin/lang/zh-cn/fastim/user.php create mode 100644 application/admin/lang/zh-cn/general/attachment.php create mode 100644 application/admin/lang/zh-cn/general/config.php create mode 100644 application/admin/lang/zh-cn/general/profile.php create mode 100644 application/admin/lang/zh-cn/index.php create mode 100644 application/admin/lang/zh-cn/renzheng.php create mode 100644 application/admin/lang/zh-cn/renzheng/php.php create mode 100644 application/admin/lang/zh-cn/shopro/vip.php create mode 100644 application/admin/lang/zh-cn/shopro/vip/paylog.php create mode 100644 application/admin/lang/zh-cn/third.php create mode 100644 application/admin/lang/zh-cn/user/club.php create mode 100644 application/admin/lang/zh-cn/user/group.php create mode 100644 application/admin/lang/zh-cn/user/player.php create mode 100644 application/admin/lang/zh-cn/user/rule.php create mode 100644 application/admin/lang/zh-cn/user/user.php create mode 100644 application/admin/library/Auth.php create mode 100644 application/admin/library/traits/Backend.php create mode 100644 application/admin/model/Addonnews.php create mode 100644 application/admin/model/Admin.php create mode 100644 application/admin/model/AdminLog.php create mode 100644 application/admin/model/AuthGroup.php create mode 100644 application/admin/model/AuthGroupAccess.php create mode 100644 application/admin/model/AuthRule.php create mode 100644 application/admin/model/Club.php create mode 100644 application/admin/model/ClubComplain.php create mode 100644 application/admin/model/ClubInvate.php create mode 100644 application/admin/model/ClubMatchApply.php create mode 100644 application/admin/model/ClubPlayercheck.php create mode 100644 application/admin/model/Combine.php create mode 100644 application/admin/model/CombineViews.php create mode 100644 application/admin/model/Command.php create mode 100644 application/admin/model/Countryflag.php create mode 100644 application/admin/model/LeagueClubIntegral.php create mode 100644 application/admin/model/LeagueIntegral.php create mode 100644 application/admin/model/LeagueMaster.php create mode 100644 application/admin/model/MatchCompetition.php create mode 100644 application/admin/model/MatchContestant.php create mode 100644 application/admin/model/MatchRanking create mode 100644 application/admin/model/MatchRanking.php create mode 100644 application/admin/model/MatchSchedule.php create mode 100644 application/admin/model/MatchScreen.php create mode 100644 application/admin/model/MessageLog.php create mode 100644 application/admin/model/PlayerCheck.php create mode 100644 application/admin/model/Players.php create mode 100644 application/admin/model/Renzheng.php create mode 100644 application/admin/model/Renzheng/Php.php create mode 100644 application/admin/model/Third.php create mode 100644 application/admin/model/User.php create mode 100644 application/admin/model/UserGroup.php create mode 100644 application/admin/model/UserRule.php create mode 100644 application/admin/model/Userwechat.php create mode 100644 application/admin/model/club/Complain.php create mode 100644 application/admin/model/club/Invate.php create mode 100644 application/admin/model/cms/Archives.php create mode 100644 application/admin/model/cms/Autolink.php create mode 100644 application/admin/model/cms/Block.php create mode 100644 application/admin/model/cms/Channel.php create mode 100644 application/admin/model/cms/ChannelAdmin.php create mode 100644 application/admin/model/cms/Comment.php create mode 100644 application/admin/model/cms/Diyform.php create mode 100644 application/admin/model/cms/Fields.php create mode 100644 application/admin/model/cms/Leaguearchives.php create mode 100644 application/admin/model/cms/Modelx.php create mode 100644 application/admin/model/cms/Order.php create mode 100644 application/admin/model/cms/Page.php create mode 100644 application/admin/model/cms/SearchLog.php create mode 100644 application/admin/model/cms/Special.php create mode 100644 application/admin/model/cms/SpiderLog.php create mode 100644 application/admin/model/cms/Tag.php create mode 100644 application/admin/model/cms/Taggable.php create mode 100644 application/admin/model/fastim/Config.php create mode 100644 application/admin/model/fastim/CsrGroup.php create mode 100644 application/admin/model/fastim/FastReply.php create mode 100644 application/admin/model/fastim/Groupchat.php create mode 100644 application/admin/model/fastim/Kbs.php create mode 100644 application/admin/model/fastim/Records.php create mode 100644 application/admin/model/fastim/Report.php create mode 100644 application/admin/model/fastim/Session.php create mode 100644 application/admin/model/fastim/User.php create mode 100644 application/admin/model/shopro/Admin.php create mode 100644 application/admin/model/shopro/Cart.php create mode 100644 application/admin/model/shopro/Category.php create mode 100644 application/admin/model/shopro/ClubThirdOauth.php create mode 100644 application/admin/model/shopro/Common.php create mode 100644 application/admin/model/shopro/Config.php create mode 100644 application/admin/model/shopro/Coupon.php create mode 100644 application/admin/model/shopro/Feedback.php create mode 100644 application/admin/model/shopro/Pay.php create mode 100644 application/admin/model/shopro/PayConfig.php create mode 100644 application/admin/model/shopro/Refund.php create mode 100644 application/admin/model/shopro/SearchHistory.php create mode 100644 application/admin/model/shopro/Share.php create mode 100644 application/admin/model/shopro/ThirdOauth.php create mode 100644 application/admin/model/shopro/Vip.php create mode 100644 application/admin/model/shopro/Withdraw.php create mode 100644 application/admin/model/shopro/WithdrawLog.php create mode 100644 application/admin/model/shopro/activity/Activity.php create mode 100644 application/admin/model/shopro/activity/GiftLog.php create mode 100644 application/admin/model/shopro/activity/Groupon.php create mode 100644 application/admin/model/shopro/activity/GrouponLog.php create mode 100644 application/admin/model/shopro/activity/Order.php create mode 100644 application/admin/model/shopro/activity/Signin.php create mode 100644 application/admin/model/shopro/activity/SkuPrice.php create mode 100644 application/admin/model/shopro/app/ScoreSkuPrice.php create mode 100644 application/admin/model/shopro/app/mplive/Goods.php create mode 100644 application/admin/model/shopro/app/mplive/Room.php create mode 100644 application/admin/model/shopro/chat/CommonWord.php create mode 100644 application/admin/model/shopro/chat/CustomerService.php create mode 100644 application/admin/model/shopro/chat/CustomerServiceUser.php create mode 100644 application/admin/model/shopro/chat/Question.php create mode 100644 application/admin/model/shopro/chat/Record.php create mode 100644 application/admin/model/shopro/chat/ServiceLog.php create mode 100644 application/admin/model/shopro/chat/User.php create mode 100644 application/admin/model/shopro/chat/traits/ChatCommon.php create mode 100644 application/admin/model/shopro/commission/Agent.php create mode 100644 application/admin/model/shopro/commission/CommissionGoods.php create mode 100644 application/admin/model/shopro/commission/Level.php create mode 100644 application/admin/model/shopro/commission/Log.php create mode 100644 application/admin/model/shopro/commission/Order.php create mode 100644 application/admin/model/shopro/commission/Reward.php create mode 100644 application/admin/model/shopro/data/Area.php create mode 100644 application/admin/model/shopro/data/Express.php create mode 100644 application/admin/model/shopro/data/FakeUser.php create mode 100644 application/admin/model/shopro/data/Faq.php create mode 100644 application/admin/model/shopro/data/Page.php create mode 100644 application/admin/model/shopro/data/Richtext.php create mode 100644 application/admin/model/shopro/data/WechatExpress.php create mode 100644 application/admin/model/shopro/decorate/Decorate.php create mode 100644 application/admin/model/shopro/decorate/Page.php create mode 100644 application/admin/model/shopro/dispatch/Dispatch.php create mode 100644 application/admin/model/shopro/dispatch/DispatchAutosend.php create mode 100644 application/admin/model/shopro/dispatch/DispatchExpress.php create mode 100644 application/admin/model/shopro/goods/Comment.php create mode 100644 application/admin/model/shopro/goods/Goods.php create mode 100644 application/admin/model/shopro/goods/Service.php create mode 100644 application/admin/model/shopro/goods/Sku.php create mode 100644 application/admin/model/shopro/goods/SkuPrice.php create mode 100644 application/admin/model/shopro/goods/StockLog.php create mode 100644 application/admin/model/shopro/goods/StockWarning.php create mode 100644 application/admin/model/shopro/notification/Config.php create mode 100644 application/admin/model/shopro/notification/Notification.php create mode 100644 application/admin/model/shopro/order/Action.php create mode 100644 application/admin/model/shopro/order/Address.php create mode 100644 application/admin/model/shopro/order/Aftersale.php create mode 100644 application/admin/model/shopro/order/AftersaleLog.php create mode 100644 application/admin/model/shopro/order/Express.php create mode 100644 application/admin/model/shopro/order/ExpressLog.php create mode 100644 application/admin/model/shopro/order/Invoice.php create mode 100644 application/admin/model/shopro/order/Order.php create mode 100644 application/admin/model/shopro/order/OrderItem.php create mode 100644 application/admin/model/shopro/order/traits/OrderItemStatus.php create mode 100644 application/admin/model/shopro/order/traits/OrderScope.php create mode 100644 application/admin/model/shopro/order/traits/OrderStatus.php create mode 100644 application/admin/model/shopro/trade/Order.php create mode 100644 application/admin/model/shopro/traits/ModelAttr.php create mode 100644 application/admin/model/shopro/user/Account.php create mode 100644 application/admin/model/shopro/user/Address.php create mode 100644 application/admin/model/shopro/user/CheckInfo.php create mode 100644 application/admin/model/shopro/user/Coupon.php create mode 100644 application/admin/model/shopro/user/GoodsLog.php create mode 100644 application/admin/model/shopro/user/Invoice.php create mode 100644 application/admin/model/shopro/user/User.php create mode 100644 application/admin/model/shopro/user/WalletLog.php create mode 100644 application/admin/model/shopro/vip/Paylog.php create mode 100644 application/admin/model/shopro/wechat/Material.php create mode 100644 application/admin/model/shopro/wechat/Menu.php create mode 100644 application/admin/model/shopro/wechat/Reply.php create mode 100644 application/admin/model/user/Club.php create mode 100644 application/admin/service/LeagueService.php create mode 100644 application/admin/service/RankingService.php create mode 100644 application/admin/service/ScheduleService.php create mode 100644 application/admin/service/ScreenService.php create mode 100644 application/admin/service/Service.php create mode 100644 application/admin/tags.php create mode 100644 application/admin/validate/Admin.php create mode 100644 application/admin/validate/AuthRule.php create mode 100644 application/admin/validate/Category.php create mode 100644 application/admin/validate/Command.php create mode 100644 application/admin/validate/Countryflag.php create mode 100644 application/admin/validate/Renzheng.php create mode 100644 application/admin/validate/Renzheng/Php.php create mode 100644 application/admin/validate/Third.php create mode 100644 application/admin/validate/User.php create mode 100644 application/admin/validate/UserGroup.php create mode 100644 application/admin/validate/UserRule.php create mode 100644 application/admin/validate/cms/Archives.php create mode 100644 application/admin/validate/cms/Autolink.php create mode 100644 application/admin/validate/cms/Block.php create mode 100644 application/admin/validate/cms/Channel.php create mode 100644 application/admin/validate/cms/Comment.php create mode 100644 application/admin/validate/cms/Fields.php create mode 100644 application/admin/validate/cms/Modelx.php create mode 100644 application/admin/validate/cms/Order.php create mode 100644 application/admin/validate/cms/Page.php create mode 100644 application/admin/validate/cms/SearchLog.php create mode 100644 application/admin/validate/cms/Special.php create mode 100644 application/admin/validate/cms/SpiderLog.php create mode 100644 application/admin/validate/cms/Tag.php create mode 100644 application/admin/validate/fastim/CsrGroup.php create mode 100644 application/admin/validate/fastim/FastReply.php create mode 100644 application/admin/validate/fastim/Groupchat.php create mode 100644 application/admin/validate/fastim/Kbs.php create mode 100644 application/admin/validate/fastim/Records.php create mode 100644 application/admin/validate/fastim/Report.php create mode 100644 application/admin/validate/fastim/Session.php create mode 100644 application/admin/validate/fastim/User.php create mode 100644 application/admin/validate/shopro/Category.php create mode 100644 application/admin/validate/shopro/Coupon.php create mode 100644 application/admin/validate/shopro/PayConfig.php create mode 100644 application/admin/validate/shopro/Vip.php create mode 100644 application/admin/validate/shopro/activity/Activity.php create mode 100644 application/admin/validate/shopro/app/ScoreShop.php create mode 100644 application/admin/validate/shopro/chat/CommonWord.php create mode 100644 application/admin/validate/shopro/chat/CustomerService.php create mode 100644 application/admin/validate/shopro/chat/Question.php create mode 100644 application/admin/validate/shopro/data/FakeUser.php create mode 100644 application/admin/validate/shopro/data/Faq.php create mode 100644 application/admin/validate/shopro/dispatch/Dispatch.php create mode 100644 application/admin/validate/shopro/goods/Comment.php create mode 100644 application/admin/validate/shopro/goods/Goods.php create mode 100644 application/admin/validate/shopro/goods/Service.php create mode 100644 application/admin/validate/shopro/goods/StockWarning.php create mode 100644 application/admin/validate/shopro/order/Aftersale.php create mode 100644 application/admin/validate/shopro/order/Order.php create mode 100644 application/admin/validate/shopro/traits/CustomRule.php create mode 100644 application/admin/validate/shopro/user/User.php create mode 100644 application/admin/validate/shopro/vip/Paylog.php create mode 100644 application/admin/validate/user/Club.php create mode 100644 application/admin/view/addon/add.html create mode 100644 application/admin/view/addon/config.html create mode 100644 application/admin/view/addon/index.html create mode 100644 application/admin/view/auth/admin/add.html create mode 100644 application/admin/view/auth/admin/edit.html create mode 100644 application/admin/view/auth/admin/index.html create mode 100644 application/admin/view/auth/adminlog/detail.html create mode 100644 application/admin/view/auth/adminlog/index.html create mode 100644 application/admin/view/auth/group/add.html create mode 100644 application/admin/view/auth/group/edit.html create mode 100644 application/admin/view/auth/group/index.html create mode 100644 application/admin/view/auth/rule/add.html create mode 100644 application/admin/view/auth/rule/edit.html create mode 100644 application/admin/view/auth/rule/index.html create mode 100644 application/admin/view/auth/rule/tpl.html create mode 100644 application/admin/view/category/add.html create mode 100644 application/admin/view/category/edit.html create mode 100644 application/admin/view/category/index.html create mode 100644 application/admin/view/club/complain/add.html create mode 100644 application/admin/view/club/complain/edit.html create mode 100644 application/admin/view/club/complain/index.html create mode 100644 application/admin/view/club/invate/add.html create mode 100644 application/admin/view/club/invate/edit.html create mode 100644 application/admin/view/club/invate/index.html create mode 100644 application/admin/view/club/invate/recyclebin.html create mode 100644 application/admin/view/cms/archives/add.html create mode 100644 application/admin/view/cms/archives/club_match_list.html create mode 100644 application/admin/view/cms/archives/common.html create mode 100644 application/admin/view/cms/archives/content.html create mode 100644 application/admin/view/cms/archives/contestant.html create mode 100644 application/admin/view/cms/archives/course.html create mode 100644 application/admin/view/cms/archives/edit.html create mode 100644 application/admin/view/cms/archives/index.html create mode 100644 application/admin/view/cms/archives/league.html create mode 100644 application/admin/view/cms/archives/league_allplayer.html create mode 100644 application/admin/view/cms/archives/league_course.html create mode 100644 application/admin/view/cms/archives/league_wholeplayer.html create mode 100644 application/admin/view/cms/archives/leaguecontestant.html create mode 100644 application/admin/view/cms/archives/recyclebin.html create mode 100644 application/admin/view/cms/archives/search.html create mode 100644 application/admin/view/cms/archives/test.html create mode 100644 application/admin/view/cms/archives/uploadfile.html create mode 100644 application/admin/view/cms/archives/visualization.html create mode 100644 application/admin/view/cms/autolink/add.html create mode 100644 application/admin/view/cms/autolink/edit.html create mode 100644 application/admin/view/cms/autolink/index.html create mode 100644 application/admin/view/cms/autolink/recyclebin.html create mode 100644 application/admin/view/cms/block/add.html create mode 100644 application/admin/view/cms/block/edit.html create mode 100644 application/admin/view/cms/block/index.html create mode 100644 application/admin/view/cms/builder/index.html create mode 100644 application/admin/view/cms/channel/add.html create mode 100644 application/admin/view/cms/channel/admin.html create mode 100644 application/admin/view/cms/channel/edit.html create mode 100644 application/admin/view/cms/channel/index.html create mode 100644 application/admin/view/cms/comment/add.html create mode 100644 application/admin/view/cms/comment/edit.html create mode 100644 application/admin/view/cms/comment/index.html create mode 100644 application/admin/view/cms/comment/recyclebin.html create mode 100644 application/admin/view/cms/common/fields.html create mode 100644 application/admin/view/cms/common/links.html create mode 100644 application/admin/view/cms/diydata/add.html create mode 100644 application/admin/view/cms/diydata/edit.html create mode 100644 application/admin/view/cms/diydata/fields.html create mode 100644 application/admin/view/cms/diydata/index.html create mode 100644 application/admin/view/cms/diyform/add.html create mode 100644 application/admin/view/cms/diyform/edit.html create mode 100644 application/admin/view/cms/diyform/index.html create mode 100644 application/admin/view/cms/diyform/tpl.html create mode 100644 application/admin/view/cms/fields/add.html create mode 100644 application/admin/view/cms/fields/archives.html create mode 100644 application/admin/view/cms/fields/edit.html create mode 100644 application/admin/view/cms/fields/index.html create mode 100644 application/admin/view/cms/modelx/add.html create mode 100644 application/admin/view/cms/modelx/edit.html create mode 100644 application/admin/view/cms/modelx/index.html create mode 100644 application/admin/view/cms/modelx/tpl.html create mode 100644 application/admin/view/cms/order/add.html create mode 100644 application/admin/view/cms/order/edit.html create mode 100644 application/admin/view/cms/order/index.html create mode 100644 application/admin/view/cms/page/add.html create mode 100644 application/admin/view/cms/page/edit.html create mode 100644 application/admin/view/cms/page/index.html create mode 100644 application/admin/view/cms/page/recyclebin.html create mode 100644 application/admin/view/cms/page/select.html create mode 100644 application/admin/view/cms/search_log/add.html create mode 100644 application/admin/view/cms/search_log/edit.html create mode 100644 application/admin/view/cms/search_log/index.html create mode 100644 application/admin/view/cms/special/add.html create mode 100644 application/admin/view/cms/special/edit.html create mode 100644 application/admin/view/cms/special/index.html create mode 100644 application/admin/view/cms/special/recyclebin.html create mode 100644 application/admin/view/cms/spider_log/add.html create mode 100644 application/admin/view/cms/spider_log/edit.html create mode 100644 application/admin/view/cms/spider_log/index.html create mode 100644 application/admin/view/cms/statistics/index.html create mode 100644 application/admin/view/cms/tag/add.html create mode 100644 application/admin/view/cms/tag/edit.html create mode 100644 application/admin/view/cms/tag/index.html create mode 100644 application/admin/view/command/add.html create mode 100644 application/admin/view/command/detail.html create mode 100644 application/admin/view/command/index.html create mode 100644 application/admin/view/common/control.html create mode 100644 application/admin/view/common/header.html create mode 100644 application/admin/view/common/menu.html create mode 100644 application/admin/view/common/meta.html create mode 100644 application/admin/view/common/script.html create mode 100644 application/admin/view/countryflag/add.html create mode 100644 application/admin/view/countryflag/edit.html create mode 100644 application/admin/view/countryflag/index.html create mode 100644 application/admin/view/dashboard/index.html create mode 100644 application/admin/view/docs/add.html create mode 100644 application/admin/view/docs/edit.html create mode 100644 application/admin/view/docs/index.html create mode 100644 application/admin/view/fastim/config/index.html create mode 100644 application/admin/view/fastim/csr_group/add.html create mode 100644 application/admin/view/fastim/csr_group/edit.html create mode 100644 application/admin/view/fastim/csr_group/index.html create mode 100644 application/admin/view/fastim/csr_group/recyclebin.html create mode 100644 application/admin/view/fastim/fast_reply/add.html create mode 100644 application/admin/view/fastim/fast_reply/edit.html create mode 100644 application/admin/view/fastim/fast_reply/index.html create mode 100644 application/admin/view/fastim/fast_reply/recyclebin.html create mode 100644 application/admin/view/fastim/groupchat/edit.html create mode 100644 application/admin/view/fastim/groupchat/index.html create mode 100644 application/admin/view/fastim/groupchat/record.html create mode 100644 application/admin/view/fastim/groupchat/recyclebin.html create mode 100644 application/admin/view/fastim/kbs/add.html create mode 100644 application/admin/view/fastim/kbs/edit.html create mode 100644 application/admin/view/fastim/kbs/index.html create mode 100644 application/admin/view/fastim/kbs/recyclebin.html create mode 100644 application/admin/view/fastim/records/edit.html create mode 100644 application/admin/view/fastim/records/index.html create mode 100644 application/admin/view/fastim/report/edit.html create mode 100644 application/admin/view/fastim/report/index.html create mode 100644 application/admin/view/fastim/session/add.html create mode 100644 application/admin/view/fastim/session/edit.html create mode 100644 application/admin/view/fastim/session/index.html create mode 100644 application/admin/view/fastim/session/record.html create mode 100644 application/admin/view/fastim/user/add.html create mode 100644 application/admin/view/fastim/user/edit.html create mode 100644 application/admin/view/fastim/user/index.html create mode 100644 application/admin/view/general/attachment/add.html create mode 100644 application/admin/view/general/attachment/edit.html create mode 100644 application/admin/view/general/attachment/index.html create mode 100644 application/admin/view/general/attachment/select.html create mode 100644 application/admin/view/general/config/index.html create mode 100644 application/admin/view/general/profile/index.html create mode 100644 application/admin/view/index/index.html create mode 100644 application/admin/view/index/login.html create mode 100644 application/admin/view/layout/default.html create mode 100644 application/admin/view/renzheng/add.html create mode 100644 application/admin/view/renzheng/edit.html create mode 100644 application/admin/view/renzheng/index.html create mode 100644 application/admin/view/renzheng/php/add.html create mode 100644 application/admin/view/renzheng/php/edit.html create mode 100644 application/admin/view/renzheng/php/index.html create mode 100644 application/admin/view/shopro/activity/activity/activity.html create mode 100644 application/admin/view/shopro/activity/activity/add.html create mode 100644 application/admin/view/shopro/activity/activity/index.html create mode 100644 application/admin/view/shopro/activity/activity/recyclebin.html create mode 100644 application/admin/view/shopro/activity/activity/select.html create mode 100644 application/admin/view/shopro/activity/activity/skus.html create mode 100644 application/admin/view/shopro/activity/groupon/detail.html create mode 100644 application/admin/view/shopro/activity/groupon/index.html create mode 100644 application/admin/view/shopro/app/mplive/goods/add.html create mode 100644 application/admin/view/shopro/app/mplive/index/index.html create mode 100644 application/admin/view/shopro/app/mplive/room/add.html create mode 100644 application/admin/view/shopro/app/mplive/room/playback.html create mode 100644 application/admin/view/shopro/app/mplive/room/push_url.html create mode 100644 application/admin/view/shopro/app/mplive/room/qrcode.html create mode 100644 application/admin/view/shopro/app/mplive/room/select.html create mode 100644 application/admin/view/shopro/app/score_shop/add.html create mode 100644 application/admin/view/shopro/app/score_shop/index.html create mode 100644 application/admin/view/shopro/app/score_shop/recyclebin.html create mode 100644 application/admin/view/shopro/app/score_shop/select.html create mode 100644 application/admin/view/shopro/category/add.html create mode 100644 application/admin/view/shopro/category/index.html create mode 100644 application/admin/view/shopro/category/select.html create mode 100644 application/admin/view/shopro/chat/common_word/add.html create mode 100644 application/admin/view/shopro/chat/common_word/index.html create mode 100644 application/admin/view/shopro/chat/customer_service/add.html create mode 100644 application/admin/view/shopro/chat/customer_service/index.html create mode 100644 application/admin/view/shopro/chat/question/add.html create mode 100644 application/admin/view/shopro/chat/question/index.html create mode 100644 application/admin/view/shopro/chat/record/index.html create mode 100644 application/admin/view/shopro/chat/user/index.html create mode 100644 application/admin/view/shopro/commission/agent/detail.html create mode 100644 application/admin/view/shopro/commission/agent/index.html create mode 100644 application/admin/view/shopro/commission/agent/select.html create mode 100644 application/admin/view/shopro/commission/agent/team.html create mode 100644 application/admin/view/shopro/commission/goods/edit.html create mode 100644 application/admin/view/shopro/commission/goods/index.html create mode 100644 application/admin/view/shopro/commission/level/add.html create mode 100644 application/admin/view/shopro/commission/level/index.html create mode 100644 application/admin/view/shopro/commission/level/select.html create mode 100644 application/admin/view/shopro/commission/log/index.html create mode 100644 application/admin/view/shopro/commission/order/index.html create mode 100644 application/admin/view/shopro/commission/reward/index.html create mode 100644 application/admin/view/shopro/common/script.html create mode 100644 application/admin/view/shopro/config/index.html create mode 100644 application/admin/view/shopro/config/platform.html create mode 100644 application/admin/view/shopro/coupon/add.html create mode 100644 application/admin/view/shopro/coupon/index.html create mode 100644 application/admin/view/shopro/coupon/recyclebin.html create mode 100644 application/admin/view/shopro/coupon/select.html create mode 100644 application/admin/view/shopro/dashboard/index.html create mode 100644 application/admin/view/shopro/data/area/add.html create mode 100644 application/admin/view/shopro/data/area/index.html create mode 100644 application/admin/view/shopro/data/area/select.html create mode 100644 application/admin/view/shopro/data/express/add.html create mode 100644 application/admin/view/shopro/data/express/index.html create mode 100644 application/admin/view/shopro/data/fake_user/add.html create mode 100644 application/admin/view/shopro/data/fake_user/index.html create mode 100644 application/admin/view/shopro/data/fake_user/random.html create mode 100644 application/admin/view/shopro/data/fake_user/select.html create mode 100644 application/admin/view/shopro/data/faq/add.html create mode 100644 application/admin/view/shopro/data/faq/index.html create mode 100644 application/admin/view/shopro/data/page/add.html create mode 100644 application/admin/view/shopro/data/page/index.html create mode 100644 application/admin/view/shopro/data/page/select.html create mode 100644 application/admin/view/shopro/data/richtext/add.html create mode 100644 application/admin/view/shopro/data/richtext/index.html create mode 100644 application/admin/view/shopro/data/richtext/select.html create mode 100644 application/admin/view/shopro/decorate/designer/index.html create mode 100644 application/admin/view/shopro/decorate/page/index.html create mode 100644 application/admin/view/shopro/decorate/page/preview.html create mode 100644 application/admin/view/shopro/decorate/template/add.html create mode 100644 application/admin/view/shopro/decorate/template/index.html create mode 100644 application/admin/view/shopro/decorate/template/recyclebin.html create mode 100644 application/admin/view/shopro/decorate/template/select.html create mode 100644 application/admin/view/shopro/dispatch/dispatch/add.html create mode 100644 application/admin/view/shopro/dispatch/dispatch/index.html create mode 100644 application/admin/view/shopro/feedback/detail.html create mode 100644 application/admin/view/shopro/feedback/index.html create mode 100644 application/admin/view/shopro/goods/comment/add.html create mode 100644 application/admin/view/shopro/goods/comment/index.html create mode 100644 application/admin/view/shopro/goods/comment/recyclebin.html create mode 100644 application/admin/view/shopro/goods/comment/reply.html create mode 100644 application/admin/view/shopro/goods/goods/add.html create mode 100644 application/admin/view/shopro/goods/goods/add_stock.html create mode 100644 application/admin/view/shopro/goods/goods/index.html create mode 100644 application/admin/view/shopro/goods/goods/recyclebin.html create mode 100644 application/admin/view/shopro/goods/goods/select.html create mode 100644 application/admin/view/shopro/goods/service/add.html create mode 100644 application/admin/view/shopro/goods/service/index.html create mode 100644 application/admin/view/shopro/goods/stock_log/index.html create mode 100644 application/admin/view/shopro/goods/stock_warning/add_stock.html create mode 100644 application/admin/view/shopro/goods/stock_warning/index.html create mode 100644 application/admin/view/shopro/goods/stock_warning/recyclebin.html create mode 100644 application/admin/view/shopro/notification/config/edit.html create mode 100644 application/admin/view/shopro/notification/config/index.html create mode 100644 application/admin/view/shopro/order/aftersale/add_log.html create mode 100644 application/admin/view/shopro/order/aftersale/detail.html create mode 100644 application/admin/view/shopro/order/aftersale/index.html create mode 100644 application/admin/view/shopro/order/aftersale/refund.html create mode 100644 application/admin/view/shopro/order/aftersale/refuse.html create mode 100644 application/admin/view/shopro/order/invoice/confirm.html create mode 100644 application/admin/view/shopro/order/invoice/index.html create mode 100644 application/admin/view/shopro/order/order/action.html create mode 100644 application/admin/view/shopro/order/order/batch_dispatch.html create mode 100644 application/admin/view/shopro/order/order/change_fee.html create mode 100644 application/admin/view/shopro/order/order/detail.html create mode 100644 application/admin/view/shopro/order/order/dispatch.html create mode 100644 application/admin/view/shopro/order/order/dispatch_list.html create mode 100644 application/admin/view/shopro/order/order/full_refund.html create mode 100644 application/admin/view/shopro/order/order/index.html create mode 100644 application/admin/view/shopro/order/order/refund.html create mode 100644 application/admin/view/shopro/pay_config/add.html create mode 100644 application/admin/view/shopro/pay_config/recyclebin.html create mode 100644 application/admin/view/shopro/paylog/add.html create mode 100644 application/admin/view/shopro/paylog/edit.html create mode 100644 application/admin/view/shopro/paylog/index.html create mode 100644 application/admin/view/shopro/trade/order/detail.html create mode 100644 application/admin/view/shopro/trade/order/index.html create mode 100644 application/admin/view/shopro/user/coupon/index.html create mode 100644 application/admin/view/shopro/user/user/detail.html create mode 100644 application/admin/view/shopro/user/user/index.html create mode 100644 application/admin/view/shopro/user/user/recharge.html create mode 100644 application/admin/view/shopro/vip/add.html create mode 100644 application/admin/view/shopro/vip/edit.html create mode 100644 application/admin/view/shopro/vip/index.html create mode 100644 application/admin/view/shopro/wechat/config/index.html create mode 100644 application/admin/view/shopro/wechat/material/add.html create mode 100644 application/admin/view/shopro/wechat/material/index.html create mode 100644 application/admin/view/shopro/wechat/menu/add.html create mode 100644 application/admin/view/shopro/wechat/menu/index.html create mode 100644 application/admin/view/shopro/wechat/reply/add.html create mode 100644 application/admin/view/shopro/wechat/reply/index.html create mode 100644 application/admin/view/shopro/withdraw/handle.html create mode 100644 application/admin/view/shopro/withdraw/index.html create mode 100644 application/admin/view/shopro/withdraw/log.html create mode 100644 application/admin/view/third/index.html create mode 100644 application/admin/view/user/club/add.html create mode 100644 application/admin/view/user/club/edit.html create mode 100644 application/admin/view/user/club/index.html create mode 100644 application/admin/view/user/club/show.html create mode 100644 application/admin/view/user/group/add.html create mode 100644 application/admin/view/user/group/edit.html create mode 100644 application/admin/view/user/group/index.html create mode 100644 application/admin/view/user/player/edit.html create mode 100644 application/admin/view/user/player/index.html create mode 100644 application/admin/view/user/rule/add.html create mode 100644 application/admin/view/user/rule/edit.html create mode 100644 application/admin/view/user/rule/index.html create mode 100644 application/admin/view/user/user/edit.html create mode 100644 application/admin/view/user/user/index.html create mode 100644 application/api/common.php create mode 100644 application/api/config.php create mode 100644 application/api/controller/Club.php create mode 100644 application/api/controller/Common.php create mode 100644 application/api/controller/Demo.php create mode 100644 application/api/controller/Ems.php create mode 100644 application/api/controller/Fastimadd.php create mode 100644 application/api/controller/Government.php create mode 100644 application/api/controller/Index.php create mode 100644 application/api/controller/Leaguepapply.php create mode 100644 application/api/controller/Match.php create mode 100644 application/api/controller/MatchServer.php create mode 100644 application/api/controller/News.php create mode 100644 application/api/controller/Players.php create mode 100644 application/api/controller/Ranking.php create mode 100644 application/api/controller/Share.php create mode 100644 application/api/controller/Sms.php create mode 100644 application/api/controller/Submail.php create mode 100644 application/api/controller/Tiktok.php create mode 100644 application/api/controller/Timerenctry.php create mode 100644 application/api/controller/Token.php create mode 100644 application/api/controller/User.php create mode 100644 application/api/controller/Validate.php create mode 100644 application/api/lang/zh-cn.php create mode 100644 application/api/lang/zh-cn/common.php create mode 100644 application/api/lang/zh-cn/user.php create mode 100644 application/api/library/ExceptionHandle.php create mode 100644 application/build.php create mode 100644 application/command.php create mode 100644 application/common.php create mode 100644 application/common/behavior/Common.php create mode 100644 application/common/controller/Api.php create mode 100644 application/common/controller/Backend.php create mode 100644 application/common/controller/Frontend.php create mode 100644 application/common/exception/UploadException.php create mode 100644 application/common/lang/zh-cn/addon.php create mode 100644 application/common/library/Auth.php create mode 100644 application/common/library/Email.php create mode 100644 application/common/library/Ems.php create mode 100644 application/common/library/Log.php create mode 100644 application/common/library/Menu.php create mode 100644 application/common/library/Security.php create mode 100644 application/common/library/Sms.php create mode 100644 application/common/library/Token.php create mode 100644 application/common/library/Upload.php create mode 100644 application/common/library/token/Driver.php create mode 100644 application/common/library/token/driver/Mysql.php create mode 100644 application/common/library/token/driver/Redis.php create mode 100644 application/common/model/Area.php create mode 100644 application/common/model/Attachment.php create mode 100644 application/common/model/Category.php create mode 100644 application/common/model/Club.php create mode 100644 application/common/model/Config.php create mode 100644 application/common/model/Ems.php create mode 100644 application/common/model/MatchContestant.php create mode 100644 application/common/model/MatchRanking.php create mode 100644 application/common/model/MatchSchedule.php create mode 100644 application/common/model/MatchScreen.php create mode 100644 application/common/model/MoneyLog.php create mode 100644 application/common/model/PlayerCertificate.php create mode 100644 application/common/model/Players.php create mode 100644 application/common/model/ScoreLog.php create mode 100644 application/common/model/Sms.php create mode 100644 application/common/model/User.php create mode 100644 application/common/model/UserGroup.php create mode 100644 application/common/model/UserRule.php create mode 100644 application/common/model/Version.php create mode 100644 application/common/view/tpl/dispatch_jump - 副本.tpl create mode 100644 application/common/view/tpl/dispatch_jump.tpl create mode 100644 application/common/view/tpl/think_exception.tpl create mode 100644 application/config.php create mode 100644 application/database.php create mode 100644 application/extra/addons.php create mode 100644 application/extra/chat.php create mode 100644 application/extra/queue.php create mode 100644 application/extra/redis.php create mode 100644 application/extra/site.php create mode 100644 application/extra/upload.php create mode 100644 application/index/controller/Ajax.php create mode 100644 application/index/controller/Archives.php create mode 100644 application/index/controller/Broadcast.php create mode 100644 application/index/controller/Certificate create mode 100644 application/index/controller/Certificate.php create mode 100644 application/index/controller/Club.php create mode 100644 application/index/controller/Clubinvate.php create mode 100644 application/index/controller/Combine.php create mode 100644 application/index/controller/Complain.php create mode 100644 application/index/controller/Index.php create mode 100644 application/index/controller/League.php create mode 100644 application/index/controller/Leagueqr.php create mode 100644 application/index/controller/Live.php create mode 100644 application/index/controller/Match.php create mode 100644 application/index/controller/Players.php create mode 100644 application/index/controller/Ranking.php create mode 100644 application/index/controller/Schedule.php create mode 100644 application/index/controller/Screen.php create mode 100644 application/index/controller/Third.php create mode 100644 application/index/controller/Tiktok.php create mode 100644 application/index/controller/User.php create mode 100644 application/index/controller/Wrj.php create mode 100644 application/index/controller/Wxmb.php create mode 100644 application/index/controller/Zhinan.php create mode 100644 application/index/controller/cms/Archives.php create mode 100644 application/index/controller/cms/Collection.php create mode 100644 application/index/controller/cms/Comment.php create mode 100644 application/index/controller/cms/Order.php create mode 100644 application/index/controller/cms/Ranking.php create mode 100644 application/index/controllertzaaa.txt create mode 100644 application/index/controllertzooo.txt create mode 100644 application/index/controllertztem.txt create mode 100644 application/index/controllertztoken.txt create mode 100644 application/index/lang/en/index.php create mode 100644 application/index/lang/zh-cn.php create mode 100644 application/index/lang/zh-cn/ajax.php create mode 100644 application/index/lang/zh-cn/cms/archives.php create mode 100644 application/index/lang/zh-cn/cms/diyform.php create mode 100644 application/index/lang/zh-cn/index.php create mode 100644 application/index/lang/zh-cn/user.php create mode 100644 application/index/service/CertificateService.php create mode 100644 application/index/service/MatchService.php create mode 100644 application/index/service/PlayerService.php create mode 100644 application/index/service/RankingService.php create mode 100644 application/index/service/ScheduleService.php create mode 100644 application/index/service/ScreenService.php create mode 100644 application/index/service/Service.php create mode 100644 application/index/view/club - 副本/index.html create mode 100644 application/index/view/club - 副本/myplayer.html create mode 100644 application/index/view/club - 副本/registerclub.html create mode 100644 application/index/view/club/index - 副本.html create mode 100644 application/index/view/club/index.html create mode 100644 application/index/view/club/myplayer - 副本.html create mode 100644 application/index/view/club/myplayer.html create mode 100644 application/index/view/club/registerclub - 备份2024-03-15.html create mode 100644 application/index/view/club/registerclub.html create mode 100644 application/index/view/club/sociaty - 副本.html create mode 100644 application/index/view/club/sociaty.html create mode 100644 application/index/view/cms/archives/my.html create mode 100644 application/index/view/cms/archives/post.html create mode 100644 application/index/view/cms/collection/index.html create mode 100644 application/index/view/cms/comment/index.html create mode 100644 application/index/view/cms/common/fields.html create mode 100644 application/index/view/cms/order/index.html create mode 100644 application/index/view/common/captcha.html create mode 100644 application/index/view/common/meta.html create mode 100644 application/index/view/common/script.html create mode 100644 application/index/view/common/sidenav - ╕▒▒╛.html create mode 100644 application/index/view/common/sidenav - 副本.html create mode 100644 application/index/view/common/sidenav.html create mode 100644 application/index/view/index/index.html create mode 100644 application/index/view/layout - 副本/default.html create mode 100644 application/index/view/layout - 副本/default2.html create mode 100644 application/index/view/layout - 副本/defaults.html create mode 100644 application/index/view/layout - 副本/defaultsRegister.html create mode 100644 application/index/view/layout/default.html create mode 100644 application/index/view/layout/default2.html create mode 100644 application/index/view/layout/defaults - 备份_2024-03-15.html create mode 100644 application/index/view/layout/defaults.html create mode 100644 application/index/view/layout/defaultsRegister.html create mode 100644 application/index/view/layout/defaults_1.html create mode 100644 application/index/view/live/live.html create mode 100644 application/index/view/live/login.html create mode 100644 application/index/view/match/club_match.html create mode 100644 application/index/view/match/user_match.html create mode 100644 application/index/view/players - 副本/authenticationflayers.html create mode 100644 application/index/view/players - 副本/edit.html create mode 100644 application/index/view/players - 副本/index.html create mode 100644 application/index/view/players - 副本/joinclub.html create mode 100644 application/index/view/players - 副本/myclub.html create mode 100644 application/index/view/players - 副本/reapply.html create mode 100644 application/index/view/players - 副本/registeredflyers.html create mode 100644 application/index/view/players/authenticationflayers.html create mode 100644 application/index/view/players/edit.html create mode 100644 application/index/view/players/index.html create mode 100644 application/index/view/players/joinclub.html create mode 100644 application/index/view/players/joinclub111.html create mode 100644 application/index/view/players/myclub - 副本.html create mode 100644 application/index/view/players/myclub.html create mode 100644 application/index/view/players/reapply.html create mode 100644 application/index/view/players/registeredflyers - 3-17 create mode 100644 application/index/view/players/registeredflyers - 副本.html create mode 100644 application/index/view/players/registeredflyers - 备份_2024-03-15.html create mode 100644 application/index/view/players/registeredflyers.html create mode 100644 application/index/view/players/sociaty.html create mode 100644 application/index/view/third/prepare.html create mode 100644 application/index/view/user - 副本/attachment.html create mode 100644 application/index/view/user - 副本/authenticationflayers.html create mode 100644 application/index/view/user - 副本/changepwd.html create mode 100644 application/index/view/user - 副本/identitylist.html create mode 100644 application/index/view/user - 副本/index.html create mode 100644 application/index/view/user - 副本/login.html create mode 100644 application/index/view/user - 副本/profile.html create mode 100644 application/index/view/user - 副本/register.html create mode 100644 application/index/view/user/attachment.html create mode 100644 application/index/view/user/authenticationflayers.html create mode 100644 application/index/view/user/changepwd.html create mode 100644 application/index/view/user/identitylist.html create mode 100644 application/index/view/user/index.html create mode 100644 application/index/view/user/login.html create mode 100644 application/index/view/user/profile.html create mode 100644 application/index/view/user/register.html create mode 100644 application/index/view/wrj/index.html create mode 100644 application/index/view/wrj/login.html create mode 100644 application/route.php create mode 100644 application/service/MatchService.php create mode 100644 application/shopro/Category.php create mode 100644 application/shopro/Common.php create mode 100644 application/shopro/Config.php create mode 100644 application/shopro/Coupon.php create mode 100644 application/shopro/Dashboard.php create mode 100644 application/shopro/Feedback.php create mode 100644 application/shopro/PayConfig.php create mode 100644 application/shopro/Share.php create mode 100644 application/shopro/Withdraw.php create mode 100644 application/shopro/activity/Activity.php create mode 100644 application/shopro/activity/Groupon.php create mode 100644 application/shopro/app/ScoreShop.php create mode 100644 application/shopro/app/mplive/Goods.php create mode 100644 application/shopro/app/mplive/Index.php create mode 100644 application/shopro/app/mplive/Room.php create mode 100644 application/shopro/chat/CommonWord.php create mode 100644 application/shopro/chat/CustomerService.php create mode 100644 application/shopro/chat/Index.php create mode 100644 application/shopro/chat/Question.php create mode 100644 application/shopro/chat/Record.php create mode 100644 application/shopro/chat/User.php create mode 100644 application/shopro/commission/Agent.php create mode 100644 application/shopro/commission/Goods.php create mode 100644 application/shopro/commission/Level.php create mode 100644 application/shopro/commission/Log.php create mode 100644 application/shopro/commission/Order.php create mode 100644 application/shopro/commission/Reward.php create mode 100644 application/shopro/data/Area.php create mode 100644 application/shopro/data/Express.php create mode 100644 application/shopro/data/FakeUser.php create mode 100644 application/shopro/data/Faq.php create mode 100644 application/shopro/data/Page.php create mode 100644 application/shopro/data/Richtext.php create mode 100644 application/shopro/decorate/Designer.php create mode 100644 application/shopro/decorate/Page.php create mode 100644 application/shopro/decorate/Template.php create mode 100644 application/shopro/dispatch/Dispatch.php create mode 100644 application/shopro/goods/Comment.php create mode 100644 application/shopro/goods/Goods.php create mode 100644 application/shopro/goods/Service.php create mode 100644 application/shopro/goods/SkuPrice.php create mode 100644 application/shopro/goods/StockLog.php create mode 100644 application/shopro/goods/StockWarning.php create mode 100644 application/shopro/notification/Config.php create mode 100644 application/shopro/notification/Notification.php create mode 100644 application/shopro/notification/traits/Notification.php create mode 100644 application/shopro/order/Aftersale.php create mode 100644 application/shopro/order/Invoice.php create mode 100644 application/shopro/order/Order.php create mode 100644 application/shopro/trade/Order.php create mode 100644 application/shopro/traits/SkuPrice.php create mode 100644 application/shopro/user/Coupon.php create mode 100644 application/shopro/user/Group.php create mode 100644 application/shopro/user/User.php create mode 100644 application/shopro/user/WalletLog.php create mode 100644 application/shopro/wechat/Admin.php create mode 100644 application/shopro/wechat/Config.php create mode 100644 application/shopro/wechat/Material.php create mode 100644 application/shopro/wechat/Menu.php create mode 100644 application/shopro/wechat/Reply.php create mode 100644 application/tags.php create mode 100644 bower.json create mode 100644 cert/fpvone.key create mode 100644 cert/fpvone.pem create mode 100644 composer.json create mode 100644 extend/.htaccess create mode 100644 extend/fast/Auth.php create mode 100644 extend/fast/Date.php create mode 100644 extend/fast/Form.php create mode 100644 extend/fast/Http.php create mode 100644 extend/fast/Pinyin.php create mode 100644 extend/fast/Random.php create mode 100644 extend/fast/Rsa.php create mode 100644 extend/fast/Tree.php create mode 100644 extend/fast/Version.php create mode 100644 sites.txt create mode 100644 think diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..3f53a73 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,11 @@ +{ + "directory": "public/assets/libs", + "ignoredDependencies": [ + "es6-promise", + "file-saver", + "html2canvas", + "jspdf", + "jspdf-autotable", + "pdfmake" + ] +} diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..ccd0f29 --- /dev/null +++ b/.env.sample @@ -0,0 +1,11 @@ +[app] +debug = false +trace = false + +[database] +hostname = 127.0.0.1 +database = fastadmin +username = root +password = root +hostport = 3306 +prefix = fa_ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b209131 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +/nbproject/ +/thinkphp/ +/vendor/ +/runtime/* +/addons/* +/public/assets/libs/ +/public/assets/addons/* +/public/uploads/* +.idea +composer.lock +*.log +*.css.map +!.gitkeep +.env +.svn +.vscode +node_modules +.user.ini diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..088bc3f --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "{}" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2017 Karson + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/addons/.gitkeep b/addons/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/addons/.gitkeep @@ -0,0 +1 @@ + diff --git a/application/.htaccess b/application/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/application/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/application/admin/behavior/AdminLog.php b/application/admin/behavior/AdminLog.php new file mode 100644 index 0000000..f5bcd3c --- /dev/null +++ b/application/admin/behavior/AdminLog.php @@ -0,0 +1,14 @@ +isPost() && config('fastadmin.auto_record_log')) { + \app\admin\model\AdminLog::record(); + } + } +} diff --git a/application/admin/command/Addon.php b/application/admin/command/Addon.php new file mode 100644 index 0000000..fb7c437 --- /dev/null +++ b/application/admin/command/Addon.php @@ -0,0 +1,344 @@ +setName('addon') + ->addOption('name', 'a', Option::VALUE_REQUIRED, 'addon name', null) + ->addOption('action', 'c', Option::VALUE_REQUIRED, 'action(create/enable/disable/uninstall/refresh/package/move)', 'create') + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', null) + ->addOption('release', 'r', Option::VALUE_OPTIONAL, 'addon release version', null) + ->addOption('uid', 'u', Option::VALUE_OPTIONAL, 'fastadmin uid', null) + ->addOption('token', 't', Option::VALUE_OPTIONAL, 'fastadmin token', null) + ->addOption('domain', 'd', Option::VALUE_OPTIONAL, 'domain', null) + ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local package', null) + ->setDescription('Addon manager'); + } + + protected function execute(Input $input, Output $output) + { + $name = $input->getOption('name') ?: ''; + $action = $input->getOption('action') ?: ''; + if (stripos($name, 'addons' . DS) !== false) { + $name = explode(DS, $name)[1]; + } + //强制覆盖 + $force = $input->getOption('force'); + //版本 + $release = $input->getOption('release') ?: ''; + //uid + $uid = $input->getOption('uid') ?: ''; + //token + $token = $input->getOption('token') ?: ''; + + include dirname(__DIR__) . DS . 'common.php'; + + if (!$name && !in_array($action, ['refresh'])) { + throw new Exception('Addon name could not be empty'); + } + if (!$action || !in_array($action, ['create', 'disable', 'enable', 'install', 'uninstall', 'refresh', 'upgrade', 'package', 'move'])) { + throw new Exception('Please input correct action name'); + } + + // 查询一次SQL,判断连接是否正常 + Db::execute("SELECT 1"); + + $addonDir = ADDON_PATH . $name . DS; + switch ($action) { + case 'create': + //非覆盖模式时如果存在则报错 + if (is_dir($addonDir) && !$force) { + throw new Exception("addon already exists!\nIf you need to create again, use the parameter --force=true "); + } + //如果存在先移除 + if (is_dir($addonDir)) { + rmdirs($addonDir); + } + mkdir($addonDir, 0755, true); + mkdir($addonDir . DS . 'controller', 0755, true); + $menuList = \app\common\library\Menu::export($name); + $createMenu = $this->getCreateMenu($menuList); + $prefix = Config::get('database.prefix'); + $createTableSql = ''; + try { + $result = Db::query("SHOW CREATE TABLE `" . $prefix . $name . "`;"); + if (isset($result[0]) && isset($result[0]['Create Table'])) { + $createTableSql = $result[0]['Create Table']; + } + } catch (PDOException $e) { + + } + + $data = [ + 'name' => $name, + 'addon' => $name, + 'addonClassName' => ucfirst($name), + 'addonInstallMenu' => $createMenu ? "\$menu = " . var_export_short($createMenu) . ";\n\tMenu::create(\$menu);" : '', + 'addonUninstallMenu' => $menuList ? 'Menu::delete("' . $name . '");' : '', + 'addonEnableMenu' => $menuList ? 'Menu::enable("' . $name . '");' : '', + 'addonDisableMenu' => $menuList ? 'Menu::disable("' . $name . '");' : '', + ]; + $this->writeToFile("addon", $data, $addonDir . ucfirst($name) . '.php'); + $this->writeToFile("config", $data, $addonDir . 'config.php'); + $this->writeToFile("info", $data, $addonDir . 'info.ini'); + $this->writeToFile("controller", $data, $addonDir . 'controller' . DS . 'Index.php'); + if ($createTableSql) { + $createTableSql = str_replace("`" . $prefix, '`__PREFIX__', $createTableSql); + file_put_contents($addonDir . 'install.sql', $createTableSql); + } + + $output->info("Create Successed!"); + break; + case 'disable': + case 'enable': + try { + //调用启用、禁用的方法 + Service::$action($name, 0); + } catch (AddonException $e) { + if ($e->getCode() != -3) { + throw new Exception($e->getMessage()); + } + if (!$force) { + //如果有冲突文件则提醒 + $data = $e->getData(); + foreach ($data['conflictlist'] as $k => $v) { + $output->warning($v); + } + $output->info("Are you sure you want to " . ($action == 'enable' ? 'override' : 'delete') . " all those files? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } + } + //调用启用、禁用的方法 + Service::$action($name, 1); + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + $output->info(ucfirst($action) . " Successed!"); + break; + case 'uninstall': + //非覆盖模式时如果存在则报错 + if (!$force) { + throw new Exception("If you need to uninstall addon, use the parameter --force=true "); + } + try { + Service::uninstall($name, 0); + } catch (AddonException $e) { + if ($e->getCode() != -3) { + throw new Exception($e->getMessage()); + } + if (!$force) { + //如果有冲突文件则提醒 + $data = $e->getData(); + foreach ($data['conflictlist'] as $k => $v) { + $output->warning($v); + } + $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } + } + Service::uninstall($name, 1); + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + + $output->info("Uninstall Successed!"); + break; + case 'refresh': + Service::refresh(); + $output->info("Refresh Successed!"); + break; + case 'package': + $infoFile = $addonDir . 'info.ini'; + if (!is_file($infoFile)) { + throw new Exception(__('Addon info file was not found')); + } + + $info = get_addon_info($name); + if (!$info) { + throw new Exception(__('Addon info file data incorrect')); + } + $infoname = isset($info['name']) ? $info['name'] : ''; + if (!$infoname || !preg_match("/^[a-z]+$/i", $infoname) || $infoname != $name) { + throw new Exception(__('Addon info name incorrect')); + } + + $infoversion = isset($info['version']) ? $info['version'] : ''; + if (!$infoversion || !preg_match("/^\d+\.\d+\.\d+$/i", $infoversion)) { + throw new Exception(__('Addon info version incorrect')); + } + + $addonTmpDir = RUNTIME_PATH . 'addons' . DS; + if (!is_dir($addonTmpDir)) { + @mkdir($addonTmpDir, 0755, true); + } + $addonFile = $addonTmpDir . $infoname . '-' . $infoversion . '.zip'; + if (!class_exists('ZipArchive')) { + throw new Exception(__('ZinArchive not install')); + } + $zip = new \ZipArchive; + $zip->open($addonFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); + + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($addonDir), \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $name => $file) { + if (!$file->isDir()) { + $filePath = $file->getRealPath(); + $relativePath = str_replace(DS, '/', substr($filePath, strlen($addonDir))); + if (!in_array($file->getFilename(), ['.git', '.DS_Store', 'Thumbs.db'])) { + $zip->addFile($filePath, $relativePath); + } + } + } + $zip->close(); + $output->info("Package Successed!"); + break; + case 'move': + $movePath = [ + 'adminOnlySelfDir' => ['admin/behavior', 'admin/controller', 'admin/library', 'admin/model', 'admin/validate', 'admin/view'], + 'adminAllSubDir' => ['admin/lang'], + 'publicDir' => ['public/assets/addons', 'public/assets/js/backend'] + ]; + $paths = []; + $appPath = str_replace('/', DS, APP_PATH); + $rootPath = str_replace('/', DS, ROOT_PATH); + foreach ($movePath as $k => $items) { + switch ($k) { + case 'adminOnlySelfDir': + foreach ($items as $v) { + $v = str_replace('/', DS, $v); + $oldPath = $appPath . $v . DS . $name; + $newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $name; + $paths[$oldPath] = $newPath; + } + break; + case 'adminAllSubDir': + foreach ($items as $v) { + $v = str_replace('/', DS, $v); + $vPath = $appPath . $v; + $list = scandir($vPath); + foreach ($list as $_v) { + if (!in_array($_v, ['.', '..']) && is_dir($vPath . DS . $_v)) { + $oldPath = $appPath . $v . DS . $_v . DS . $name; + $newPath = $rootPath . "addons" . DS . $name . DS . "application" . DS . $v . DS . $_v . DS . $name; + $paths[$oldPath] = $newPath; + } + } + } + break; + case 'publicDir': + foreach ($items as $v) { + $v = str_replace('/', DS, $v); + $oldPath = $rootPath . $v . DS . $name; + $newPath = $rootPath . 'addons' . DS . $name . DS . $v . DS . $name; + $paths[$oldPath] = $newPath; + } + break; + } + } + foreach ($paths as $oldPath => $newPath) { + if (is_dir($oldPath)) { + if ($force) { + if (is_dir($newPath)) { + $list = scandir($newPath); + foreach ($list as $_v) { + if (!in_array($_v, ['.', '..'])) { + $file = $newPath . DS . $_v; + @chmod($file, 0777); + @unlink($file); + } + } + @rmdir($newPath); + } + } + copydirs($oldPath, $newPath); + } + } + break; + default: + break; + } + } + + /** + * 获取创建菜单的数组 + * @param array $menu + * @return array + */ + protected function getCreateMenu($menu) + { + $result = []; + foreach ($menu as $k => & $v) { + $arr = [ + 'name' => $v['name'], + 'title' => $v['title'], + ]; + if ($v['icon'] != 'fa fa-circle-o') { + $arr['icon'] = $v['icon']; + } + if ($v['ismenu']) { + $arr['ismenu'] = $v['ismenu']; + } + if (isset($v['childlist']) && $v['childlist']) { + $arr['sublist'] = $this->getCreateMenu($v['childlist']); + } + $result[] = $arr; + } + return $result; + } + + /** + * 写入到文件 + * @param string $name + * @param array $data + * @param string $pathname + * @return mixed + */ + protected function writeToFile($name, $data, $pathname) + { + $search = $replace = []; + foreach ($data as $k => $v) { + $search[] = "{%{$k}%}"; + $replace[] = $v; + } + $stub = file_get_contents($this->getStub($name)); + $content = str_replace($search, $replace, $stub); + + if (!is_dir(dirname($pathname))) { + mkdir(strtolower(dirname($pathname)), 0755, true); + } + return file_put_contents($pathname, $content); + } + + /** + * 获取基础模板 + * @param string $name + * @return string + */ + protected function getStub($name) + { + return __DIR__ . '/Addon/stubs/' . $name . '.stub'; + } + +} diff --git a/application/admin/command/Addon/stubs/addon.stub b/application/admin/command/Addon/stubs/addon.stub new file mode 100644 index 0000000..a32cb13 --- /dev/null +++ b/application/admin/command/Addon/stubs/addon.stub @@ -0,0 +1,54 @@ + 'usernmae', + //显示的标题 + 'title' => '用户名', + //类型 + 'type' => 'string', + //分组 + 'group' => '', + //动态显示 + 'visible' => '', + //数据字典 + 'content' => [ + ], + //值 + 'value' => '', + //验证规则 + 'rule' => 'required', + //错误消息 + 'msg' => '', + //提示消息 + 'tip' => '', + //成功消息 + 'ok' => '', + //扩展信息 + 'extend' => '' + ], + [ + 'name' => 'password', + 'title' => '密码', + 'type' => 'string', + 'content' => [ + ], + 'value' => '', + 'rule' => 'required', + 'msg' => '', + 'tip' => '', + 'ok' => '', + 'extend' => '' + ], +]; diff --git a/application/admin/command/Addon/stubs/controller.stub b/application/admin/command/Addon/stubs/controller.stub new file mode 100644 index 0000000..d79476f --- /dev/null +++ b/application/admin/command/Addon/stubs/controller.stub @@ -0,0 +1,15 @@ +error("当前插件暂无前台页面"); + } + +} diff --git a/application/admin/command/Addon/stubs/info.stub b/application/admin/command/Addon/stubs/info.stub new file mode 100644 index 0000000..1c4e304 --- /dev/null +++ b/application/admin/command/Addon/stubs/info.stub @@ -0,0 +1,7 @@ +name = {%name%} +title = 插件名称{%name%} +intro = 插件介绍 +author = yourname +website = https://www.fastadmin.net +version = 1.0.0 +state = 1 diff --git a/application/admin/command/Api.php b/application/admin/command/Api.php new file mode 100644 index 0000000..d9d79c4 --- /dev/null +++ b/application/admin/command/Api.php @@ -0,0 +1,195 @@ +setName('api') + ->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '') + ->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api') + ->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html') + ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html') + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false) + ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name'] ?? '') + ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null) + ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn') + ->addOption('addon', 'a', Option::VALUE_OPTIONAL, 'addon name', null) + ->addOption('controller', 'r', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name', null) + ->setDescription('Build Api document from controller'); + } + + protected function execute(Input $input, Output $output) + { + $apiDir = __DIR__ . DS . 'Api' . DS; + + $force = $input->getOption('force'); + $url = $input->getOption('url'); + $language = $input->getOption('language'); + $template = $input->getOption('template'); + if (!preg_match("/^([a-z0-9]+)\.html\$/i", $template)) { + throw new Exception('template file not correct'); + } + $language = $language ? $language : 'zh-cn'; + $langFile = $apiDir . 'lang' . DS . $language . '.php'; + if (!is_file($langFile)) { + throw new Exception('language file not found'); + } + $lang = include_once $langFile; + // 目标目录 + $output_dir = ROOT_PATH . 'public' . DS; + $output_file = $output_dir . $input->getOption('output'); + if (is_file($output_file) && !$force) { + throw new Exception("api index file already exists!\nIf you need to rebuild again, use the parameter --force=true "); + } + // 模板文件 + $template_dir = $apiDir . 'template' . DS; + $template_file = $template_dir . $template; + if (!is_file($template_file)) { + throw new Exception('template file not found'); + } + // 额外的类 + $classes = $input->getOption('class'); + // 标题 + $title = $input->getOption('title'); + // 模块 + $module = $input->getOption('module'); + // 插件 + $addon = $input->getOption('addon'); + + $moduleDir = $addonDir = ''; + if ($addon) { + $addonInfo = get_addon_info($addon); + if (!$addonInfo) { + throw new Exception('addon not found'); + } + $moduleDir = ADDON_PATH . $addon . DS; + } else { + $moduleDir = APP_PATH . $module . DS; + } + if (!is_dir($moduleDir)) { + throw new Exception('module not found'); + } + + if (version_compare(PHP_VERSION, '7.0.0', '<')) { + throw new Exception("Requires PHP version 7.0 or newer"); + } + + //控制器名 + $controller = $input->getOption('controller') ?: []; + if (!$controller) { + $controllerDir = $moduleDir . Config::get('url_controller_layer') . DS; + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($controllerDir), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $name => $file) { + if (!$file->isDir() && $file->getExtension() == 'php') { + $filePath = $file->getRealPath(); + $classes[] = $this->get_class_from_file($filePath); + } + } + } else { + foreach ($controller as $index => $item) { + $filePath = $moduleDir . Config::get('url_controller_layer') . DS . $item . '.php'; + $classes[] = $this->get_class_from_file($filePath); + } + } + + $classes = array_unique(array_filter($classes)); + + $config = [ + 'sitename' => config('site.name'), + 'title' => $title, + 'author' => config('site.name'), + 'description' => '', + 'apiurl' => $url, + 'language' => $language, + ]; + + $builder = new Builder($classes); + $content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]); + + if (!file_put_contents($output_file, $content)) { + throw new Exception('Cannot save the content to ' . $output_file); + } + $output->info("Build Successed!"); + } + + /** + * get full qualified class name + * + * @param string $path_to_file + * @return string + * @author JBYRNE http://jarretbyrne.com/2015/06/197/ + */ + protected function get_class_from_file($path_to_file) + { + //Grab the contents of the file + $contents = file_get_contents($path_to_file); + + //Start with a blank namespace and class + $namespace = $class = ""; + + //Set helper values to know that we have found the namespace/class token and need to collect the string values after them + $getting_namespace = $getting_class = false; + + //Go through each token and evaluate it as necessary + foreach (token_get_all($contents) as $token) { + + //If this token is the namespace declaring, then flag that the next tokens will be the namespace name + if (is_array($token) && $token[0] == T_NAMESPACE) { + $getting_namespace = true; + } + + //If this token is the class declaring, then flag that the next tokens will be the class name + if (is_array($token) && $token[0] == T_CLASS) { + $getting_class = true; + } + + //While we're grabbing the namespace name... + if ($getting_namespace === true) { + + //If the token is a string or the namespace separator... + if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR])) { + + //Append the token's value to the name of the namespace + $namespace .= $token[1]; + } elseif ($token === ';') { + + //If the token is the semicolon, then we're done with the namespace declaration + $getting_namespace = false; + } + } + + //While we're grabbing the class name... + if ($getting_class === true) { + + //If the token is a string, it's the name of the class + if (is_array($token) && $token[0] == T_STRING) { + + //Store the token's value as the class name + $class = $token[1]; + + //Got what we need, stope here + break; + } + } + } + + //Build the fully-qualified class name and return it + return $namespace ? $namespace . '\\' . $class : $class; + } +} diff --git a/application/admin/command/Api/lang/zh-cn.php b/application/admin/command/Api/lang/zh-cn.php new file mode 100644 index 0000000..7f0ecc7 --- /dev/null +++ b/application/admin/command/Api/lang/zh-cn.php @@ -0,0 +1,25 @@ + '基础信息', + 'Sandbox' => '在线测试', + 'Sampleoutput' => '返回示例', + 'Headers' => 'Headers', + 'Parameters' => '参数', + 'Body' => '正文', + 'Name' => '名称', + 'Type' => '类型', + 'Required' => '必选', + 'Description' => '描述', + 'Send' => '提交', + 'Reset' => '重置', + 'Tokentips' => 'Token在会员注册或登录后都会返回,WEB端同时存在于Cookie中', + 'Apiurltips' => 'API接口URL', + 'Savetips' => '点击保存后Token和Api url都将保存在本地Localstorage中', + 'Authorization' => '权限', + 'NeedLogin' => '登录', + 'NeedRight' => '鉴权', + 'ReturnHeaders' => '响应头', + 'ReturnParameters' => '返回参数', + 'Response' => '响应输出', +]; diff --git a/application/admin/command/Api/library/Builder.php b/application/admin/command/Api/library/Builder.php new file mode 100644 index 0000000..64a2ebd --- /dev/null +++ b/application/admin/command/Api/library/Builder.php @@ -0,0 +1,253 @@ + + * @author Karson + */ +class Builder +{ + + /** + * + * @var \think\View + */ + public $view = null; + + /** + * parse classes + * @var array + */ + protected $classes = []; + + /** + * + * @param array $classes + */ + public function __construct($classes = []) + { + $this->classes = array_merge($this->classes, $classes); + $this->view = new \think\View(Config::get('template'), Config::get('view_replace_str')); + } + + protected function extractAnnotations() + { + foreach ($this->classes as $class) { + $classAnnotation = Extractor::getClassAnnotations($class); + // 如果忽略 + if (isset($classAnnotation['ApiInternal'])) { + continue; + } + Extractor::getClassMethodAnnotations($class); + //Extractor::getClassPropertyValues($class); + } + $allClassAnnotation = Extractor::getAllClassAnnotations(); + $allClassMethodAnnotation = Extractor::getAllClassMethodAnnotations(); + //$allClassPropertyValue = Extractor::getAllClassPropertyValues(); + +// foreach ($allClassMethodAnnotation as $className => &$methods) { +// foreach ($methods as &$method) { +// //权重判断 +// if ($method && !isset($method['ApiWeigh']) && isset($allClassAnnotation[$className]['ApiWeigh'])) { +// $method['ApiWeigh'] = $allClassAnnotation[$className]['ApiWeigh']; +// } +// } +// } +// unset($methods); + return [$allClassAnnotation, $allClassMethodAnnotation]; + } + + protected function generateHeadersTemplate($docs) + { + if (!isset($docs['ApiHeaders'])) { + return []; + } + + $headerslist = array(); + foreach ($docs['ApiHeaders'] as $params) { + $tr = array( + 'name' => $params['name'] ?? '', + 'type' => $params['type'] ?? 'string', + 'sample' => $params['sample'] ?? '', + 'required' => $params['required'] ?? false, + 'description' => $params['description'] ?? '', + ); + $headerslist[] = $tr; + } + + return $headerslist; + } + + protected function generateParamsTemplate($docs) + { + if (!isset($docs['ApiParams'])) { + return []; + } + + $paramslist = array(); + foreach ($docs['ApiParams'] as $params) { + $tr = array( + 'name' => $params['name'], + 'type' => $params['type'] ?? 'string', + 'sample' => $params['sample'] ?? '', + 'required' => $params['required'] ?? true, + 'description' => $params['description'] ?? '', + ); + $paramslist[] = $tr; + } + + return $paramslist; + } + + protected function generateReturnHeadersTemplate($docs) + { + if (!isset($docs['ApiReturnHeaders'])) { + return []; + } + + $headerslist = array(); + foreach ($docs['ApiReturnHeaders'] as $params) { + $tr = array( + 'name' => $params['name'] ?? '', + 'type' => 'string', + 'sample' => $params['sample'] ?? '', + 'required' => isset($params['required']) && $params['required'] ? 'Yes' : 'No', + 'description' => $params['description'] ?? '', + ); + $headerslist[] = $tr; + } + + return $headerslist; + } + + protected function generateReturnParamsTemplate($st_params) + { + if (!isset($st_params['ApiReturnParams'])) { + return []; + } + + $paramslist = array(); + foreach ($st_params['ApiReturnParams'] as $params) { + $tr = array( + 'name' => $params['name'] ?? '', + 'type' => $params['type'] ?? 'string', + 'sample' => $params['sample'] ?? '', + 'description' => $params['description'] ?? '', + ); + $paramslist[] = $tr; + } + + return $paramslist; + } + + protected function generateBadgeForMethod($data) + { + $method = strtoupper(is_array($data['ApiMethod'][0]) ? $data['ApiMethod'][0]['data'] : $data['ApiMethod'][0]); + $labes = array( + 'POST' => 'label-primary', + 'GET' => 'label-success', + 'PUT' => 'label-warning', + 'DELETE' => 'label-danger', + 'PATCH' => 'label-default', + 'OPTIONS' => 'label-info' + ); + + return isset($labes[$method]) ? $labes[$method] : $labes['GET']; + } + + public function parse() + { + list($allClassAnnotations, $allClassMethodAnnotations) = $this->extractAnnotations(); + + $sectorArr = []; + foreach ($allClassAnnotations as $index => &$allClassAnnotation) { + // 如果设置隐藏,则不显示在文档 + if (isset($allClassAnnotation['ApiInternal'])) { + continue; + } + $sector = isset($allClassAnnotation['ApiSector']) ? $allClassAnnotation['ApiSector'][0] : $allClassAnnotation['ApiTitle'][0]; + $sectorArr[$sector] = isset($allClassAnnotation['ApiWeigh']) ? $allClassAnnotation['ApiWeigh'][0] : 0; + } + unset($allClassAnnotation); + + arsort($sectorArr); + $routes = include_once CONF_PATH . 'route.php'; + $subdomain = false; + if (config('url_domain_deploy') && isset($routes['__domain__']) && isset($routes['__domain__']['api']) && $routes['__domain__']['api']) { + $subdomain = true; + } + $counter = 0; + $section = null; + $weigh = 0; + $docsList = []; + foreach ($allClassMethodAnnotations as $class => $methods) { + foreach ($methods as $name => $docs) { + if (isset($docs['ApiSector'][0])) { + $section = is_array($docs['ApiSector'][0]) ? $docs['ApiSector'][0]['data'] : $docs['ApiSector'][0]; + } else { + $section = $class; + } + if (0 === count($docs)) { + continue; + } + $route = is_array($docs['ApiRoute'][0]) ? $docs['ApiRoute'][0]['data'] : $docs['ApiRoute'][0]; + if ($subdomain) { + $route = substr($route, 4); + } + $docsList[$section][$name] = [ + 'id' => $counter, + 'method' => is_array($docs['ApiMethod'][0]) ? $docs['ApiMethod'][0]['data'] : $docs['ApiMethod'][0], + 'methodLabel' => $this->generateBadgeForMethod($docs), + 'section' => $section, + 'route' => $route, + 'title' => is_array($docs['ApiTitle'][0]) ? $docs['ApiTitle'][0]['data'] : $docs['ApiTitle'][0], + 'summary' => is_array($docs['ApiSummary'][0]) ? $docs['ApiSummary'][0]['data'] : $docs['ApiSummary'][0], + 'body' => isset($docs['ApiBody'][0]) ? (is_array($docs['ApiBody'][0]) ? $docs['ApiBody'][0]['data'] : $docs['ApiBody'][0]) : '', + 'headersList' => $this->generateHeadersTemplate($docs), + 'paramsList' => $this->generateParamsTemplate($docs), + 'returnHeadersList' => $this->generateReturnHeadersTemplate($docs), + 'returnParamsList' => $this->generateReturnParamsTemplate($docs), + 'weigh' => is_array($docs['ApiWeigh'][0]) ? $docs['ApiWeigh'][0]['data'] : $docs['ApiWeigh'][0], + 'return' => isset($docs['ApiReturn']) ? (is_array($docs['ApiReturn'][0]) ? $docs['ApiReturn'][0]['data'] : $docs['ApiReturn'][0]) : '', + 'needLogin' => $docs['ApiPermissionLogin'][0], + 'needRight' => $docs['ApiPermissionRight'][0], + ]; + $counter++; + } + } + + //重建排序 + foreach ($docsList as $index => &$methods) { + $methodSectorArr = []; + foreach ($methods as $name => $method) { + $methodSectorArr[$name] = isset($method['weigh']) ? $method['weigh'] : 0; + } + arsort($methodSectorArr); + $methods = array_merge(array_flip(array_keys($methodSectorArr)), $methods); + } + $docsList = array_merge(array_flip(array_keys($sectorArr)), $docsList); + return $docsList; + } + + public function getView() + { + return $this->view; + } + + /** + * 渲染 + * @param string $template + * @param array $vars + * @return string + */ + public function render($template, $vars = []) + { + $docsList = $this->parse(); + + return $this->view->display(file_get_contents($template), array_merge($vars, ['docsList' => $docsList])); + } +} diff --git a/application/admin/command/Api/library/Extractor.php b/application/admin/command/Api/library/Extractor.php new file mode 100644 index 0000000..575bf05 --- /dev/null +++ b/application/admin/command/Api/library/Extractor.php @@ -0,0 +1,544 @@ + + */ +class Extractor +{ + + /** + * Static array to store already parsed annotations + * @var array + */ + private static $annotationCache; + + private static $classAnnotationCache; + + private static $classMethodAnnotationCache; + + private static $classPropertyValueCache; + + /** + * Indicates that annotations should has strict behavior, 'false' by default + * @var boolean + */ + private $strict = false; + + /** + * Stores the default namespace for Objects instance, usually used on methods like getMethodAnnotationsObjects() + * @var string + */ + public $defaultNamespace = ''; + + /** + * Sets strict variable to true/false + * @param bool $value boolean value to indicate that annotations to has strict behavior + */ + public function setStrict($value) + { + $this->strict = (bool)$value; + } + + /** + * Sets default namespace to use in object instantiation + * @param string $namespace default namespace + */ + public function setDefaultNamespace($namespace) + { + $this->defaultNamespace = $namespace; + } + + /** + * Gets default namespace used in object instantiation + * @return string $namespace default namespace + */ + public function getDefaultAnnotationNamespace() + { + return $this->defaultNamespace; + } + + /** + * Gets all anotations with pattern @SomeAnnotation() from a given class + * + * @param string $className class name to get annotations + * @return array self::$classAnnotationCache all annotated elements + */ + public static function getClassAnnotations($className) + { + if (!isset(self::$classAnnotationCache[$className])) { + $class = new \ReflectionClass($className); + $annotationArr = self::parseAnnotations($class->getDocComment()); + $annotationArr['ApiTitle'] = !isset($annotationArr['ApiTitle'][0]) || !trim($annotationArr['ApiTitle'][0]) ? [$class->getShortName()] : $annotationArr['ApiTitle']; + self::$classAnnotationCache[$className] = $annotationArr; + } + + return self::$classAnnotationCache[$className]; + } + + /** + * 获取类所有方法的属性配置 + * @param $className + * @return mixed + * @throws \ReflectionException + */ + public static function getClassMethodAnnotations($className) + { + $class = new \ReflectionClass($className); + + foreach ($class->getMethods() as $object) { + self::$classMethodAnnotationCache[$className][$object->name] = self::getMethodAnnotations($className, $object->name); + } + + return self::$classMethodAnnotationCache[$className]; + } + + public static function getClassPropertyValues($className) + { + $class = new \ReflectionClass($className); + + foreach ($class->getProperties() as $object) { + self::$classPropertyValueCache[$className][$object->name] = self::getClassPropertyValue($className, $object->name); + } + + return self::$classMethodAnnotationCache[$className]; + } + + public static function getAllClassAnnotations() + { + return self::$classAnnotationCache; + } + + public static function getAllClassMethodAnnotations() + { + return self::$classMethodAnnotationCache; + } + + public static function getAllClassPropertyValues() + { + return self::$classPropertyValueCache; + } + + public static function getClassPropertyValue($className, $property) + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $reflectionClass = new \ReflectionClass($className); + $reflectionProperty = $reflectionClass->getProperty($property); + $reflectionProperty->setAccessible(true); + return $reflectionProperty->getValue($reflectionClass->newInstanceWithoutConstructor()); + } + + /** + * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class + * + * @param string $className class name + * @param string $methodName method name to get annotations + * @return array self::$annotationCache all annotated elements of a method given + */ + public static function getMethodAnnotations($className, $methodName) + { + if (!isset(self::$annotationCache[$className . '::' . $methodName])) { + try { + $method = new \ReflectionMethod($className, $methodName); + $class = new \ReflectionClass($className); + if (!$method->isPublic() || $method->isConstructor()) { + $annotations = array(); + } else { + $annotations = self::consolidateAnnotations($method, $class); + } + } catch (\ReflectionException $e) { + $annotations = array(); + } + + self::$annotationCache[$className . '::' . $methodName] = $annotations; + } + + return self::$annotationCache[$className . '::' . $methodName]; + } + + /** + * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class + * and instance its abcAnnotation class + * + * @param string $className class name + * @param string $methodName method name to get annotations + * @return array self::$annotationCache all annotated objects of a method given + */ + public function getMethodAnnotationsObjects($className, $methodName) + { + $annotations = $this->getMethodAnnotations($className, $methodName); + $objects = array(); + + $i = 0; + + foreach ($annotations as $annotationClass => $listParams) { + $annotationClass = ucfirst($annotationClass); + $class = $this->defaultNamespace . $annotationClass . 'Annotation'; + + // verify is the annotation class exists, depending if Annotations::strict is true + // if not, just skip the annotation instance creation. + if (!class_exists($class)) { + if ($this->strict) { + throw new Exception(sprintf('Runtime Error: Annotation Class Not Found: %s', $class)); + } else { + // silent skip & continue + continue; + } + } + + if (empty($objects[$annotationClass])) { + $objects[$annotationClass] = new $class(); + } + + foreach ($listParams as $params) { + if (is_array($params)) { + foreach ($params as $key => $value) { + $objects[$annotationClass]->set($key, $value); + } + } else { + $objects[$annotationClass]->set($i++, $params); + } + } + } + + return $objects; + } + + private static function consolidateAnnotations($method, $class) + { + $dockblockClass = $class->getDocComment(); + $docblockMethod = $method->getDocComment(); + $methodName = $method->getName(); + + $methodAnnotations = self::parseAnnotations($docblockMethod); + $methodAnnotations['ApiTitle'] = !isset($methodAnnotations['ApiTitle'][0]) || !trim($methodAnnotations['ApiTitle'][0]) ? [$method->getName()] : $methodAnnotations['ApiTitle']; + + $classAnnotations = self::parseAnnotations($dockblockClass); + $classAnnotations['ApiTitle'] = !isset($classAnnotations['ApiTitle'][0]) || !trim($classAnnotations['ApiTitle'][0]) ? [$class->getShortName()] : $classAnnotations['ApiTitle']; + + if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty') { + return []; + } + + $properties = $class->getDefaultProperties(); + $noNeedLogin = isset($properties['noNeedLogin']) ? (is_array($properties['noNeedLogin']) ? $properties['noNeedLogin'] : [$properties['noNeedLogin']]) : []; + $noNeedRight = isset($properties['noNeedRight']) ? (is_array($properties['noNeedRight']) ? $properties['noNeedRight'] : [$properties['noNeedRight']]) : []; + + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblockMethod), $methodArr); + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $dockblockClass), $classArr); + + if (!isset($methodAnnotations['ApiMethod'])) { + $methodAnnotations['ApiMethod'] = ['get']; + } + if (!isset($methodAnnotations['ApiWeigh'])) { + $methodAnnotations['ApiWeigh'] = [0]; + } + if (!isset($methodAnnotations['ApiSummary'])) { + $methodAnnotations['ApiSummary'] = $methodAnnotations['ApiTitle']; + } + + if ($methodAnnotations) { + foreach ($classAnnotations as $name => $valueClass) { + if (count($valueClass) !== 1) { + continue; + } + + if ($name === 'ApiRoute') { + if (isset($methodAnnotations[$name])) { + $methodAnnotations[$name] = [rtrim($valueClass[0], '/') . $methodAnnotations[$name][0]]; + } else { + $methodAnnotations[$name] = [rtrim($valueClass[0], '/') . '/' . $method->getName()]; + } + } + + if ($name === 'ApiSector') { + $methodAnnotations[$name] = $valueClass; + } + } + } + if (!isset($methodAnnotations['ApiRoute'])) { + $urlArr = []; + $className = $class->getName(); + + list($prefix, $suffix) = explode('\\' . \think\Config::get('url_controller_layer') . '\\', $className); + $prefixArr = explode('\\', $prefix); + $suffixArr = explode('\\', $suffix); + if ($prefixArr[0] == \think\Config::get('app_namespace')) { + $prefixArr[0] = ''; + } + $urlArr = array_merge($urlArr, $prefixArr); + $urlArr[] = implode('.', array_map(function ($item) { + return \think\Loader::parseName($item); + }, $suffixArr)); + $urlArr[] = $method->getName(); + + $methodAnnotations['ApiRoute'] = [implode('/', $urlArr)]; + } + if (!isset($methodAnnotations['ApiSector'])) { + $methodAnnotations['ApiSector'] = isset($classAnnotations['ApiSector']) ? $classAnnotations['ApiSector'] : $classAnnotations['ApiTitle']; + } + if (!isset($methodAnnotations['ApiParams'])) { + $params = self::parseCustomAnnotations($docblockMethod, 'param'); + foreach ($params as $k => $v) { + $arr = explode(' ', preg_replace("/[\s]+/", " ", $v)); + $methodAnnotations['ApiParams'][] = [ + 'name' => isset($arr[1]) ? str_replace('$', '', $arr[1]) : '', + 'nullable' => false, + 'type' => isset($arr[0]) ? $arr[0] : 'string', + 'description' => isset($arr[2]) ? $arr[2] : '' + ]; + } + } + $methodAnnotations['ApiPermissionLogin'] = [!in_array('*', $noNeedLogin) && !in_array($methodName, $noNeedLogin)]; + $methodAnnotations['ApiPermissionRight'] = !$methodAnnotations['ApiPermissionLogin'][0] ? [false] : [!in_array('*', $noNeedRight) && !in_array($methodName, $noNeedRight)]; + return $methodAnnotations; + } + + /** + * Parse annotations + * + * @param string $docblock + * @param string $name + * @return array parsed annotations params + */ + private static function parseCustomAnnotations($docblock, $name = 'param') + { + $annotations = array(); + + $docblock = substr($docblock, 3, -2); + if (preg_match_all('/@' . $name . '(?:\s*(?:\(\s*)?(.*?)(?:\s*\))?)??\s*(?:\n|\*\/)/', $docblock, $matches)) { + foreach ($matches[1] as $k => $v) { + $annotations[] = $v; + } + } + return $annotations; + } + + /** + * Parse annotations + * + * @param string $docblock + * @return array parsed annotations params + */ + private static function parseAnnotations($docblock) + { + $annotations = array(); + + // Strip away the docblock header and footer to ease parsing of one line annotations + $docblock = substr($docblock, 3, -2); + if (preg_match_all('/@(?[A-Za-z_-]+)[\s\t]*\((?(?:(?!\)).)*)\)\r?/s', $docblock, $matches)) { + $numMatches = count($matches[0]); + for ($i = 0; $i < $numMatches; ++$i) { + $name = $matches['name'][$i]; + $value = ''; + // annotations has arguments + if (isset($matches['args'][$i])) { + $argsParts = trim($matches['args'][$i]); + if ($name == 'ApiReturn') { + $value = $argsParts; + } elseif ($matches['args'][$i] != '') { + $argsParts = preg_replace("/\{(\w+)\}/", '#$1#', $argsParts); + $value = self::parseArgs($argsParts); + if (is_string($value)) { + $value = preg_replace("/\#(\w+)\#/", '{$1}', $argsParts); + } + } + } + + $annotations[$name][] = $value; + } + } + if (stripos($docblock, '@ApiInternal') !== false) { + $annotations['ApiInternal'] = [true]; + } + if (!isset($annotations['ApiTitle'])) { + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblock), $matchArr); + $title = isset($matchArr[1]) && isset($matchArr[1][0]) ? $matchArr[1][0] : ''; + $annotations['ApiTitle'] = [$title]; + } + + return $annotations; + } + + /** + * Parse individual annotation arguments + * + * @param string $content arguments string + * @return array annotated arguments + */ + private static function parseArgs($content) + { + // Replace initial stars + $content = preg_replace('/^\s*\*/m', '', $content); + + $data = array(); + $len = strlen($content); + $i = 0; + $var = ''; + $val = ''; + $level = 1; + + $prevDelimiter = ''; + $nextDelimiter = ''; + $nextToken = ''; + $composing = false; + $type = 'plain'; + $delimiter = null; + $quoted = false; + $tokens = array('"', '"', '{', '}', ',', '='); + + while ($i <= $len) { + $prev_c = substr($content, $i - 1, 1); + $c = substr($content, $i++, 1); + + if ($c === '"' && $prev_c !== "\\") { + $delimiter = $c; + //open delimiter + if (!$composing && empty($prevDelimiter) && empty($nextDelimiter)) { + $prevDelimiter = $nextDelimiter = $delimiter; + $val = ''; + $composing = true; + $quoted = true; + } else { + // close delimiter + if ($c !== $nextDelimiter) { + throw new Exception(sprintf( + "Parse Error: enclosing error -> expected: [%s], given: [%s]", + $nextDelimiter, + $c + )); + } + + // validating syntax + if ($i < $len) { + if (',' !== substr($content, $i, 1) && '\\' !== $prev_c) { + throw new Exception(sprintf( + "Parse Error: missing comma separator near: ...%s<--", + substr($content, ($i - 10), $i) + )); + } + } + + $prevDelimiter = $nextDelimiter = ''; + $composing = false; + $delimiter = null; + } + } elseif (!$composing && in_array($c, $tokens)) { + switch ($c) { + case '=': + $prevDelimiter = $nextDelimiter = ''; + $level = 2; + $composing = false; + $type = 'assoc'; + $quoted = false; + break; + case ',': + $level = 3; + + // If composing flag is true yet, + // it means that the string was not enclosed, so it is parsing error. + if ($composing === true && !empty($prevDelimiter) && !empty($nextDelimiter)) { + throw new Exception(sprintf( + "Parse Error: enclosing error -> expected: [%s], given: [%s]", + $nextDelimiter, + $c + )); + } + + $prevDelimiter = $nextDelimiter = ''; + break; + case '{': + $subc = ''; + $subComposing = true; + + while ($i <= $len) { + $c = substr($content, $i++, 1); + + if (isset($delimiter) && $c === $delimiter) { + throw new Exception(sprintf( + "Parse Error: Composite variable is not enclosed correctly." + )); + } + + if ($c === '}') { + $subComposing = false; + break; + } + $subc .= $c; + } + + // if the string is composing yet means that the structure of var. never was enclosed with '}' + if ($subComposing) { + throw new Exception(sprintf( + "Parse Error: Composite variable is not enclosed correctly. near: ...%s'", + $subc + )); + } + + $val = self::parseArgs($subc); + break; + } + } else { + if ($level == 1) { + $var .= $c; + } elseif ($level == 2) { + $val .= $c; + } + } + + if ($level === 3 || $i === $len) { + if ($type == 'plain' && $i === $len) { + $data = self::castValue($var); + } else { + $data[trim($var)] = self::castValue($val, !$quoted); + } + + $level = 1; + $var = $val = ''; + $composing = false; + $quoted = false; + } + } + + return $data; + } + + /** + * Try determinate the original type variable of a string + * + * @param string $val string containing possibles variables that can be cast to bool or int + * @param boolean $trim indicate if the value passed should be trimmed after to try cast + * @return mixed returns the value converted to original type if was possible + */ + private static function castValue($val, $trim = false) + { + if (is_array($val)) { + foreach ($val as $key => $value) { + $val[$key] = self::castValue($value); + } + } elseif (is_string($val)) { + if ($trim) { + $val = trim($val); + } + $val = stripslashes($val); + $tmp = strtolower($val); + + if ($tmp === 'false' || $tmp === 'true') { + $val = $tmp === 'true'; + } elseif (is_numeric($val)) { + return $val + 0; + } + + unset($tmp); + } + + return $val; + } +} diff --git a/application/admin/command/Api/template/index.html b/application/admin/command/Api/template/index.html new file mode 100644 index 0000000..02ee4f4 --- /dev/null +++ b/application/admin/command/Api/template/index.html @@ -0,0 +1,654 @@ + + + + + + + + {$config.title} + + + + + + + + + + + + + + + + +
+ + +
+ {foreach name="docsList" id="docs"} +

{$key}

+
+ {foreach name="docs" id="api" } +
+
+

+ {$api.method|strtoupper} + {$api.title} {$api.route} +

+
+
+
+ + + + + +
+ +
+
+ {$api.summary} +
+
+
{$lang.Authorization}
+
+ + + + + + + + + + + +
{$lang.NeedLogin}{$api.needLogin?'是':'否'}
{$lang.NeedRight}{$api.needRight?'是':'否'}
+
+
+
+
{$lang.Headers}
+
+ {if $api.headersList} + + + + + + + + + + + {foreach name="api['headersList']" id="header"} + + + + + + + {/foreach} + +
{$lang.Name}{$lang.Type}{$lang.Required}{$lang.Description}
{$header.name}{$header.type}{$header.required?'是':'否'}{$header.description}
+ {else /} + 无 + {/if} +
+
+
+
{$lang.Parameters}
+
+ {if $api.paramsList} + + + + + + + + + + + {foreach name="api['paramsList']" id="param"} + + + + + + + {/foreach} + +
{$lang.Name}{$lang.Type}{$lang.Required}{$lang.Description}
{$param.name}{$param.type}{:$param.required?'是':'否'}{$param.description}
+ {else /} + 无 + {/if} +
+
+
+
{$lang.Body}
+
+ {$api.body|default='无'} +
+
+
+ +
+
+
+ {if $api.headersList} +
+
{$lang.Headers}
+
+
+ {foreach name="api['headersList']" id="param"} +
+ + +
+ {/foreach} +
+
+
+ {/if} +
+
{$lang.Parameters} +
+ 追加 +
+
+
+
+ {if $api.paramsList} + {foreach name="api['paramsList']" id="param"} +
+ + +
+ {/foreach} + {else /} +
+ 无 +
+ {/if} +
+ + +
+
+
+
+
+
{$lang.Response}
+
+
+
+

+                                                            

+                                                        
+
+
+
+
+
{$lang.ReturnParameters}
+
+ {if $api.returnParamsList} + + + + + + + + + + {foreach name="api['returnParamsList']" id="param"} + + + + + + {/foreach} + +
{$lang.Name}{$lang.Type}{$lang.Description}
{$param.name}{$param.type}{$param.description}
+ {else /} + 无 + {/if} +
+
+
+
+
+ +
+
+
+
{$api.return|default='无'}
+
+
+
+ +
+
+
+
+ {/foreach} + {/foreach} +
+ +
+ + + +
+ + + + + + + + + + + diff --git a/application/admin/command/Crud.php b/application/admin/command/Crud.php new file mode 100644 index 0000000..390cb95 --- /dev/null +++ b/application/admin/command/Crud.php @@ -0,0 +1,1770 @@ + ['user_id', 'user_ids', 'admin_id', 'admin_ids'] + ]; + + /** + * Enum类型识别为单选框的结尾字符,默认会识别为单选下拉列表 + */ + protected $enumRadioSuffix = ['data', 'state', 'status']; + + /** + * Set类型识别为复选框的结尾字符,默认会识别为多选下拉列表 + */ + protected $setCheckboxSuffix = ['data', 'state', 'status']; + + /** + * Int类型识别为日期时间的结尾字符,默认会识别为日期文本框 + */ + protected $intDateSuffix = ['time']; + + /** + * 开关后缀 + */ + protected $switchSuffix = ['switch']; + + /** + * 富文本后缀 + */ + protected $editorSuffix = ['content']; + + /** + * 城市后缀 + */ + protected $citySuffix = ['city']; + + /** + * 时间区间后缀 + */ + protected $rangeSuffix = ['range']; + + /** + * JSON后缀 + */ + protected $jsonSuffix = ['json']; + + /** + * 标签后缀 + */ + protected $tagSuffix = ['tag', 'tags']; + + /** + * Selectpage对应的后缀 + */ + protected $selectpageSuffix = ['_id', '_ids']; + + /** + * Selectpage多选对应的后缀 + */ + protected $selectpagesSuffix = ['_ids']; + + /** + * 以指定字符结尾的字段格式化函数 + */ + protected $fieldFormatterSuffix = [ + 'status' => ['type' => ['varchar', 'enum'], 'name' => 'status'], + 'icon' => 'icon', + 'flag' => 'flag', + 'url' => 'url', + 'image' => 'image', + 'images' => 'images', + 'file' => 'file', + 'files' => 'files', + 'avatar' => 'image', + 'switch' => 'toggle', + 'tag' => 'flag', + 'tags' => 'flag', + 'time' => ['type' => ['int', 'bigint', 'timestamp'], 'name' => 'datetime'], + ]; + + /** + * 识别为图片字段 + */ + protected $imageField = ['image', 'images', 'avatar', 'avatars']; + + /** + * 识别为文件字段 + */ + protected $fileField = ['file', 'files']; + + /** + * 保留字段 + */ + protected $reservedField = ['admin_id']; + + /** + * 排除字段 + */ + protected $ignoreFields = []; + + /** + * 排序字段 + */ + protected $sortField = 'weigh'; + + /** + * 筛选字段 + * @var string + */ + protected $headingFilterField = 'status'; + + /** + * 添加时间字段 + * @var string + */ + protected $createTimeField = 'createtime'; + + /** + * 更新时间字段 + * @var string + */ + protected $updateTimeField = 'updatetime'; + + /** + * 软删除时间字段 + * @var string + */ + protected $deleteTimeField = 'deletetime'; + + /** + * 编辑器的Class + */ + protected $editorClass = 'editor'; + + /** + * langList的key最长字节数 + */ + protected $fieldMaxLen = 0; + + protected function configure() + { + $this + ->setName('crud') + ->addOption('table', 't', Option::VALUE_REQUIRED, 'table name without prefix', null) + ->addOption('controller', 'c', Option::VALUE_OPTIONAL, 'controller name', null) + ->addOption('model', 'm', Option::VALUE_OPTIONAL, 'model name', null) + ->addOption('fields', 'i', Option::VALUE_OPTIONAL, 'model visible fields', null) + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override or force delete,without tips', null) + ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local model', 1) + ->addOption('import', 'a', Option::VALUE_OPTIONAL, 'enable import function', 0) + ->addOption('relation', 'r', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table name without prefix', null) + ->addOption('relationmodel', 'e', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation model name', null) + ->addOption('relationforeignkey', 'k', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation foreign key', null) + ->addOption('relationprimarykey', 'p', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation primary key', null) + ->addOption('relationfields', 's', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table fields', null) + ->addOption('relationmode', 'o', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table mode,hasone/belongsto/hasmany', null) + ->addOption('relationcontroller', 'w', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'relation table controller,only work at hasmany mode', null) + ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete all files generated by CRUD', null) + ->addOption('menu', 'u', Option::VALUE_OPTIONAL, 'create menu when CRUD completed', null) + ->addOption('setcheckboxsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate checkbox component with suffix', null) + ->addOption('enumradiosuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate radio component with suffix', null) + ->addOption('imagefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate image component with suffix', null) + ->addOption('filefield', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate file component with suffix', null) + ->addOption('intdatesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate date component with suffix', null) + ->addOption('switchsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate switch component with suffix', null) + ->addOption('citysuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate citypicker component with suffix', null) + ->addOption('jsonsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate fieldlist component with suffix', null) + ->addOption('tagsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate tag component with suffix', null) + ->addOption('editorsuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate editor component with suffix', null) + ->addOption('selectpagesuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate selectpage component with suffix', null) + ->addOption('selectpagessuffix', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'automatically generate multiple selectpage component with suffix', null) + ->addOption('ignorefields', null, Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'ignore fields', null) + ->addOption('sortfield', null, Option::VALUE_OPTIONAL, 'sort field', null) + ->addOption('headingfilterfield', null, Option::VALUE_OPTIONAL, 'heading filter field', null) + ->addOption('fixedcolumns', null, Option::VALUE_OPTIONAL, 'fixed columns', null) + ->addOption('editorclass', null, Option::VALUE_OPTIONAL, 'automatically generate editor class', null) + ->addOption('db', null, Option::VALUE_OPTIONAL, 'database config name', 'database') + ->setDescription('Build CRUD controller and model from table'); + } + + protected function execute(Input $input, Output $output) + { + $adminPath = dirname(__DIR__) . DS; + //数据库 + $db = $input->getOption('db'); + //表名 + $table = $input->getOption('table') ?: ''; + //自定义控制器 + $controller = $input->getOption('controller'); + //自定义模型 + $model = $input->getOption('model'); + $model = $model ? $model : $controller; + //验证器类 + $validate = $model; + //自定义显示字段 + $fields = $input->getOption('fields'); + //强制覆盖 + $force = $input->getOption('force'); + //是否为本地model,为0时表示为全局model将会把model放在app/common/model中 + $local = $input->getOption('local'); + //是否启用导入功能 + $import = $input->getOption('import'); + + if (!$table) { + throw new Exception('table name can\'t empty'); + } + + + //是否生成菜单 + $menu = $input->getOption("menu"); + //关联表 + $relation = $input->getOption('relation'); + //自定义关联表模型 + $relationModels = $input->getOption('relationmodel'); + //模式 + $relationMode = $mode = $input->getOption('relationmode'); + //外键 + $relationForeignKey = $input->getOption('relationforeignkey'); + //主键 + $relationPrimaryKey = $input->getOption('relationprimarykey'); + //关联表显示字段 + $relationFields = $input->getOption('relationfields'); + //关联表显示字段 + $relationController = $input->getOption('relationcontroller'); + //复选框后缀 + $setcheckboxsuffix = $input->getOption('setcheckboxsuffix'); + //单选框后缀 + $enumradiosuffix = $input->getOption('enumradiosuffix'); + //图片后缀 + $imagefield = $input->getOption('imagefield'); + //文件后缀 + $filefield = $input->getOption('filefield'); + //标签后缀 + $tagsuffix = $input->getOption('tagsuffix'); + //日期后缀 + $intdatesuffix = $input->getOption('intdatesuffix'); + //开关后缀 + $switchsuffix = $input->getOption('switchsuffix'); + //富文本编辑器 + $editorsuffix = $input->getOption('editorsuffix'); + //城市后缀 + $citysuffix = $input->getOption('citysuffix'); + //JSON配置后缀 + $jsonsuffix = $input->getOption('jsonsuffix'); + //selectpage后缀 + $selectpagesuffix = $input->getOption('selectpagesuffix'); + //selectpage多选后缀 + $selectpagessuffix = $input->getOption('selectpagessuffix'); + //排除字段 + $ignoreFields = $input->getOption('ignorefields'); + //排序字段 + $sortfield = $input->getOption('sortfield'); + //顶部筛选过滤字段 + $headingfilterfield = $input->getOption('headingfilterfield'); + //固定列数量 + $fixedcolumns = $input->getOption('fixedcolumns'); + //编辑器Class + $editorclass = $input->getOption('editorclass'); + if ($setcheckboxsuffix) { + $this->setCheckboxSuffix = $setcheckboxsuffix; + } + if ($enumradiosuffix) { + $this->enumRadioSuffix = $enumradiosuffix; + } + if ($imagefield) { + $this->imageField = $imagefield; + } + if ($filefield) { + $this->fileField = $filefield; + } + if ($tagsuffix) { + $this->tagSuffix = $tagsuffix; + } + if ($intdatesuffix) { + $this->intDateSuffix = $intdatesuffix; + } + if ($switchsuffix) { + $this->switchSuffix = $switchsuffix; + } + if ($editorsuffix) { + $this->editorSuffix = $editorsuffix; + } + if ($citysuffix) { + $this->citySuffix = $citysuffix; + } + if ($jsonsuffix) { + $this->jsonSuffix = $jsonsuffix; + } + if ($selectpagesuffix) { + $this->selectpageSuffix = $selectpagesuffix; + } + if ($selectpagessuffix) { + $this->selectpagesSuffix = $selectpagessuffix; + } + if ($ignoreFields) { + $this->ignoreFields = $ignoreFields; + } + if ($editorclass) { + $this->editorClass = $editorclass; + } + if ($sortfield) { + $this->sortField = $sortfield; + } + if ($headingfilterfield) { + $this->headingFilterField = $headingfilterfield; + } + + $this->reservedField = array_merge($this->reservedField, [$this->createTimeField, $this->updateTimeField, $this->deleteTimeField]); + + $dbconnect = Db::connect($db); + $dbname = Config::get($db . '.database'); + $prefix = Config::get($db . '.prefix'); + + //系统表无法生成,防止后台错乱 + if (in_array(str_replace($prefix, "", $table), $this->systemTables)) { + throw new Exception('system table can\'t be crud'); + } + + //模块 + $moduleName = 'admin'; + $modelModuleName = $local ? $moduleName : 'common'; + $validateModuleName = $local ? $moduleName : 'common'; + + //检查主表 + $modelName = $table = stripos($table, $prefix) === 0 ? substr($table, strlen($prefix)) : $table; + $modelTableType = 'table'; + $modelTableTypeName = $modelTableName = $modelName; + $modelTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], true); + if (!$modelTableInfo) { + $modelTableType = 'name'; + $modelTableName = $prefix . $modelName; + $modelTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$modelTableName}'", [], true); + if (!$modelTableInfo) { + throw new Exception("table not found"); + } + } + $modelTableInfo = $modelTableInfo[0]; + + $relations = []; + //检查关联表 + if ($relation) { + $relationArr = $relation; + $relations = []; + + foreach ($relationArr as $index => $relationTable) { + $relationName = stripos($relationTable, $prefix) === 0 ? substr($relationTable, strlen($prefix)) : $relationTable; + $relationTableType = 'table'; + $relationTableTypeName = $relationTableName = $relationName; + $relationTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], true); + if (!$relationTableInfo) { + $relationTableType = 'name'; + $relationTableName = $prefix . $relationName; + $relationTableInfo = $dbconnect->query("SHOW TABLE STATUS LIKE '{$relationTableName}'", [], true); + if (!$relationTableInfo) { + throw new Exception("relation table not found"); + } + } + $relationTableInfo = $relationTableInfo[0]; + $relationModel = isset($relationModels[$index]) ? $relationModels[$index] : ''; + + list($relationNamespace, $relationName, $relationFile) = $this->getModelData($modelModuleName, $relationModel, $relationName); + + $relations[] = [ + //关联表基础名 + 'relationName' => $relationName, + //关联表类命名空间 + 'relationNamespace' => $relationNamespace, + //关联模型名 + 'relationModel' => $relationModel, + //关联文件 + 'relationFile' => $relationFile, + //关联表名称 + 'relationTableName' => $relationTableName, + //关联表信息 + 'relationTableInfo' => $relationTableInfo, + //关联模型表类型(name或table) + 'relationTableType' => $relationTableType, + //关联模型表类型名称 + 'relationTableTypeName' => $relationTableTypeName, + //关联模式 + 'relationFields' => isset($relationFields[$index]) ? explode(',', $relationFields[$index]) : [], + //关联模式 + 'relationMode' => isset($relationMode[$index]) ? $relationMode[$index] : 'belongsto', + //关联模型控制器 + 'relationController' => isset($relationController[$index]) ? $relationController[$index] : '', + //关联表外键 + 'relationForeignKey' => isset($relationForeignKey[$index]) ? $relationForeignKey[$index] : '', + //关联表主键 + 'relationPrimaryKey' => isset($relationPrimaryKey[$index]) ? $relationPrimaryKey[$index] : '', + ]; + } + } + + //根据表名匹配对应的Fontawesome图标 + $iconPath = ROOT_PATH . str_replace('/', DS, '/public/assets/libs/font-awesome/less/variables.less'); + $iconName = is_file($iconPath) && stripos(file_get_contents($iconPath), '@fa-var-' . $table . ':') ? 'fa fa-' . $table : 'fa fa-circle-o'; + + //控制器 + list($controllerNamespace, $controllerName, $controllerFile, $controllerArr) = $this->getControllerData($moduleName, $controller, $table); + //模型 + list($modelNamespace, $modelName, $modelFile, $modelArr) = $this->getModelData($modelModuleName, $model, $table); + //验证器 + list($validateNamespace, $validateName, $validateFile, $validateArr) = $this->getValidateData($validateModuleName, $validate, $table); + + //处理基础文件名,取消所有下划线并转换为小写 + $baseNameArr = $controllerArr; + $baseFileName = Loader::parseName(array_pop($baseNameArr), 0); + array_push($baseNameArr, $baseFileName); + $controllerBaseName = strtolower(implode(DS, $baseNameArr)); + //$controllerUrl = strtolower(implode('/', $baseNameArr)); + $controllerUrl = $this->getControllerUrl($moduleName, $baseNameArr); + + //视图文件 + $viewArr = $controllerArr; + $lastValue = array_pop($viewArr); + $viewArr[] = Loader::parseName($lastValue, 0); + array_unshift($viewArr, 'view'); + $viewDir = $adminPath . strtolower(implode(DS, $viewArr)) . DS; + + //最终将生成的文件路径 + $javascriptFile = ROOT_PATH . 'public' . DS . 'assets' . DS . 'js' . DS . 'backend' . DS . $controllerBaseName . '.js'; + $addFile = $viewDir . 'add.html'; + $editFile = $viewDir . 'edit.html'; + $indexFile = $viewDir . 'index.html'; + $recyclebinFile = $viewDir . 'recyclebin.html'; + $langFile = $adminPath . 'lang' . DS . Lang::detect() . DS . $controllerBaseName . '.php'; + + //是否为删除模式 + $delete = $input->getOption('delete'); + if ($delete) { + $readyFiles = [$controllerFile, $modelFile, $validateFile, $addFile, $editFile, $indexFile, $recyclebinFile, $langFile, $javascriptFile]; + foreach ($readyFiles as $k => $v) { + $output->warning($v); + } + if (!$force) { + $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } + } + foreach ($readyFiles as $k => $v) { + if (file_exists($v)) { + unlink($v); + } + //删除空文件夹 + switch ($v) { + case $modelFile: + $this->removeEmptyBaseDir($v, $modelArr); + break; + case $validateFile: + $this->removeEmptyBaseDir($v, $validateArr); + break; + case $addFile: + case $editFile: + case $indexFile: + case $recyclebinFile: + $this->removeEmptyBaseDir($v, $viewArr); + break; + default: + $this->removeEmptyBaseDir($v, $controllerArr); + } + } + + //继续删除菜单 + if ($menu) { + exec("php think menu -c {$controllerUrl} -d 1 -f 1"); + } + + $output->info("Delete Successed"); + return; + } + + //非覆盖模式时如果存在控制器文件则报错 + if (is_file($controllerFile) && !$force) { + throw new Exception("controller already exists!\nIf you need to rebuild again, use the parameter --force=true "); + } + + //非覆盖模式时如果存在模型文件则报错 + if (is_file($modelFile) && !$force) { + throw new Exception("model already exists!\nIf you need to rebuild again, use the parameter --force=true "); + } + + //非覆盖模式时如果存在验证文件则报错 + if (is_file($validateFile) && !$force) { + throw new Exception("validate already exists!\nIf you need to rebuild again, use the parameter --force=true "); + } + + require $adminPath . 'common.php'; + + //从数据库中获取表字段信息 + $sql = "SELECT * FROM `information_schema`.`columns` " + . "WHERE TABLE_SCHEMA = ? AND table_name = ? " + . "ORDER BY ORDINAL_POSITION"; + //加载主表的列 + $columnList = $dbconnect->query($sql, [$dbname, $modelTableName]); + $fieldArr = []; + foreach ($columnList as $k => $v) { + $fieldArr[] = $v['COLUMN_NAME']; + } + + // 加载关联表的列 + foreach ($relations as $index => &$relation) { + $relationColumnList = $dbconnect->query($sql, [$dbname, $relation['relationTableName']]); + + $relationFieldList = []; + foreach ($relationColumnList as $k => $v) { + $relationFieldList[] = $v['COLUMN_NAME']; + } + if (!$relation['relationPrimaryKey']) { + foreach ($relationColumnList as $k => $v) { + if ($v['COLUMN_KEY'] == 'PRI') { + $relation['relationPrimaryKey'] = $v['COLUMN_NAME']; + break; + } + } + } + // 如果主键为空 + if (!$relation['relationPrimaryKey']) { + throw new Exception('Relation Primary key not found!'); + } + // 如果主键不在表字段中 + if (!in_array($relation['relationPrimaryKey'], $relationFieldList)) { + throw new Exception('Relation Primary key not found in table!'); + } + $relation['relationColumnList'] = $relationColumnList; + $relation['relationFieldList'] = $relationFieldList; + } + unset($relation); + + $addList = []; + $editList = []; + $javascriptList = []; + $langList = []; + $operateButtonList = []; + $field = 'id'; + $order = 'id'; + $priDefined = false; + $priKeyArr = []; + $relationPrimaryKey = ''; + foreach ($columnList as $k => $v) { + if ($v['COLUMN_KEY'] == 'PRI') { + $priKeyArr[] = $v['COLUMN_NAME']; + } + } + if (!$priKeyArr) { + throw new Exception('Primary key not found!'); + } + if (count($priKeyArr) > 1) { + throw new Exception('Multiple primary key not support!'); + } + $priKey = reset($priKeyArr); + + $order = $priKey; + + //如果是关联模型 + foreach ($relations as $index => &$relation) { + if ($relation['relationMode'] == 'hasone') { + $relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : $table . "_id"; + $relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey; + + if (!in_array($relationForeignKey, $relation['relationFieldList'])) { + throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationForeignKey . ']'); + } + if (!in_array($relationPrimaryKey, $fieldArr)) { + throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationPrimaryKey . ']'); + } + } elseif ($relation['relationMode'] == 'belongsto') { + $relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : Loader::parseName($relation['relationName']) . "_id"; + $relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $relation['relationPriKey']; + if (!in_array($relationForeignKey, $fieldArr)) { + throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationForeignKey . ']'); + } + if (!in_array($relationPrimaryKey, $relation['relationFieldList'])) { + throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationPrimaryKey . ']'); + } + } elseif ($relation['relationMode'] == 'hasmany') { + $relationForeignKey = $relation['relationForeignKey'] ? $relation['relationForeignKey'] : $table . "_id"; + $relationPrimaryKey = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey; + if (!in_array($relationForeignKey, $relation['relationFieldList'])) { + throw new Exception('relation table [' . $relation['relationTableName'] . '] must be contain field [' . $relationForeignKey . ']'); + } + if (!in_array($relationPrimaryKey, $fieldArr)) { + throw new Exception('table [' . $modelTableName . '] must be contain field [' . $relationPrimaryKey . ']'); + } + $relation['relationColumnList'] = []; + $relation['relationFieldList'] = []; + } + $relation['relationForeignKey'] = $relationForeignKey; + $relation['relationPrimaryKey'] = $relationPrimaryKey; + $relation['relationClassName'] = $modelNamespace != $relation['relationNamespace'] ? $relation['relationNamespace'] . '\\' . $relation['relationName'] : $relation['relationName']; + } + unset($relation); + + try { + Form::setEscapeHtml(false); + $setAttrArr = []; + $getAttrArr = []; + $getEnumArr = []; + $appendAttrList = []; + $controllerAssignList = []; + $headingHtml = '{:build_heading()}'; + $controllerImport = ''; + $importHtml = ''; + $recyclebinHtml = ''; + + if ($import) { + $controllerImport = $this->getReplacedStub('mixins/import', []); + $importHtml = ' {:__(\'Import\')}'; + } + + //循环所有字段,开始构造视图的HTML和JS信息 + foreach ($columnList as $k => $v) { + $field = $v['COLUMN_NAME']; + $itemArr = []; + // 这里构建Enum和Set类型的列表数据 + if (in_array($v['DATA_TYPE'], ['enum', 'set', 'tinyint']) || $this->headingFilterField == $field) { + if ($v['DATA_TYPE'] !== 'tinyint') { + $itemArr = substr($v['COLUMN_TYPE'], strlen($v['DATA_TYPE']) + 1, -1); + $itemArr = explode(',', str_replace("'", '', $itemArr)); + } + $itemArr = $this->getItemArray($itemArr, $field, $v['COLUMN_COMMENT']); + //如果类型为tinyint且有使用备注数据 + if ($itemArr && !in_array($v['DATA_TYPE'], ['enum', 'set'])) { + $v['DATA_TYPE'] = 'enum'; + } + } + // 语言列表 + if ($v['COLUMN_COMMENT'] != '') { + $langList[] = $this->getLangItem($field, $v['COLUMN_COMMENT']); + } + $inputType = ''; + //保留字段不能修改和添加 + if ($v['COLUMN_KEY'] != 'PRI' && !in_array($field, $this->reservedField) && !in_array($field, $this->ignoreFields)) { + $inputType = $this->getFieldType($v); + + // 如果是number类型时增加一个步长 + $step = $inputType == 'number' && $v['NUMERIC_SCALE'] > 0 ? "0." . str_repeat(0, $v['NUMERIC_SCALE'] - 1) . "1" : 0; + + $attrArr = ['id' => "c-{$field}"]; + $cssClassArr = ['form-control']; + $fieldName = "row[{$field}]"; + $defaultValue = $v['COLUMN_DEFAULT']; + $editValue = "{\$row.{$field}|htmlentities}"; + // 如果默认值非null,则是一个必选项 + if ($v['IS_NULLABLE'] == 'NO') { + $attrArr['data-rule'] = 'required'; + } + + //如果字段类型为无符号型,则设置 + if (stripos($v['COLUMN_TYPE'], 'unsigned') !== false) { + $attrArr['min'] = 0; + } + + if ($inputType == 'select') { + $cssClassArr[] = 'selectpicker'; + $attrArr['class'] = implode(' ', $cssClassArr); + if ($v['DATA_TYPE'] == 'set') { + $attrArr['multiple'] = ''; + $fieldName .= "[]"; + } + $attrArr['name'] = $fieldName; + + $this->getEnum($getEnumArr, $controllerAssignList, $field, $itemArr, $v['DATA_TYPE'] == 'set' ? 'multiple' : 'select'); + + $itemArr = $this->getLangArray($itemArr, false); + //添加一个获取器 + $this->getAttr($getAttrArr, $field, $v['DATA_TYPE'] == 'set' ? 'multiple' : 'select'); + if ($v['DATA_TYPE'] == 'set') { + $this->setAttr($setAttrArr, $field, $inputType); + } + $this->appendAttr($appendAttrList, $field); + $formAddElement = $this->getReplacedStub('html/select', ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => $defaultValue]); + $formEditElement = $this->getReplacedStub('html/select', ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => "\$row.{$field}"]); + } elseif ($inputType == 'datetime') { + $cssClassArr[] = 'datetimepicker'; + $attrArr['class'] = implode(' ', $cssClassArr); + $format = "YYYY-MM-DD HH:mm:ss"; + $phpFormat = "Y-m-d H:i:s"; + $fieldFunc = ''; + switch ($v['DATA_TYPE']) { + case 'year': + $format = "YYYY"; + $phpFormat = 'Y'; + break; + case 'date': + $format = "YYYY-MM-DD"; + $phpFormat = 'Y-m-d'; + break; + case 'time': + $format = "HH:mm:ss"; + $phpFormat = 'H:i:s'; + break; + case 'timestamp': + $fieldFunc = 'datetime'; + // no break + case 'datetime': + $format = "YYYY-MM-DD HH:mm:ss"; + $phpFormat = 'Y-m-d H:i:s'; + break; + default: + $fieldFunc = 'datetime'; + $this->getAttr($getAttrArr, $field, $inputType); + $this->setAttr($setAttrArr, $field, $inputType); + $this->appendAttr($appendAttrList, $field); + break; + } + $defaultDateTime = "{:date('{$phpFormat}')}"; + $attrArr['data-date-format'] = $format; + $attrArr['data-use-current'] = "true"; + $formAddElement = Form::text($fieldName, $defaultDateTime, $attrArr); + $formEditElement = Form::text($fieldName, ($fieldFunc ? "{:\$row.{$field}?{$fieldFunc}(\$row.{$field}):''}" : "{\$row.{$field}{$fieldFunc}}"), $attrArr); + } elseif ($inputType == 'datetimerange') { + $cssClassArr[] = 'datetimerange'; + $attrArr['class'] = implode(' ', $cssClassArr); + $attrArr['data-locale'] = '{"format":"YYYY-MM-DD HH:mm:ss"}'; + $fieldFunc = ''; + $defaultDateTime = ""; + $formAddElement = Form::text($fieldName, $defaultDateTime, $attrArr); + $formEditElement = Form::text($fieldName, $editValue, $attrArr); + } elseif ($inputType == 'checkbox' || $inputType == 'radio') { + unset($attrArr['data-rule']); + $fieldName = $inputType == 'checkbox' ? $fieldName .= "[]" : $fieldName; + $attrArr['name'] = "row[{$fieldName}]"; + + $this->getEnum($getEnumArr, $controllerAssignList, $field, $itemArr, $inputType); + $itemArr = $this->getLangArray($itemArr, false); + //添加一个获取器 + $this->getAttr($getAttrArr, $field, $inputType); + if ($inputType == 'checkbox') { + $this->setAttr($setAttrArr, $field, $inputType); + } + $this->appendAttr($appendAttrList, $field); + $defaultValue = $inputType == 'radio' && !$defaultValue ? key($itemArr) : $defaultValue; + + $formAddElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => $defaultValue]); + $formEditElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldList' => $this->getFieldListName($field), 'attrStr' => Form::attributes($attrArr), 'selectedValue' => "\$row.{$field}"]); + } elseif ($inputType == 'textarea' && !$this->isMatchSuffix($field, $this->selectpagesSuffix) && !$this->isMatchSuffix($field, $this->imageField)) { + $cssClassArr[] = $this->isMatchSuffix($field, $this->editorSuffix) ? $this->editorClass : ''; + $attrArr['class'] = implode(' ', $cssClassArr); + $attrArr['rows'] = 5; + $formAddElement = Form::textarea($fieldName, $defaultValue, $attrArr); + $formEditElement = Form::textarea($fieldName, $editValue, $attrArr); + } elseif ($inputType == 'switch') { + unset($attrArr['data-rule']); + if ($defaultValue === '1' || $defaultValue === 'Y') { + $yes = $defaultValue; + $no = $defaultValue === '1' ? '0' : 'N'; + } else { + $no = $defaultValue; + $yes = $defaultValue === '0' ? '1' : 'Y'; + } + if (!$itemArr) { + $itemArr = [$yes => 'Yes', $no => 'No']; + } + $stateNoClass = 'fa-flip-horizontal text-gray'; + $formAddElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldYes' => $yes, 'fieldNo' => $no, 'attrStr' => Form::attributes($attrArr), 'fieldValue' => $defaultValue, 'fieldSwitchClass' => $defaultValue == $no ? $stateNoClass : '']); + $formEditElement = $this->getReplacedStub('html/' . $inputType, ['field' => $field, 'fieldName' => $fieldName, 'fieldYes' => $yes, 'fieldNo' => $no, 'attrStr' => Form::attributes($attrArr), 'fieldValue' => "{\$row.{$field}}", 'fieldSwitchClass' => "{eq name=\"\$row.{$field}\" value=\"{$no}\"}fa-flip-horizontal text-gray{/eq}"]); + } elseif ($inputType == 'citypicker') { + $attrArr['class'] = implode(' ', $cssClassArr); + $attrArr['data-toggle'] = "city-picker"; + $formAddElement = sprintf("
%s
", Form::input('text', $fieldName, $defaultValue, $attrArr)); + $formEditElement = sprintf("
%s
", Form::input('text', $fieldName, $editValue, $attrArr)); + } elseif ($inputType == 'tagsinput') { + $attrArr['class'] = implode(' ', $cssClassArr); + $attrArr['data-role'] = "tagsinput"; + $formAddElement = Form::input('text', $fieldName, $defaultValue, $attrArr); + $formEditElement = Form::input('text', $fieldName, $editValue, $attrArr); + } elseif ($inputType == 'fieldlist') { + $itemArr = $this->getItemArray($itemArr, $field, $v['COLUMN_COMMENT']); + $templateName = !isset($itemArr['key']) && !isset($itemArr['value']) && count($itemArr) > 0 ? 'fieldlist-template' : 'fieldlist'; + $itemKey = isset($itemArr['key']) ? ucfirst($itemArr['key']) : 'Key'; + $itemValue = isset($itemArr['value']) ? ucfirst($itemArr['value']) : 'Value'; + $theadListArr = $tbodyListArr = []; + foreach ($itemArr as $index => $item) { + $theadListArr[] = "{:__('" . $item . "')}"; + $tbodyListArr[] = ''; + } + $colspan = count($theadListArr) + 1; + $commonFields = ['field' => $field, 'fieldName' => $fieldName, 'itemKey' => $itemKey, 'itemValue' => $itemValue, 'theadList' => implode("\n", $theadListArr), 'tbodyList' => implode("\n", $tbodyListArr), 'colspan' => $colspan]; + $formAddElement = $this->getReplacedStub('html/' . $templateName, array_merge($commonFields, ['fieldValue' => $defaultValue])); + $formEditElement = $this->getReplacedStub('html/' . $templateName, array_merge($commonFields, ['fieldValue' => $editValue])); + } else { + $search = $replace = ''; + //特殊字段为关联搜索 + if ($this->isMatchSuffix($field, $this->selectpageSuffix)) { + $inputType = 'text'; + $defaultValue = ''; + $attrArr['data-rule'] = 'required'; + $cssClassArr[] = 'selectpage'; + $selectpageTable = substr($field, 0, strripos($field, '_')); + $selectpageField = ''; + $selectpageController = str_replace('_', '/', $selectpageTable); + $attrArr['data-source'] = $selectpageController . "/index"; + //如果是类型表需要特殊处理下 + if ($selectpageController == 'category') { + $attrArr['data-source'] = 'category/selectpage'; + $attrArr['data-params'] = '##replacetext##'; + $search = '"##replacetext##"'; + $replace = '\'{"custom[type]":"' . $table . '"}\''; + } elseif ($selectpageController == 'admin') { + $attrArr['data-source'] = 'auth/admin/selectpage'; + } elseif ($selectpageController == 'user') { + $attrArr['data-source'] = 'user/user/index'; + $attrArr['data-field'] = 'nickname'; + } + if ($this->isMatchSuffix($field, $this->selectpagesSuffix)) { + $attrArr['data-multiple'] = 'true'; + } + + $tableInfo = null; + try { + $tableInfo = \think\Db::name($selectpageTable)->getTableInfo(); + if (isset($tableInfo['fields'])) { + foreach ($tableInfo['fields'] as $m => $n) { + if (in_array($n, ['nickname', 'title', 'name'])) { + $selectpageField = $n; + break; + } + } + } + } catch (\Exception $e) { + + } + if (!$selectpageField) { + foreach ($this->fieldSelectpageMap as $m => $n) { + if (in_array($field, $n)) { + $attrArr['data-field'] = $m; + break; + } + } + } + } + //因为有自动完成可输入其它内容 + $step = array_intersect($cssClassArr, ['selectpage']) ? 0 : $step; + $attrArr['class'] = implode(' ', $cssClassArr); + $isUpload = false; + if ($this->isMatchSuffix($field, array_merge($this->imageField, $this->fileField))) { + $isUpload = true; + } + //如果是步长则加上步长 + if ($step) { + $attrArr['step'] = $step; + } + //如果是图片加上个size + if ($isUpload) { + $attrArr['size'] = 50; + } + + $formAddElement = Form::input($inputType, $fieldName, $defaultValue, $attrArr); + $formEditElement = Form::input($inputType, $fieldName, $editValue, $attrArr); + if ($search && $replace) { + $formAddElement = str_replace($search, $replace, $formAddElement); + $formEditElement = str_replace($search, $replace, $formEditElement); + } + //如果是图片或文件 + if ($isUpload) { + $formAddElement = $this->getImageUpload($field, $formAddElement); + $formEditElement = $this->getImageUpload($field, $formEditElement); + } + } + //构造添加和编辑HTML信息 + $addList[] = $this->getFormGroup($field, $formAddElement); + $editList[] = $this->getFormGroup($field, $formEditElement); + } + + //过滤text类型字段 + if ($v['DATA_TYPE'] != 'text' && $inputType != 'fieldlist') { + //主键 + if ($v['COLUMN_KEY'] == 'PRI' && !$priDefined) { + $priDefined = true; + $javascriptList[] = "{checkbox: true}"; + } + if ($this->deleteTimeField == $field) { + $recyclebinHtml = $this->getReplacedStub('html/recyclebin-html', ['controllerUrl' => $controllerUrl]); + continue; + } + if (!$fields || in_array($field, explode(',', $fields))) { + //构造JS列信息 + $javascriptList[] = $this->getJsColumn($field, $v['DATA_TYPE'], $inputType && in_array($inputType, ['select', 'checkbox', 'radio']) ? '_text' : '', $itemArr); + } + if ($this->headingFilterField && $this->headingFilterField == $field && $itemArr) { + $headingHtml = $this->getReplacedStub('html/heading-html', ['field' => $field, 'fieldName' => Loader::parseName($field, 1, false)]); + } + //排序方式,如果有指定排序字段,否则按主键排序 + $order = $field == $this->sortField ? $this->sortField : $order; + } + } + + //循环关联表,追加语言包和JS列 + foreach ($relations as $index => $relation) { + if ($relation['relationMode'] == 'hasmany') { + $relationFieldText = ucfirst(strtolower($relation['relationName'])) . ' List'; + // 语言列表 + if ($relation['relationTableInfo']['Comment']) { + $langList[] = $this->getLangItem($relationFieldText, rtrim($relation['relationTableInfo']['Comment'], "表") . "列表"); + } + + $relationTableName = $relation['relationTableName']; + $relationTableName = stripos($relationTableName, $prefix) === 0 ? substr($relationTableName, strlen($prefix)) : $relationTableName; + + list($realtionControllerNamespace, $realtionControllerName, $realtionControllerFile, $realtionControllerArr) = $this->getControllerData($moduleName, $relation['relationController'], $relationTableName); + $realtionControllerArr = array_map("strtolower", $realtionControllerArr); + if (count($realtionControllerArr) > 1) { + $realtionControllerArr = [implode('.', $realtionControllerArr)]; + } + $realtionControllerArr[] = 'index'; + $realtionControllerArr[] = $relation['relationForeignKey'] . '/{ids}'; + $relationControllerUrl = implode('/', $realtionControllerArr); + + //构造JS列信息 + $operateButtonList[] = "{name: 'addtabs',title: __('{$relationFieldText}'),text: __('{$relationFieldText}'),classname: 'btn btn-xs btn-info btn-dialog',icon: 'fa fa-list',url: '" . $relationControllerUrl . "'}"; + //echo "php think crud -t {$relation['relationTableName']} -c {$relation['relationController']} -m {$relation['relationModel']} -i " . implode(',', $relation['relationFields']); + //不存在关联表控制器的情况下才进行生成 + if (!is_file($realtionControllerFile)) { + exec("php think crud -t {$relation['relationTableName']} -c {$relation['relationController']} -m {$relation['relationModel']} -i " . implode(',', $relation['relationFields'])); + } + } + foreach ($relation['relationColumnList'] as $k => $v) { + // 不显示的字段直接过滤掉 + if ($relation['relationFields'] && !in_array($v['COLUMN_NAME'], $relation['relationFields'])) { + continue; + } + + $relationField = strtolower($relation['relationName']) . "." . $v['COLUMN_NAME']; + // 语言列表 + if ($v['COLUMN_COMMENT'] != '') { + $langList[] = $this->getLangItem($relationField, $v['COLUMN_COMMENT']); + } + + //过滤text类型字段 + if ($v['DATA_TYPE'] != 'text') { + //构造JS列信息 + $javascriptList[] = $this->getJsColumn($relationField, $v['DATA_TYPE']); + } + } + } + + //JS最后一列加上操作列 + $javascriptList[] = str_repeat(" ", 24) . "{field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, " . ($operateButtonList ? "buttons: [" . implode(',', $operateButtonList) . "], " : "") . "formatter: Table.api.formatter.operate}"; + $addList = implode("\n", array_filter($addList)); + $editList = implode("\n", array_filter($editList)); + $javascriptList = implode(",\n", array_filter($javascriptList)); + $langList = implode(",\n", array_filter($langList)); + //数组等号对齐 + $langList = array_filter(explode(",\n", $langList . ",\n")); + foreach ($langList as &$line) { + if (preg_match("/^\s+'([^']+)'\s*=>\s*'([^']+)'\s*/is", $line, $matches)) { + $line = " '{$matches[1]}'" . str_pad('=>', ($this->fieldMaxLen - strlen($matches[1]) + 3), ' ', STR_PAD_LEFT) . " '{$matches[2]}'"; + } + } + unset($line); + $langList = implode(",\n", array_filter($langList)); + $fixedcolumns = count($columnList) >= 10 ? 1 : $fixedcolumns; + + $fixedColumnsJs = ''; + if (is_numeric($fixedcolumns) && $fixedcolumns) { + $fixedColumnsJs = "\n" . str_repeat(" ", 16) . "fixedColumns: true,\n" . str_repeat(" ", 16) . ($fixedcolumns < 0 ? "fixedNumber" : "fixedRightNumber") . ": " . $fixedcolumns . ","; + } + + //表注释 + $tableComment = $modelTableInfo['Comment']; + $tableComment = mb_substr($tableComment, -1) == '表' ? mb_substr($tableComment, 0, -1) . '管理' : $tableComment; + + $modelInit = ''; + if ($priKey != $order) { + $modelInit = $this->getReplacedStub('mixins' . DS . 'modelinit', ['order' => $order]); + } + + $data = [ + 'modelConnection' => $db == 'database' ? '' : "protected \$connection = '{$db}';", + 'controllerNamespace' => $controllerNamespace, + 'modelNamespace' => $modelNamespace, + 'validateNamespace' => $validateNamespace, + 'controllerUrl' => $controllerUrl, + 'controllerName' => $controllerName, + 'controllerAssignList' => implode("\n", $controllerAssignList), + 'modelName' => $modelName, + 'modelTableName' => $modelTableName, + 'modelTableType' => $modelTableType, + 'modelTableTypeName' => $modelTableTypeName, + 'validateName' => $validateName, + 'tableComment' => $tableComment, + 'iconName' => $iconName, + 'pk' => $priKey, + 'order' => $order, + 'fixedColumnsJs' => $fixedColumnsJs, + 'table' => $table, + 'tableName' => $modelTableName, + 'addList' => $addList, + 'editList' => $editList, + 'javascriptList' => $javascriptList, + 'langList' => $langList, + 'softDeleteClassPath' => in_array($this->deleteTimeField, $fieldArr) ? "use traits\model\SoftDelete;" : '', + 'softDelete' => in_array($this->deleteTimeField, $fieldArr) ? "use SoftDelete;" : '', + 'modelAutoWriteTimestamp' => in_array($this->createTimeField, $fieldArr) || in_array($this->updateTimeField, $fieldArr) ? "'integer'" : 'false', + 'createTime' => in_array($this->createTimeField, $fieldArr) ? "'{$this->createTimeField}'" : 'false', + 'updateTime' => in_array($this->updateTimeField, $fieldArr) ? "'{$this->updateTimeField}'" : 'false', + 'deleteTime' => in_array($this->deleteTimeField, $fieldArr) ? "'{$this->deleteTimeField}'" : 'false', + 'relationSearch' => $relations ? 'true' : 'false', + 'relationWithList' => '', + 'relationMethodList' => '', + 'controllerImport' => $controllerImport, + 'controllerIndex' => '', + 'recyclebinJs' => '', + 'headingHtml' => $headingHtml, + 'importHtml' => $importHtml, + 'recyclebinHtml' => $recyclebinHtml, + 'visibleFieldList' => $fields ? "\$row->visible(['" . implode("','", array_filter(in_array($priKey, explode(',', $fields)) ? explode(',', $fields) : explode(',', $priKey . ',' . $fields))) . "']);" : '', + 'appendAttrList' => implode(",\n", $appendAttrList), + 'getEnumList' => implode("\n\n", $getEnumArr), + 'getAttrList' => implode("\n\n", $getAttrArr), + 'setAttrList' => implode("\n\n", $setAttrArr), + 'modelInit' => $modelInit, + ]; + + //如果使用关联模型 + if ($relations) { + $relationWithList = $relationMethodList = $relationVisibleFieldList = []; + $relationKeyArr = ['hasone' => 'hasOne', 'belongsto' => 'belongsTo', 'hasmany' => 'hasMany']; + foreach ($relations as $index => $relation) { + //需要构造关联的方法 + $relation['relationMethod'] = strtolower($relation['relationName']); + + //关联的模式 + $relation['relationMode'] = strtolower($relation['relationMode']); + $relation['relationMode'] = array_key_exists($relation['relationMode'], $relationKeyArr) ? $relationKeyArr[$relation['relationMode']] : ''; + + //关联字段 + $relation['relationPrimaryKey'] = $relation['relationPrimaryKey'] ? $relation['relationPrimaryKey'] : $priKey; + + //构造关联模型的方法 + $relationMethodList[] = $this->getReplacedStub('mixins' . DS . 'modelrelationmethod' . ($relation['relationMode'] == 'hasMany' ? '-hasmany' : ''), $relation); + + if ($relation['relationMode'] == 'hasMany') { + continue; + } + + //预载入的方法 + $relationWithList[] = $relation['relationMethod']; + + unset($relation['relationColumnList'], $relation['relationFieldList'], $relation['relationTableInfo']); + + //如果设置了显示主表字段,则必须显式将关联表字段显示 + if ($fields) { + $relationVisibleFieldList[] = "\$row->visible(['{$relation['relationMethod']}']);"; + } + + //显示的字段 + if ($relation['relationFields']) { + $relationVisibleFieldList[] = "\$row->getRelation('" . $relation['relationMethod'] . "')->visible(['" . implode("','", $relation['relationFields']) . "']);"; + } + } + + $data['relationWithList'] = "->with(['" . implode("','", $relationWithList) . "'])"; + $data['relationMethodList'] = implode("\n\n", $relationMethodList); + $data['relationVisibleFieldList'] = implode("\n\t\t\t\t", $relationVisibleFieldList); + + if ($relationWithList) { + //需要重写index方法 + $data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data); + } + } elseif ($fields) { + $data = array_merge($data, ['relationWithList' => '', 'relationMethodList' => '', 'relationVisibleFieldList' => '']); + //需要重写index方法 + $data['controllerIndex'] = $this->getReplacedStub('controllerindex', $data); + } + + // 生成控制器文件 + $this->writeToFile('controller', $data, $controllerFile); + // 生成模型文件 + $this->writeToFile('model', $data, $modelFile); + + if ($relations) { + foreach ($relations as $i => $relation) { + $relation['modelNamespace'] = $relation['relationNamespace']; + if (!is_file($relation['relationFile'])) { + // 生成关联模型文件 + $this->writeToFile('relationmodel', $relation, $relation['relationFile']); + } + } + } + // 生成验证文件 + $this->writeToFile('validate', $data, $validateFile); + // 生成视图文件 + $this->writeToFile('add', $data, $addFile); + $this->writeToFile('edit', $data, $editFile); + $this->writeToFile('index', $data, $indexFile); + if ($recyclebinHtml) { + $this->writeToFile('recyclebin', $data, $recyclebinFile); + $recyclebinTitle = in_array('title', $fieldArr) ? 'title' : (in_array('name', $fieldArr) ? 'name' : ''); + $recyclebinTitleJs = $recyclebinTitle ? "\n {field: '{$recyclebinTitle}', title: __('" . (ucfirst($recyclebinTitle)) . "'), align: 'left'}," : ''; + $data['recyclebinJs'] = $this->getReplacedStub('mixins/recyclebinjs', ['deleteTimeField' => $this->deleteTimeField, 'recyclebinTitleJs' => $recyclebinTitleJs, 'controllerUrl' => $controllerUrl]); + } + // 生成JS文件 + $this->writeToFile('javascript', $data, $javascriptFile); + // 生成语言文件 + $this->writeToFile('lang', $data, $langFile); + } catch (ErrorException $e) { + throw new Exception("Code: " . $e->getCode() . "\nLine: " . $e->getLine() . "\nMessage: " . $e->getMessage() . "\nFile: " . $e->getFile()); + } + + //继续生成菜单 + if ($menu) { + exec("php think menu -c {$controllerUrl}"); + } + + $output->info("Build Successed"); + } + + protected function getEnum(&$getEnum, &$controllerAssignList, $field, $itemArr = '', $inputType = '') + { + if (!in_array($inputType, ['datetime', 'select', 'multiple', 'checkbox', 'radio'])) { + return; + } + $fieldList = $this->getFieldListName($field); + $methodName = 'get' . ucfirst($fieldList); + foreach ($itemArr as $k => &$v) { + $v = "__('" . mb_ucfirst($v) . "')"; + } + unset($v); + $itemString = $this->getArrayString($itemArr); + $getEnum[] = <<view->assign("{$fieldList}", \$this->model->{$methodName}()); +EOD; + } + + protected function getAttr(&$getAttr, $field, $inputType = '') + { + if (!in_array($inputType, ['datetime', 'select', 'multiple', 'checkbox', 'radio'])) { + return; + } + $attrField = ucfirst($this->getCamelizeName($field)); + $getAttr[] = $this->getReplacedStub("mixins" . DS . $inputType, ['field' => $field, 'methodName' => "get{$attrField}TextAttr", 'listMethodName' => "get{$attrField}List"]); + } + + protected function setAttr(&$setAttr, $field, $inputType = '') + { + if (!in_array($inputType, ['datetime', 'checkbox', 'select'])) { + return; + } + $attrField = ucfirst($this->getCamelizeName($field)); + if ($inputType == 'datetime') { + $return = << 1) { + $parentDir = dirname($parseFile); + for ($i = 0; $i < count($parseArr); $i++) { + try { + $iterator = new \FilesystemIterator($parentDir); + $isDirEmpty = !$iterator->valid(); + if ($isDirEmpty) { + rmdir($parentDir); + $parentDir = dirname($parentDir); + } else { + return true; + } + } catch (\UnexpectedValueException $e) { + return false; + } + } + } + return true; + } + + /** + * 获取控制器URL + * @param string $moduleName + * @param array $baseNameArr + * @return string + */ + protected function getControllerUrl($moduleName, $baseNameArr) + { + for ($i = 0; $i < count($baseNameArr) - 1; $i++) { + $temp = array_slice($baseNameArr, 0, $i + 1); + $temp[$i] = ucfirst($temp[$i]); + $controllerFile = APP_PATH . $moduleName . DS . 'controller' . DS . implode(DS, $temp) . '.php'; + //检测父级目录同名控制器是否存在,存在则变更URL格式 + if (is_file($controllerFile)) { + $baseNameArr = [implode('.', $baseNameArr)]; + break; + } + } + $controllerUrl = strtolower(implode('/', $baseNameArr)); + return $controllerUrl; + } + + /** + * 获取控制器相关信息 + * @param $module + * @param $controller + * @param $table + * @return array + */ + protected function getControllerData($module, $controller, $table) + { + return $this->getParseNameData($module, $controller, $table, 'controller'); + } + + /** + * 获取模型相关信息 + * @param $module + * @param $model + * @param $table + * @return array + */ + protected function getModelData($module, $model, $table) + { + return $this->getParseNameData($module, $model, $table, 'model'); + } + + /** + * 获取验证器相关信息 + * @param $module + * @param $validate + * @param $table + * @return array + */ + protected function getValidateData($module, $validate, $table) + { + return $this->getParseNameData($module, $validate, $table, 'validate'); + } + + /** + * 获取已解析相关信息 + * @param string $module 模块名称 + * @param string $name 自定义名称 + * @param string $table 数据表名 + * @param string $type 解析类型,本例中为controller、model、validate + * @return array + */ + protected function getParseNameData($module, $name, $table, $type) + { + $arr = []; + if (!$name) { + $parseName = Loader::parseName($table, 1); + $name = str_replace('_', '/', $table); + } + + $name = str_replace(['.', '/', '\\'], '/', $name); + $arr = explode('/', $name); + $parseName = ucfirst(array_pop($arr)); + $parseArr = $arr; + array_push($parseArr, $parseName); + //类名不能为内部关键字 + if (in_array(strtolower($parseName), $this->internalKeywords)) { + throw new Exception('Unable to use internal variable:' . $parseName); + } + $appNamespace = Config::get('app_namespace'); + $parseNamespace = "{$appNamespace}\\{$module}\\{$type}" . ($arr ? "\\" . implode("\\", $arr) : ""); + $moduleDir = APP_PATH . $module . DS; + $parseFile = $moduleDir . $type . DS . ($arr ? implode(DS, $arr) . DS : '') . $parseName . '.php'; + return [$parseNamespace, $parseName, $parseFile, $parseArr]; + } + + /** + * 写入到文件 + * @param string $name + * @param array $data + * @param string $pathname + * @return mixed + */ + protected function writeToFile($name, $data, $pathname) + { + foreach ($data as $index => &$datum) { + $datum = is_array($datum) ? '' : $datum; + } + unset($datum); + $content = $this->getReplacedStub($name, $data); + + if (!is_dir(dirname($pathname))) { + mkdir(dirname($pathname), 0755, true); + } + return file_put_contents($pathname, $content); + } + + /** + * 获取替换后的数据 + * @param string $name + * @param array $data + * @return string + */ + protected function getReplacedStub($name, $data) + { + foreach ($data as $index => &$datum) { + $datum = is_array($datum) ? '' : $datum; + } + unset($datum); + $search = $replace = []; + foreach ($data as $k => $v) { + $search[] = "{%{$k}%}"; + $replace[] = $v; + } + $stubname = $this->getStub($name); + if (isset($this->stubList[$stubname])) { + $stub = $this->stubList[$stubname]; + } else { + $this->stubList[$stubname] = $stub = file_get_contents($stubname); + } + $content = str_replace($search, $replace, $stub); + return $content; + } + + /** + * 获取基础模板 + * @param string $name + * @return string + */ + protected function getStub($name) + { + return __DIR__ . DS . 'Crud' . DS . 'stubs' . DS . $name . '.stub'; + } + + protected function getLangItem($field, $content) + { + if ($content || !Lang::has($field)) { + $this->fieldMaxLen = strlen($field) > $this->fieldMaxLen ? strlen($field) : $this->fieldMaxLen; + $content = str_replace(',', ',', $content); + if (stripos($content, ':') !== false && stripos($content, ',') && stripos($content, '=') !== false) { + list($fieldLang, $item) = explode(':', $content); + $itemArr = [$field => $fieldLang]; + foreach (explode(',', $item) as $k => $v) { + $valArr = explode('=', $v); + if (count($valArr) == 2) { + list($key, $value) = $valArr; + $itemArr[$field . ' ' . $key] = $value; + $this->fieldMaxLen = strlen($field . ' ' . $key) > $this->fieldMaxLen ? strlen($field . ' ' . $key) : $this->fieldMaxLen; + } + } + } else { + $itemArr = [$field => $content]; + } + $resultArr = []; + foreach ($itemArr as $k => $v) { + $resultArr[] = " '" . mb_ucfirst($k) . "' => '{$v}'"; + } + return implode(",\n", $resultArr); + } else { + return ''; + } + } + + /** + * 读取数据和语言数组列表 + * @param array $arr + * @param boolean $withTpl + * @return array + */ + protected function getLangArray($arr, $withTpl = true) + { + $langArr = []; + foreach ($arr as $k => $v) { + $langArr[$k] = is_numeric($k) ? ($withTpl ? "{:" : "") . "__('" . mb_ucfirst($v) . "')" . ($withTpl ? "}" : "") : $v; + } + return $langArr; + } + + /** + * 将数据转换成带字符串 + * @param array $arr + * @return string + */ + protected function getArrayString($arr) + { + if (!is_array($arr)) { + return $arr; + } + $stringArr = []; + foreach ($arr as $k => $v) { + $is_var = in_array(substr($v, 0, 1), ['$', '_']); + if (!$is_var) { + $v = str_replace("'", "\'", $v); + $k = str_replace("'", "\'", $k); + } + $stringArr[] = "'" . $k . "' => " . ($is_var ? $v : "'{$v}'"); + } + return implode(", ", $stringArr); + } + + protected function getItemArray($item, $field, $comment) + { + $itemArr = []; + $comment = str_replace(',', ',', $comment); + if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false) { + list($fieldLang, $item) = explode(':', $comment); + $itemArr = []; + foreach (explode(',', $item) as $k => $v) { + $valArr = explode('=', $v); + if (count($valArr) == 2) { + list($key, $value) = $valArr; + $itemArr[$key] = $field . ' ' . $key; + } + } + } else { + foreach ($item as $k => $v) { + $itemArr[$v] = is_numeric($v) ? $field . ' ' . $v : $v; + } + } + return $itemArr; + } + + protected function getFieldType(& $v) + { + $inputType = 'text'; + switch ($v['DATA_TYPE']) { + case 'bigint': + case 'int': + case 'mediumint': + case 'smallint': + case 'tinyint': + $inputType = 'number'; + break; + case 'enum': + case 'set': + $inputType = 'select'; + break; + case 'decimal': + case 'double': + case 'float': + $inputType = 'number'; + break; + case 'longtext': + case 'text': + case 'mediumtext': + case 'smalltext': + case 'tinytext': + $inputType = 'textarea'; + break; + case 'year': + case 'date': + case 'time': + case 'datetime': + case 'timestamp': + $inputType = 'datetime'; + break; + default: + break; + } + $fieldsName = $v['COLUMN_NAME']; + // 指定后缀说明也是个时间字段 + if ($this->isMatchSuffix($fieldsName, $this->intDateSuffix)) { + $inputType = 'datetime'; + } + // 指定后缀结尾且类型为enum,说明是个单选框 + if ($this->isMatchSuffix($fieldsName, $this->enumRadioSuffix) && $v['DATA_TYPE'] == 'enum') { + $inputType = "radio"; + } + // 指定后缀结尾且类型为set,说明是个复选框 + if ($this->isMatchSuffix($fieldsName, $this->setCheckboxSuffix) && $v['DATA_TYPE'] == 'set') { + $inputType = "checkbox"; + } + // 指定后缀结尾且类型为char或tinyint且长度为1,说明是个Switch复选框 + if ($this->isMatchSuffix($fieldsName, $this->switchSuffix) && ($v['COLUMN_TYPE'] == 'tinyint(1)' || $v['COLUMN_TYPE'] == 'char(1)') && $v['COLUMN_DEFAULT'] !== '' && $v['COLUMN_DEFAULT'] !== null) { + $inputType = "switch"; + } + // 指定后缀结尾城市选择框 + if ($this->isMatchSuffix($fieldsName, $this->citySuffix) && ($v['DATA_TYPE'] == 'varchar' || $v['DATA_TYPE'] == 'char')) { + $inputType = "citypicker"; + } + // 指定后缀结尾城市选择框 + if ($this->isMatchSuffix($fieldsName, $this->rangeSuffix) && ($v['DATA_TYPE'] == 'varchar' || $v['DATA_TYPE'] == 'char')) { + $inputType = "datetimerange"; + } + // 指定后缀结尾JSON配置 + if ($this->isMatchSuffix($fieldsName, $this->jsonSuffix) && ($v['DATA_TYPE'] == 'varchar' || $v['DATA_TYPE'] == 'text')) { + $inputType = "fieldlist"; + } + // 指定后缀结尾标签配置 + if ($this->isMatchSuffix($fieldsName, $this->tagSuffix) && ($v['DATA_TYPE'] == 'varchar' || $v['DATA_TYPE'] == 'text')) { + $inputType = "tagsinput"; + } + return $inputType; + } + + /** + * 判断是否符合指定后缀 + * @param string $field 字段名称 + * @param mixed $suffixArr 后缀 + * @return boolean + */ + protected function isMatchSuffix($field, $suffixArr) + { + $suffixArr = is_array($suffixArr) ? $suffixArr : explode(',', $suffixArr); + foreach ($suffixArr as $k => $v) { + if (preg_match("/{$v}$/i", $field)) { + return true; + } + } + return false; + } + + /** + * 获取表单分组数据 + * @param string $field + * @param string $content + * @return string + */ + protected function getFormGroup($field, $content) + { + $langField = mb_ucfirst($field); + return << + +
+ {$content} +
+ +EOD; + } + + /** + * 获取图片模板数据 + * @param string $field + * @param string $content + * @return string + */ + protected function getImageUpload($field, $content) + { + $uploadfilter = $selectfilter = ''; + if ($this->isMatchSuffix($field, $this->imageField)) { + $uploadfilter = ' data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp,image/webp"'; + $selectfilter = ' data-mimetype="image/*"'; + } + $multiple = substr($field, -1) == 's' ? ' data-multiple="true"' : ' data-multiple="false"'; + $preview = ' data-preview-id="p-' . $field . '"'; + $previewcontainer = $preview ? '
    ' : ''; + return << + {$content} +
    + + +
    + + + {$previewcontainer} +EOD; + } + + /** + * 获取JS列数据 + * @param string $field + * @param string $datatype + * @param string $extend + * @param array $itemArr + * @return string + */ + protected function getJsColumn($field, $datatype = '', $extend = '', $itemArr = []) + { + $lang = mb_ucfirst($field); + $formatter = ''; + foreach ($this->fieldFormatterSuffix as $k => $v) { + if (preg_match("/{$k}$/i", $field)) { + if (is_array($v)) { + if (in_array($datatype, $v['type'])) { + $formatter = $v['name']; + break; + } + } else { + $formatter = $v; + break; + } + } + } + $html = str_repeat(" ", 24) . "{field: '{$field}', title: __('{$lang}')"; + + if ($datatype == 'set') { + $formatter = 'label'; + } + foreach ($itemArr as $k => &$v) { + if (substr($v, 0, 3) !== '__(') { + $v = "__('" . mb_ucfirst($v) . "')"; + } + } + unset($v); + $searchList = json_encode($itemArr, JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE); + $searchList = str_replace(['":"', '"}', ')","'], ['":', '}', '),"'], $searchList); + if ($itemArr) { + $html .= ", searchList: " . $searchList; + } + + // 文件、图片、权重等字段默认不加入搜索栏,字符串类型默认LIKE + $noSearchFiles = ['file$', 'files$', 'image$', 'images$', '^weigh$']; + if (preg_match("/" . implode('|', $noSearchFiles) . "/i", $field)) { + $html .= ", operate: false"; + } else if (in_array($datatype, ['varchar'])) { + $html .= ", operate: 'LIKE'"; + } + + if (in_array($datatype, ['date', 'datetime']) || $formatter === 'datetime') { + $html .= ", operate:'RANGE', addclass:'datetimerange', autocomplete:false"; + } elseif (in_array($datatype, ['float', 'double', 'decimal'])) { + $html .= ", operate:'BETWEEN'"; + } + if (in_array($datatype, ['set'])) { + $html .= ", operate:'FIND_IN_SET'"; + } + if (in_array($formatter, ['image', 'images'])) { + $html .= ", events: Table.api.events.image"; + } + if (in_array($formatter, ['toggle'])) { + $html .= ", table: table"; + } + if ($itemArr && !$formatter) { + $formatter = 'normal'; + } + if ($formatter) { + $html .= ", formatter: Table.api.formatter." . $formatter . "}"; + } else { + $html .= "}"; + } + return $html; + } + + protected function getCamelizeName($uncamelized_words, $separator = '_') + { + $uncamelized_words = $separator . str_replace($separator, " ", strtolower($uncamelized_words)); + return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator); + } + + protected function getFieldListName($field) + { + return $this->getCamelizeName($field) . 'List'; + } +} diff --git a/application/admin/command/Crud/stubs/add.stub b/application/admin/command/Crud/stubs/add.stub new file mode 100644 index 0000000..e51cf20 --- /dev/null +++ b/application/admin/command/Crud/stubs/add.stub @@ -0,0 +1,11 @@ +
    + +{%addList%} + +
    diff --git a/application/admin/command/Crud/stubs/controller.stub b/application/admin/command/Crud/stubs/controller.stub new file mode 100644 index 0000000..61a83cf --- /dev/null +++ b/application/admin/command/Crud/stubs/controller.stub @@ -0,0 +1,37 @@ +model = new \{%modelNamespace%}\{%modelName%}; +{%controllerAssignList%} + } + +{%controllerImport%} + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + +{%controllerIndex%} +} diff --git a/application/admin/command/Crud/stubs/controllerindex.stub b/application/admin/command/Crud/stubs/controllerindex.stub new file mode 100644 index 0000000..e04ca12 --- /dev/null +++ b/application/admin/command/Crud/stubs/controllerindex.stub @@ -0,0 +1,34 @@ + + /** + * 查看 + */ + public function index() + { + //当前是否为关联查询 + $this->relationSearch = {%relationSearch%}; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + {%relationWithList%} + ->where($where) + ->order($sort, $order) + ->paginate($limit); + + foreach ($list as $row) { + {%visibleFieldList%} + {%relationVisibleFieldList%} + } + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } diff --git a/application/admin/command/Crud/stubs/edit.stub b/application/admin/command/Crud/stubs/edit.stub new file mode 100644 index 0000000..1c05f20 --- /dev/null +++ b/application/admin/command/Crud/stubs/edit.stub @@ -0,0 +1,11 @@ +
    + +{%editList%} + +
    diff --git a/application/admin/command/Crud/stubs/html/checkbox.stub b/application/admin/command/Crud/stubs/html/checkbox.stub new file mode 100644 index 0000000..9aa587e --- /dev/null +++ b/application/admin/command/Crud/stubs/html/checkbox.stub @@ -0,0 +1,6 @@ + +
    + {foreach name="{%fieldList%}" item="vo"} + + {/foreach} +
    diff --git a/application/admin/command/Crud/stubs/html/fieldlist-template.stub b/application/admin/command/Crud/stubs/html/fieldlist-template.stub new file mode 100644 index 0000000..5881cac --- /dev/null +++ b/application/admin/command/Crud/stubs/html/fieldlist-template.stub @@ -0,0 +1,20 @@ + + + + {%theadList%} + + + +
    {:__('Operate')}
    + {:__('Append')} + +
    + diff --git a/application/admin/command/Crud/stubs/html/fieldlist.stub b/application/admin/command/Crud/stubs/html/fieldlist.stub new file mode 100644 index 0000000..42688bb --- /dev/null +++ b/application/admin/command/Crud/stubs/html/fieldlist.stub @@ -0,0 +1,10 @@ + +
    +
    + {:__('{%itemKey%}')} + {:__('{%itemValue%}')} +
    +
    {:__('Append')}
    + +
    + diff --git a/application/admin/command/Crud/stubs/html/heading-html.stub b/application/admin/command/Crud/stubs/html/heading-html.stub new file mode 100644 index 0000000..9392c80 --- /dev/null +++ b/application/admin/command/Crud/stubs/html/heading-html.stub @@ -0,0 +1,10 @@ + +
    + {:build_heading(null,FALSE)} + +
    diff --git a/application/admin/command/Crud/stubs/html/radio.stub b/application/admin/command/Crud/stubs/html/radio.stub new file mode 100644 index 0000000..a3c7b2e --- /dev/null +++ b/application/admin/command/Crud/stubs/html/radio.stub @@ -0,0 +1,6 @@ + +
    + {foreach name="{%fieldList%}" item="vo"} + + {/foreach} +
    diff --git a/application/admin/command/Crud/stubs/html/recyclebin-html.stub b/application/admin/command/Crud/stubs/html/recyclebin-html.stub new file mode 100644 index 0000000..8aa3949 --- /dev/null +++ b/application/admin/command/Crud/stubs/html/recyclebin-html.stub @@ -0,0 +1 @@ + {:__('Recycle bin')} \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/html/select.stub b/application/admin/command/Crud/stubs/html/select.stub new file mode 100644 index 0000000..b55baaa --- /dev/null +++ b/application/admin/command/Crud/stubs/html/select.stub @@ -0,0 +1,6 @@ + + diff --git a/application/admin/command/Crud/stubs/html/switch.stub b/application/admin/command/Crud/stubs/html/switch.stub new file mode 100644 index 0000000..e2aeec0 --- /dev/null +++ b/application/admin/command/Crud/stubs/html/switch.stub @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/index.stub b/application/admin/command/Crud/stubs/index.stub new file mode 100644 index 0000000..7fea21f --- /dev/null +++ b/application/admin/command/Crud/stubs/index.stub @@ -0,0 +1,35 @@ +
    + {%headingHtml%} + +
    +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    diff --git a/application/admin/command/Crud/stubs/javascript.stub b/application/admin/command/Crud/stubs/javascript.stub new file mode 100644 index 0000000..31b2b0d --- /dev/null +++ b/application/admin/command/Crud/stubs/javascript.stub @@ -0,0 +1,48 @@ +define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { + + var Controller = { + index: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + index_url: '{%controllerUrl%}/index' + location.search, + add_url: '{%controllerUrl%}/add', + edit_url: '{%controllerUrl%}/edit', + del_url: '{%controllerUrl%}/del', + multi_url: '{%controllerUrl%}/multi', + import_url: '{%controllerUrl%}/import', + table: '{%table%}', + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: $.fn.bootstrapTable.defaults.extend.index_url, + pk: '{%pk%}', + sortName: '{%order%}',{%fixedColumnsJs%} + columns: [ + [ + {%javascriptList%} + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + },{%recyclebinJs%} + add: function () { + Controller.api.bindevent(); + }, + edit: function () { + Controller.api.bindevent(); + }, + api: { + bindevent: function () { + Form.api.bindevent($("form[role=form]")); + } + } + }; + return Controller; +}); diff --git a/application/admin/command/Crud/stubs/lang.stub b/application/admin/command/Crud/stubs/lang.stub new file mode 100644 index 0000000..2828223 --- /dev/null +++ b/application/admin/command/Crud/stubs/lang.stub @@ -0,0 +1,5 @@ +{%listMethodName%}(); + return implode(',', array_intersect_key($list, array_flip($valueArr))); + } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/datetime.stub b/application/admin/command/Crud/stubs/mixins/datetime.stub new file mode 100644 index 0000000..591dd4d --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/datetime.stub @@ -0,0 +1,6 @@ + + public function {%methodName%}($value, $data) + { + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/enum.stub b/application/admin/command/Crud/stubs/mixins/enum.stub new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/enum.stub @@ -0,0 +1 @@ + diff --git a/application/admin/command/Crud/stubs/mixins/import.stub b/application/admin/command/Crud/stubs/mixins/import.stub new file mode 100644 index 0000000..f1db48a --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/import.stub @@ -0,0 +1,4 @@ + public function import() + { + parent::import(); + } diff --git a/application/admin/command/Crud/stubs/mixins/modelinit.stub b/application/admin/command/Crud/stubs/mixins/modelinit.stub new file mode 100644 index 0000000..329fb95 --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/modelinit.stub @@ -0,0 +1,8 @@ + + protected static function init() + { + self::afterInsert(function ($row) { + $pk = $row->getPk(); + $row->getQuery()->where($pk, $row[$pk])->update(['{%order%}' => $row[$pk]]); + }); + } diff --git a/application/admin/command/Crud/stubs/mixins/modelrelationmethod-hasmany.stub b/application/admin/command/Crud/stubs/mixins/modelrelationmethod-hasmany.stub new file mode 100644 index 0000000..f75e56c --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/modelrelationmethod-hasmany.stub @@ -0,0 +1,5 @@ + + public function {%relationMethod%}s() + { + return $this->{%relationMode%}('{%relationClassName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}'); + } diff --git a/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub b/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub new file mode 100644 index 0000000..3da6462 --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/modelrelationmethod.stub @@ -0,0 +1,5 @@ + + public function {%relationMethod%}() + { + return $this->{%relationMode%}('{%relationClassName%}', '{%relationForeignKey%}', '{%relationPrimaryKey%}', [], 'LEFT')->setEagerlyType(0); + } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/multiple.stub b/application/admin/command/Crud/stubs/mixins/multiple.stub new file mode 100644 index 0000000..d5f7b66 --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/multiple.stub @@ -0,0 +1,8 @@ + + public function {%methodName%}($value, $data) + { + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); + $valueArr = explode(',', $value); + $list = $this->{%listMethodName%}(); + return implode(',', array_intersect_key($list, array_flip($valueArr))); + } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/radio.stub b/application/admin/command/Crud/stubs/mixins/radio.stub new file mode 100644 index 0000000..71234a6 --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/radio.stub @@ -0,0 +1,7 @@ + + public function {%methodName%}($value, $data) + { + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); + $list = $this->{%listMethodName%}(); + return isset($list[$value]) ? $list[$value] : ''; + } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/mixins/recyclebinjs.stub b/application/admin/command/Crud/stubs/mixins/recyclebinjs.stub new file mode 100644 index 0000000..180b719 --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/recyclebinjs.stub @@ -0,0 +1,60 @@ + + recyclebin: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + 'dragsort_url': '' + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: '{%controllerUrl%}/recyclebin' + location.search, + pk: 'id', + sortName: 'id', + columns: [ + [ + {checkbox: true}, + {field: 'id', title: __('Id')},{%recyclebinTitleJs%} + { + field: '{%deleteTimeField%}', + 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: '{%controllerUrl%}/restore', + refresh: true + }, + { + name: 'Destroy', + text: __('Destroy'), + classname: 'btn btn-xs btn-danger btn-ajax btn-destroyit', + icon: 'fa fa-times', + url: '{%controllerUrl%}/destroy', + refresh: true + } + ], + formatter: Table.api.formatter.operate + } + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + }, diff --git a/application/admin/command/Crud/stubs/mixins/select.stub b/application/admin/command/Crud/stubs/mixins/select.stub new file mode 100644 index 0000000..71234a6 --- /dev/null +++ b/application/admin/command/Crud/stubs/mixins/select.stub @@ -0,0 +1,7 @@ + + public function {%methodName%}($value, $data) + { + $value = $value ? $value : (isset($data['{%field%}']) ? $data['{%field%}'] : ''); + $list = $this->{%listMethodName%}(); + return isset($list[$value]) ? $list[$value] : ''; + } \ No newline at end of file diff --git a/application/admin/command/Crud/stubs/model.stub b/application/admin/command/Crud/stubs/model.stub new file mode 100644 index 0000000..855af09 --- /dev/null +++ b/application/admin/command/Crud/stubs/model.stub @@ -0,0 +1,40 @@ + + {:build_heading()} + +
    +
    +
    + +
    + +
    +
    + diff --git a/application/admin/command/Crud/stubs/relationmodel.stub b/application/admin/command/Crud/stubs/relationmodel.stub new file mode 100644 index 0000000..dd22a53 --- /dev/null +++ b/application/admin/command/Crud/stubs/relationmodel.stub @@ -0,0 +1,12 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/command/Install.php b/application/admin/command/Install.php new file mode 100644 index 0000000..ec68f94 --- /dev/null +++ b/application/admin/command/Install.php @@ -0,0 +1,329 @@ +setName('install') + ->addOption('hostname', 'a', Option::VALUE_OPTIONAL, 'mysql hostname', $config['hostname']) + ->addOption('hostport', 'o', Option::VALUE_OPTIONAL, 'mysql hostport', $config['hostport']) + ->addOption('database', 'd', Option::VALUE_OPTIONAL, 'mysql database', $config['database']) + ->addOption('prefix', 'r', Option::VALUE_OPTIONAL, 'table prefix', $config['prefix']) + ->addOption('username', 'u', Option::VALUE_OPTIONAL, 'mysql username', $config['username']) + ->addOption('password', 'p', Option::VALUE_OPTIONAL, 'mysql password', $config['password']) + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', false) + ->setDescription('New installation of FastAdmin'); + } + + /** + * 命令行安装 + */ + protected function execute(Input $input, Output $output) + { + define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS); + // 覆盖安装 + $force = $input->getOption('force'); + $hostname = $input->getOption('hostname'); + $hostport = $input->getOption('hostport'); + $database = $input->getOption('database'); + $prefix = $input->getOption('prefix'); + $username = $input->getOption('username'); + $password = $input->getOption('password'); + + $installLockFile = INSTALL_PATH . "install.lock"; + if (is_file($installLockFile) && !$force) { + throw new Exception("\nFastAdmin already installed!\nIf you need to reinstall again, use the parameter --force=true "); + } + + $adminUsername = 'admin'; + $adminPassword = Random::alnum(10); + $adminEmail = 'admin@admin.com'; + $siteName = __('My Website'); + + $adminName = $this->installation($hostname, $hostport, $database, $username, $password, $prefix, $adminUsername, $adminPassword, $adminEmail, $siteName); + if ($adminName) { + $output->highlight("Admin url:http://www.yoursite.com/{$adminName}"); + } + + $output->highlight("Admin username:{$adminUsername}"); + $output->highlight("Admin password:{$adminPassword}"); + + \think\Cache::rm('__menu__'); + + $output->info("Install Successed!"); + } + + /** + * PC端安装 + */ + public function index() + { + $this->view = View::instance(Config::get('template'), Config::get('view_replace_str')); + $this->request = Request::instance(); + + define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS); + + $lang = $this->request->langset(); + $lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn'; + + if (!$lang || in_array($lang, ['zh-cn', 'zh-hans-cn'])) { + Lang::load(INSTALL_PATH . 'zh-cn.php'); + } + + $installLockFile = INSTALL_PATH . "install.lock"; + + if (is_file($installLockFile)) { + echo __('The system has been installed. If you need to reinstall, please remove %s first', 'install.lock'); + exit; + } + $output = function ($code, $msg, $url = null, $data = null) { + return json(['code' => $code, 'msg' => $msg, 'url' => $url, 'data' => $data]); + }; + + if ($this->request->isPost()) { + $mysqlHostname = $this->request->post('mysqlHostname', '127.0.0.1'); + $mysqlHostport = $this->request->post('mysqlHostport', '3306'); + $hostArr = explode(':', $mysqlHostname); + if (count($hostArr) > 1) { + $mysqlHostname = $hostArr[0]; + $mysqlHostport = $hostArr[1]; + } + $mysqlUsername = $this->request->post('mysqlUsername', 'root'); + $mysqlPassword = $this->request->post('mysqlPassword', ''); + $mysqlDatabase = $this->request->post('mysqlDatabase', ''); + $mysqlPrefix = $this->request->post('mysqlPrefix', 'fa_'); + $adminUsername = $this->request->post('adminUsername', 'admin'); + $adminPassword = $this->request->post('adminPassword', ''); + $adminPasswordConfirmation = $this->request->post('adminPasswordConfirmation', ''); + $adminEmail = $this->request->post('adminEmail', 'admin@admin.com'); + $siteName = $this->request->post('siteName', __('My Website')); + + if ($adminPassword !== $adminPasswordConfirmation) { + return $output(0, __('The two passwords you entered did not match')); + } + + $adminName = ''; + try { + $adminName = $this->installation($mysqlHostname, $mysqlHostport, $mysqlDatabase, $mysqlUsername, $mysqlPassword, $mysqlPrefix, $adminUsername, $adminPassword, $adminEmail, $siteName); + } catch (\PDOException $e) { + throw new Exception($e->getMessage()); + } catch (\Exception $e) { + return $output(0, $e->getMessage()); + } + return $output(1, __('Install Successed'), null, ['adminName' => $adminName]); + } + $errInfo = ''; + try { + $this->checkenv(); + } catch (\Exception $e) { + $errInfo = $e->getMessage(); + } + return $this->view->fetch(INSTALL_PATH . "install.html", ['errInfo' => $errInfo]); + } + + /** + * 执行安装 + */ + protected function installation($mysqlHostname, $mysqlHostport, $mysqlDatabase, $mysqlUsername, $mysqlPassword, $mysqlPrefix, $adminUsername, $adminPassword, $adminEmail = null, $siteName = null) + { + $this->checkenv(); + + if ($mysqlDatabase == '') { + throw new Exception(__('Please input correct database')); + } + if (!preg_match("/^\w{3,12}$/", $adminUsername)) { + throw new Exception(__('Please input correct username')); + } + if (!preg_match("/^[\S]{6,16}$/", $adminPassword)) { + throw new Exception(__('Please input correct password')); + } + $weakPasswordArr = ['123456', '12345678', '123456789', '654321', '111111', '000000', 'password', 'qwerty', 'abc123', '1qaz2wsx']; + if (in_array($adminPassword, $weakPasswordArr)) { + throw new Exception(__('Password is too weak')); + } + if ($siteName == '' || preg_match("/fast" . "admin/i", $siteName)) { + throw new Exception(__('Please input correct website')); + } + + $sql = file_get_contents(INSTALL_PATH . 'fastadmin.sql'); + + $sql = str_replace("`fa_", "`{$mysqlPrefix}", $sql); + + // 先尝试能否自动创建数据库 + $config = Config::get('database'); + try { + $pdo = new PDO("{$config['type']}:host={$mysqlHostname}" . ($mysqlHostport ? ";port={$mysqlHostport}" : ''), $mysqlUsername, $mysqlPassword); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $pdo->query("CREATE DATABASE IF NOT EXISTS `{$mysqlDatabase}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"); + + // 连接install命令中指定的数据库 + $instance = Db::connect([ + 'type' => "{$config['type']}", + 'hostname' => "{$mysqlHostname}", + 'hostport' => "{$mysqlHostport}", + 'database' => "{$mysqlDatabase}", + 'username' => "{$mysqlUsername}", + 'password' => "{$mysqlPassword}", + 'prefix' => "{$mysqlPrefix}", + ]); + + // 查询一次SQL,判断连接是否正常 + $instance->execute("SELECT 1"); + + // 调用原生PDO对象进行批量查询 + $instance->getPdo()->exec($sql); + } catch (\PDOException $e) { + throw new Exception($e->getMessage()); + } + // 后台入口文件 + $adminFile = ROOT_PATH . 'public' . DS . 'admin.php'; + + // 数据库配置文件 + $dbConfigFile = APP_PATH . 'database.php'; + $dbConfigText = @file_get_contents($dbConfigFile); + $callback = function ($matches) use ($mysqlHostname, $mysqlHostport, $mysqlUsername, $mysqlPassword, $mysqlDatabase, $mysqlPrefix) { + $field = "mysql" . ucfirst($matches[1]); + $replace = $$field; + if ($matches[1] == 'hostport' && $mysqlHostport == 3306) { + $replace = ''; + } + return "'{$matches[1]}'{$matches[2]}=>{$matches[3]}Env::get('database.{$matches[1]}', '{$replace}'),"; + }; + $dbConfigText = preg_replace_callback("/'(hostname|database|username|password|hostport|prefix)'(\s+)=>(\s+)Env::get\((.*)\)\,/", $callback, $dbConfigText); + + // 检测能否成功写入数据库配置 + $result = @file_put_contents($dbConfigFile, $dbConfigText); + if (!$result) { + throw new Exception(__('The current permissions are insufficient to write the file %s', 'application/database.php')); + } + + // 设置新的Token随机密钥key + $oldTokenKey = config('token.key'); + $newTokenKey = \fast\Random::alnum(32); + $coreConfigFile = CONF_PATH . 'config.php'; + $coreConfigText = @file_get_contents($coreConfigFile); + $coreConfigText = preg_replace("/'key'(\s+)=>(\s+)'{$oldTokenKey}'/", "'key'\$1=>\$2'{$newTokenKey}'", $coreConfigText); + + $result = @file_put_contents($coreConfigFile, $coreConfigText); + if (!$result) { + throw new Exception(__('The current permissions are insufficient to write the file %s', 'application/config.php')); + } + + $avatar = request()->domain() . '/assets/img/avatar.png'; + // 变更默认管理员密码 + $adminPassword = $adminPassword ? $adminPassword : Random::alnum(8); + $adminEmail = $adminEmail ? $adminEmail : "admin@admin.com"; + $newSalt = substr(md5(uniqid(true)), 0, 6); + $newPassword = md5(md5($adminPassword) . $newSalt); + $data = ['username' => $adminUsername, 'email' => $adminEmail, 'avatar' => $avatar, 'password' => $newPassword, 'salt' => $newSalt]; + $instance->name('admin')->where('username', 'admin')->update($data); + + // 变更前台默认用户的密码,随机生成 + $newSalt = substr(md5(uniqid(true)), 0, 6); + $newPassword = md5(md5(Random::alnum(8)) . $newSalt); + $instance->name('user')->where('username', 'admin')->update(['avatar' => $avatar, 'password' => $newPassword, 'salt' => $newSalt]); + + // 修改后台入口 + $adminName = ''; + if (is_file($adminFile)) { + $adminName = Random::alpha(10) . '.php'; + rename($adminFile, ROOT_PATH . 'public' . DS . $adminName); + } + + //修改站点名称 + if ($siteName != config('site.name')) { + $instance->name('config')->where('name', 'name')->update(['value' => $siteName]); + $siteConfigFile = CONF_PATH . 'extra' . DS . 'site.php'; + $siteConfig = include $siteConfigFile; + $configList = $instance->name("config")->select(); + foreach ($configList as $k => $value) { + if (in_array($value['type'], ['selects', 'checkbox', 'images', 'files'])) { + $value['value'] = is_array($value['value']) ? $value['value'] : explode(',', $value['value']); + } + if ($value['type'] == 'array') { + $value['value'] = (array)json_decode($value['value'], true); + } + $siteConfig[$value['name']] = $value['value']; + } + $siteConfig['name'] = $siteName; + file_put_contents($siteConfigFile, ' $v) { + if (!is_dir(ROOT_PATH . $v)) { + throw new Exception(__('Please go to the official website to download the full package or resource package and try to install')); + break; + } + } + return true; + } +} diff --git a/application/admin/command/Install/fastadmin.sql b/application/admin/command/Install/fastadmin.sql new file mode 100644 index 0000000..e71020f --- /dev/null +++ b/application/admin/command/Install/fastadmin.sql @@ -0,0 +1,605 @@ +/* + FastAdmin Install SQL + Date: 2020-06-11 22:11:09 +*/ + +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for fa_admin +-- ---------------------------- +DROP TABLE IF EXISTS `fa_admin`; +CREATE TABLE `fa_admin` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `username` varchar(20) DEFAULT '' COMMENT '用户名', + `nickname` varchar(50) DEFAULT '' COMMENT '昵称', + `password` varchar(32) DEFAULT '' COMMENT '密码', + `salt` varchar(30) DEFAULT '' COMMENT '密码盐', + `avatar` varchar(255) DEFAULT '' COMMENT '头像', + `email` varchar(100) DEFAULT '' COMMENT '电子邮箱', + `mobile` varchar(11) DEFAULT '' COMMENT '手机号码', + `loginfailure` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '失败次数', + `logintime` bigint(16) DEFAULT NULL COMMENT '登录时间', + `loginip` varchar(50) DEFAULT NULL COMMENT '登录IP', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `token` varchar(59) DEFAULT '' COMMENT 'Session标识', + `status` varchar(30) NOT NULL DEFAULT 'normal' COMMENT '状态', + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='管理员表'; + +-- ---------------------------- +-- Records of fa_admin +-- ---------------------------- +BEGIN; +INSERT INTO `fa_admin` VALUES (1, 'admin', 'Admin', '', '', '/assets/img/avatar.png', 'admin@admin.com', '', 0, 1491635035, '127.0.0.1',1491635035, 1491635035, '', 'normal'); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_admin_log +-- ---------------------------- +DROP TABLE IF EXISTS `fa_admin_log`; +CREATE TABLE `fa_admin_log` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `admin_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '管理员ID', + `username` varchar(30) DEFAULT '' COMMENT '管理员名字', + `url` varchar(1500) DEFAULT '' COMMENT '操作页面', + `title` varchar(100) DEFAULT '' COMMENT '日志标题', + `content` longtext NOT NULL COMMENT '内容', + `ip` varchar(50) DEFAULT '' COMMENT 'IP', + `useragent` varchar(255) DEFAULT '' COMMENT 'User-Agent', + `createtime` bigint(16) DEFAULT NULL COMMENT '操作时间', + PRIMARY KEY (`id`), + KEY `name` (`username`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='管理员日志表'; + +-- ---------------------------- +-- Table structure for fa_area +-- ---------------------------- +DROP TABLE IF EXISTS `fa_area`; +CREATE TABLE `fa_area` ( + `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `pid` int(10) DEFAULT NULL COMMENT '父id', + `shortname` varchar(100) DEFAULT NULL COMMENT '简称', + `name` varchar(100) DEFAULT NULL COMMENT '名称', + `mergename` varchar(255) DEFAULT NULL COMMENT '全称', + `level` tinyint(4) DEFAULT NULL COMMENT '层级:1=省,2=市,3=区/县', + `pinyin` varchar(100) DEFAULT NULL COMMENT '拼音', + `code` varchar(100) DEFAULT NULL COMMENT '长途区号', + `zip` varchar(100) DEFAULT NULL COMMENT '邮编', + `first` varchar(50) DEFAULT NULL COMMENT '首字母', + `lng` varchar(100) DEFAULT NULL COMMENT '经度', + `lat` varchar(100) DEFAULT NULL COMMENT '纬度', + PRIMARY KEY (`id`), + KEY `pid` (`pid`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='地区表'; + +-- ---------------------------- +-- Table structure for fa_attachment +-- ---------------------------- +DROP TABLE IF EXISTS `fa_attachment`; +CREATE TABLE `fa_attachment` ( + `id` int(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `category` varchar(50) DEFAULT '' COMMENT '类别', + `admin_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '管理员ID', + `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', + `url` varchar(255) DEFAULT '' COMMENT '物理路径', + `imagewidth` varchar(30) DEFAULT '' COMMENT '宽度', + `imageheight` varchar(30) DEFAULT '' COMMENT '高度', + `imagetype` varchar(30) DEFAULT '' COMMENT '图片类型', + `imageframes` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '图片帧数', + `filename` varchar(100) DEFAULT '' COMMENT '文件名称', + `filesize` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '文件大小', + `mimetype` varchar(100) DEFAULT '' COMMENT 'mime类型', + `extparam` varchar(255) DEFAULT '' COMMENT '透传数据', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建日期', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `uploadtime` bigint(16) DEFAULT NULL COMMENT '上传时间', + `storage` varchar(100) NOT NULL DEFAULT 'local' COMMENT '存储位置', + `sha1` varchar(40) DEFAULT '' COMMENT '文件 sha1编码', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='附件表'; + +-- ---------------------------- +-- Records of fa_attachment +-- ---------------------------- +BEGIN; +INSERT INTO `fa_attachment` VALUES (1, '', 1, 0, '/assets/img/qrcode.png', '150', '150', 'png', 0, 'qrcode.png', 21859, 'image/png', '', 1491635035, 1491635035, 1491635035, 'local', '17163603d0263e4838b9387ff2cd4877e8b018f6'); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_auth_group +-- ---------------------------- +DROP TABLE IF EXISTS `fa_auth_group`; +CREATE TABLE `fa_auth_group` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父组别', + `name` varchar(100) DEFAULT '' COMMENT '组名', + `rules` text NOT NULL COMMENT '规则ID', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `status` varchar(30) DEFAULT '' COMMENT '状态', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='分组表'; + +-- ---------------------------- +-- Records of fa_auth_group +-- ---------------------------- +BEGIN; +INSERT INTO `fa_auth_group` VALUES (1, 0, 'Admin group', '*', 1491635035, 1491635035, 'normal'); +INSERT INTO `fa_auth_group` VALUES (2, 1, 'Second group', '13,14,16,15,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,40,41,42,43,44,45,46,47,48,49,50,55,56,57,58,59,60,61,62,63,64,65,1,9,10,11,7,6,8,2,4,5', 1491635035, 1491635035, 'normal'); +INSERT INTO `fa_auth_group` VALUES (3, 2, 'Third group', '1,4,9,10,11,13,14,15,16,17,40,41,42,43,44,45,46,47,48,49,50,55,56,57,58,59,60,61,62,63,64,65,5', 1491635035, 1491635035, 'normal'); +INSERT INTO `fa_auth_group` VALUES (4, 1, 'Second group 2', '1,4,13,14,15,16,17,55,56,57,58,59,60,61,62,63,64,65', 1491635035, 1491635035, 'normal'); +INSERT INTO `fa_auth_group` VALUES (5, 2, 'Third group 2', '1,2,6,7,8,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34', 1491635035, 1491635035, 'normal'); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_auth_group_access +-- ---------------------------- +DROP TABLE IF EXISTS `fa_auth_group_access`; +CREATE TABLE `fa_auth_group_access` ( + `uid` int(10) unsigned NOT NULL COMMENT '会员ID', + `group_id` int(10) unsigned NOT NULL COMMENT '级别ID', + UNIQUE KEY `uid_group_id` (`uid`,`group_id`), + KEY `uid` (`uid`), + KEY `group_id` (`group_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='权限分组表'; + +-- ---------------------------- +-- Records of fa_auth_group_access +-- ---------------------------- +BEGIN; +INSERT INTO `fa_auth_group_access` VALUES (1, 1); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_auth_rule +-- ---------------------------- +DROP TABLE IF EXISTS `fa_auth_rule`; +CREATE TABLE `fa_auth_rule` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `type` enum('menu','file') NOT NULL DEFAULT 'file' COMMENT 'menu为菜单,file为权限节点', + `pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父ID', + `name` varchar(100) DEFAULT '' COMMENT '规则名称', + `title` varchar(50) DEFAULT '' COMMENT '规则名称', + `icon` varchar(50) DEFAULT '' COMMENT '图标', + `url` varchar(255) DEFAULT '' COMMENT '规则URL', + `condition` varchar(255) DEFAULT '' COMMENT '条件', + `remark` varchar(255) DEFAULT '' COMMENT '备注', + `ismenu` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否为菜单', + `menutype` enum('addtabs','blank','dialog','ajax') DEFAULT NULL COMMENT '菜单类型', + `extend` varchar(255) DEFAULT '' COMMENT '扩展属性', + `py` varchar(30) DEFAULT '' COMMENT '拼音首字母', + `pinyin` varchar(100) DEFAULT '' COMMENT '拼音', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重', + `status` varchar(30) DEFAULT '' COMMENT '状态', + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) USING BTREE, + KEY `pid` (`pid`), + KEY `weigh` (`weigh`) +) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='节点表'; + +-- ---------------------------- +-- Records of fa_auth_rule +-- ---------------------------- +BEGIN; +INSERT INTO `fa_auth_rule` VALUES (1, 'file', 0, 'dashboard', 'Dashboard', 'fa fa-dashboard', '', '', 'Dashboard tips', 1, NULL, '', 'kzt', 'kongzhitai', 1491635035, 1491635035, 143, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (2, 'file', 0, 'general', 'General', 'fa fa-cogs', '', '', '', 1, NULL, '', 'cggl', 'changguiguanli', 1491635035, 1491635035, 137, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (3, 'file', 0, 'category', 'Category', 'fa fa-leaf', '', '', 'Category tips', 0, NULL, '', 'flgl', 'fenleiguanli', 1491635035, 1491635035, 119, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (4, 'file', 0, 'addon', 'Addon', 'fa fa-rocket', '', '', 'Addon tips', 1, NULL, '', 'cjgl', 'chajianguanli', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (5, 'file', 0, 'auth', 'Auth', 'fa fa-group', '', '', '', 1, NULL, '', 'qxgl', 'quanxianguanli', 1491635035, 1491635035, 99, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (6, 'file', 2, 'general/config', 'Config', 'fa fa-cog', '', '', 'Config tips', 1, NULL, '', 'xtpz', 'xitongpeizhi', 1491635035, 1491635035, 60, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (7, 'file', 2, 'general/attachment', 'Attachment', 'fa fa-file-image-o', '', '', 'Attachment tips', 1, NULL, '', 'fjgl', 'fujianguanli', 1491635035, 1491635035, 53, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (8, 'file', 2, 'general/profile', 'Profile', 'fa fa-user', '', '', '', 1, NULL, '', 'grzl', 'gerenziliao', 1491635035, 1491635035, 34, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (9, 'file', 5, 'auth/admin', 'Admin', 'fa fa-user', '', '', 'Admin tips', 1, NULL, '', 'glygl', 'guanliyuanguanli', 1491635035, 1491635035, 118, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (10, 'file', 5, 'auth/adminlog', 'Admin log', 'fa fa-list-alt', '', '', 'Admin log tips', 1, NULL, '', 'glyrz', 'guanliyuanrizhi', 1491635035, 1491635035, 113, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (11, 'file', 5, 'auth/group', 'Group', 'fa fa-group', '', '', 'Group tips', 1, NULL, '', 'jsz', 'juesezu', 1491635035, 1491635035, 109, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (12, 'file', 5, 'auth/rule', 'Rule', 'fa fa-bars', '', '', 'Rule tips', 1, NULL, '', 'cdgz', 'caidanguize', 1491635035, 1491635035, 104, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (13, 'file', 1, 'dashboard/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 136, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (14, 'file', 1, 'dashboard/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 135, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (15, 'file', 1, 'dashboard/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 133, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (16, 'file', 1, 'dashboard/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 134, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (17, 'file', 1, 'dashboard/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 132, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (18, 'file', 6, 'general/config/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 52, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (19, 'file', 6, 'general/config/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 51, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (20, 'file', 6, 'general/config/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 50, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (21, 'file', 6, 'general/config/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 49, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (22, 'file', 6, 'general/config/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 48, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (23, 'file', 7, 'general/attachment/index', 'View', 'fa fa-circle-o', '', '', 'Attachment tips', 0, NULL, '', '', '', 1491635035, 1491635035, 59, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (24, 'file', 7, 'general/attachment/select', 'Select attachment', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 58, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (25, 'file', 7, 'general/attachment/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 57, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (26, 'file', 7, 'general/attachment/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 56, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (27, 'file', 7, 'general/attachment/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 55, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (28, 'file', 7, 'general/attachment/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 54, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (29, 'file', 8, 'general/profile/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 33, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (30, 'file', 8, 'general/profile/update', 'Update profile', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 32, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (31, 'file', 8, 'general/profile/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 31, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (32, 'file', 8, 'general/profile/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 30, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (33, 'file', 8, 'general/profile/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 29, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (34, 'file', 8, 'general/profile/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 28, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (35, 'file', 3, 'category/index', 'View', 'fa fa-circle-o', '', '', 'Category tips', 0, NULL, '', '', '', 1491635035, 1491635035, 142, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (36, 'file', 3, 'category/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 141, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (37, 'file', 3, 'category/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 140, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (38, 'file', 3, 'category/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 139, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (39, 'file', 3, 'category/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 138, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (40, 'file', 9, 'auth/admin/index', 'View', 'fa fa-circle-o', '', '', 'Admin tips', 0, NULL, '', '', '', 1491635035, 1491635035, 117, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (41, 'file', 9, 'auth/admin/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 116, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (42, 'file', 9, 'auth/admin/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 115, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (43, 'file', 9, 'auth/admin/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 114, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (44, 'file', 10, 'auth/adminlog/index', 'View', 'fa fa-circle-o', '', '', 'Admin log tips', 0, NULL, '', '', '', 1491635035, 1491635035, 112, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (45, 'file', 10, 'auth/adminlog/detail', 'Detail', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 111, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (46, 'file', 10, 'auth/adminlog/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 110, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (47, 'file', 11, 'auth/group/index', 'View', 'fa fa-circle-o', '', '', 'Group tips', 0, NULL, '', '', '', 1491635035, 1491635035, 108, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (48, 'file', 11, 'auth/group/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 107, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (49, 'file', 11, 'auth/group/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 106, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (50, 'file', 11, 'auth/group/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 105, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (51, 'file', 12, 'auth/rule/index', 'View', 'fa fa-circle-o', '', '', 'Rule tips', 0, NULL, '', '', '', 1491635035, 1491635035, 103, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (52, 'file', 12, 'auth/rule/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 102, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (53, 'file', 12, 'auth/rule/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 101, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (54, 'file', 12, 'auth/rule/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 100, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (55, 'file', 4, 'addon/index', 'View', 'fa fa-circle-o', '', '', 'Addon tips', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (56, 'file', 4, 'addon/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (57, 'file', 4, 'addon/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (58, 'file', 4, 'addon/del', 'Delete', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (59, 'file', 4, 'addon/downloaded', 'Local addon', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (60, 'file', 4, 'addon/state', 'Update state', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (63, 'file', 4, 'addon/config', 'Setting', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (64, 'file', 4, 'addon/refresh', 'Refresh', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (65, 'file', 4, 'addon/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (66, 'file', 0, 'user', 'User', 'fa fa-user-circle', '', '', '', 1, NULL, '', 'hygl', 'huiyuanguanli', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (67, 'file', 66, 'user/user', 'User', 'fa fa-user', '', '', '', 1, NULL, '', 'hygl', 'huiyuanguanli', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (68, 'file', 67, 'user/user/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (69, 'file', 67, 'user/user/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (70, 'file', 67, 'user/user/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (71, 'file', 67, 'user/user/del', 'Del', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (72, 'file', 67, 'user/user/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (73, 'file', 66, 'user/group', 'User group', 'fa fa-users', '', '', '', 1, NULL, '', 'hyfz', 'huiyuanfenzu', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (74, 'file', 73, 'user/group/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (75, 'file', 73, 'user/group/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (76, 'file', 73, 'user/group/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (77, 'file', 73, 'user/group/del', 'Del', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (78, 'file', 73, 'user/group/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (79, 'file', 66, 'user/rule', 'User rule', 'fa fa-circle-o', '', '', '', 1, NULL, '', 'hygz', 'huiyuanguize', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (80, 'file', 79, 'user/rule/index', 'View', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (81, 'file', 79, 'user/rule/del', 'Del', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (82, 'file', 79, 'user/rule/add', 'Add', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (83, 'file', 79, 'user/rule/edit', 'Edit', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +INSERT INTO `fa_auth_rule` VALUES (84, 'file', 79, 'user/rule/multi', 'Multi', 'fa fa-circle-o', '', '', '', 0, NULL, '', '', '', 1491635035, 1491635035, 0, 'normal'); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_category +-- ---------------------------- +DROP TABLE IF EXISTS `fa_category`; +CREATE TABLE `fa_category` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `pid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父ID', + `type` varchar(30) DEFAULT '' COMMENT '栏目类型', + `name` varchar(30) DEFAULT '', + `nickname` varchar(50) DEFAULT '', + `flag` set('hot','index','recommend') DEFAULT '', + `image` varchar(100) DEFAULT '' COMMENT '图片', + `keywords` varchar(255) DEFAULT '' COMMENT '关键字', + `description` varchar(255) DEFAULT '' COMMENT '描述', + `diyname` varchar(30) DEFAULT '' COMMENT '自定义名称', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重', + `status` varchar(30) DEFAULT '' COMMENT '状态', + PRIMARY KEY (`id`), + KEY `weigh` (`weigh`,`id`), + KEY `pid` (`pid`) +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='分类表'; + +-- ---------------------------- +-- Records of fa_category +-- ---------------------------- +BEGIN; +INSERT INTO `fa_category` VALUES (1, 0, 'page', '官方新闻', 'news', 'recommend', '/assets/img/qrcode.png', '', '', 'news', 1491635035, 1491635035, 1, 'normal'); +INSERT INTO `fa_category` VALUES (2, 0, 'page', '移动应用', 'mobileapp', 'hot', '/assets/img/qrcode.png', '', '', 'mobileapp', 1491635035, 1491635035, 2, 'normal'); +INSERT INTO `fa_category` VALUES (3, 2, 'page', '微信公众号', 'wechatpublic', 'index', '/assets/img/qrcode.png', '', '', 'wechatpublic', 1491635035, 1491635035, 3, 'normal'); +INSERT INTO `fa_category` VALUES (4, 2, 'page', 'Android开发', 'android', 'recommend', '/assets/img/qrcode.png', '', '', 'android', 1491635035, 1491635035, 4, 'normal'); +INSERT INTO `fa_category` VALUES (5, 0, 'page', '软件产品', 'software', 'recommend', '/assets/img/qrcode.png', '', '', 'software', 1491635035, 1491635035, 5, 'normal'); +INSERT INTO `fa_category` VALUES (6, 5, 'page', '网站建站', 'website', 'recommend', '/assets/img/qrcode.png', '', '', 'website', 1491635035, 1491635035, 6, 'normal'); +INSERT INTO `fa_category` VALUES (7, 5, 'page', '企业管理软件', 'company', 'index', '/assets/img/qrcode.png', '', '', 'company', 1491635035, 1491635035, 7, 'normal'); +INSERT INTO `fa_category` VALUES (8, 6, 'page', 'PC端', 'website-pc', 'recommend', '/assets/img/qrcode.png', '', '', 'website-pc', 1491635035, 1491635035, 8, 'normal'); +INSERT INTO `fa_category` VALUES (9, 6, 'page', '移动端', 'website-mobile', 'recommend', '/assets/img/qrcode.png', '', '', 'website-mobile', 1491635035, 1491635035, 9, 'normal'); +INSERT INTO `fa_category` VALUES (10, 7, 'page', 'CRM系统 ', 'company-crm', 'recommend', '/assets/img/qrcode.png', '', '', 'company-crm', 1491635035, 1491635035, 10, 'normal'); +INSERT INTO `fa_category` VALUES (11, 7, 'page', 'SASS平台软件', 'company-sass', 'recommend', '/assets/img/qrcode.png', '', '', 'company-sass', 1491635035, 1491635035, 11, 'normal'); +INSERT INTO `fa_category` VALUES (12, 0, 'test', '测试1', 'test1', 'recommend', '/assets/img/qrcode.png', '', '', 'test1', 1491635035, 1491635035, 12, 'normal'); +INSERT INTO `fa_category` VALUES (13, 0, 'test', '测试2', 'test2', 'recommend', '/assets/img/qrcode.png', '', '', 'test2', 1491635035, 1491635035, 13, 'normal'); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_config +-- ---------------------------- +DROP TABLE IF EXISTS `fa_config`; +CREATE TABLE `fa_config` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(30) DEFAULT '' COMMENT '变量名', + `group` varchar(30) DEFAULT '' COMMENT '分组', + `title` varchar(100) DEFAULT '' COMMENT '变量标题', + `tip` varchar(100) DEFAULT '' COMMENT '变量描述', + `type` varchar(30) DEFAULT '' COMMENT '类型:string,text,int,bool,array,datetime,date,file', + `visible` varchar(255) DEFAULT '' COMMENT '可见条件', + `value` text COMMENT '变量值', + `content` text COMMENT '变量字典数据', + `rule` varchar(100) DEFAULT '' COMMENT '验证规则', + `extend` varchar(255) DEFAULT '' COMMENT '扩展属性', + `setting` varchar(255) DEFAULT '' COMMENT '配置', + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='系统配置'; + +-- ---------------------------- +-- Records of fa_config +-- ---------------------------- +BEGIN; +INSERT INTO `fa_config` VALUES (1, 'name', 'basic', 'Site name', '请填写站点名称', 'string', '', '我的网站', '', 'required', '', ''); +INSERT INTO `fa_config` VALUES (2, 'beian', 'basic', 'Beian', '粤ICP备15000000号-1', 'string', '', '', '', '', '', ''); +INSERT INTO `fa_config` VALUES (3, 'cdnurl', 'basic', 'Cdn url', '如果全站静态资源使用第三方云储存请配置该值', 'string', '', '', '', '', '', ''); +INSERT INTO `fa_config` VALUES (4, 'version', 'basic', 'Version', '如果静态资源有变动请重新配置该值', 'string', '', '1.0.1', '', 'required', '', ''); +INSERT INTO `fa_config` VALUES (5, 'timezone', 'basic', 'Timezone', '', 'string', '', 'Asia/Shanghai', '', 'required', '', ''); +INSERT INTO `fa_config` VALUES (6, 'forbiddenip', 'basic', 'Forbidden ip', '一行一条记录', 'text', '', '', '', '', '', ''); +INSERT INTO `fa_config` VALUES (7, 'languages', 'basic', 'Languages', '', 'array', '', '{\"backend\":\"zh-cn\",\"frontend\":\"zh-cn\"}', '', 'required', '', ''); +INSERT INTO `fa_config` VALUES (8, 'fixedpage', 'basic', 'Fixed page', '请尽量输入左侧菜单栏存在的链接', 'string', '', 'dashboard', '', 'required', '', ''); +INSERT INTO `fa_config` VALUES (9, 'categorytype', 'dictionary', 'Category type', '', 'array', '', '{\"default\":\"Default\",\"page\":\"Page\",\"article\":\"Article\",\"test\":\"Test\"}', '', '', '', ''); +INSERT INTO `fa_config` VALUES (10, 'configgroup', 'dictionary', 'Config group', '', 'array', '', '{\"basic\":\"Basic\",\"email\":\"Email\",\"dictionary\":\"Dictionary\",\"user\":\"User\",\"example\":\"Example\"}', '', '', '', ''); +INSERT INTO `fa_config` VALUES (11, 'mail_type', 'email', 'Mail type', '选择邮件发送方式', 'select', '', '1', '[\"请选择\",\"SMTP\"]', '', '', ''); +INSERT INTO `fa_config` VALUES (12, 'mail_smtp_host', 'email', 'Mail smtp host', '错误的配置发送邮件会导致服务器超时', 'string', '', 'smtp.qq.com', '', '', '', ''); +INSERT INTO `fa_config` VALUES (13, 'mail_smtp_port', 'email', 'Mail smtp port', '(不加密默认25,SSL默认465,TLS默认587)', 'string', '', '465', '', '', '', ''); +INSERT INTO `fa_config` VALUES (14, 'mail_smtp_user', 'email', 'Mail smtp user', '(填写完整用户名)', 'string', '', '10000', '', '', '', ''); +INSERT INTO `fa_config` VALUES (15, 'mail_smtp_pass', 'email', 'Mail smtp password', '(填写您的密码或授权码)', 'string', '', 'password', '', '', '', ''); +INSERT INTO `fa_config` VALUES (16, 'mail_verify_type', 'email', 'Mail vertify type', '(SMTP验证方式[推荐SSL])', 'select', '', '2', '[\"无\",\"TLS\",\"SSL\"]', '', '', ''); +INSERT INTO `fa_config` VALUES (17, 'mail_from', 'email', 'Mail from', '', 'string', '', '10000@qq.com', '', '', '', ''); +INSERT INTO `fa_config` VALUES (18, 'attachmentcategory', 'dictionary', 'Attachment category', '', 'array', '', '{\"category1\":\"Category1\",\"category2\":\"Category2\",\"custom\":\"Custom\"}', '', '', '', ''); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_ems +-- ---------------------------- +DROP TABLE IF EXISTS `fa_ems`; +CREATE TABLE `fa_ems` ( + `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', + `event` varchar(30) DEFAULT '' COMMENT '事件', + `email` varchar(100) DEFAULT '' COMMENT '邮箱', + `code` varchar(10) DEFAULT '' COMMENT '验证码', + `times` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '验证次数', + `ip` varchar(30) DEFAULT '' COMMENT 'IP', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='邮箱验证码表'; + +-- ---------------------------- +-- Table structure for fa_sms +-- ---------------------------- +DROP TABLE IF EXISTS `fa_sms`; +CREATE TABLE `fa_sms` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `event` varchar(30) DEFAULT '' COMMENT '事件', + `mobile` varchar(20) DEFAULT '' COMMENT '手机号', + `code` varchar(10) DEFAULT '' COMMENT '验证码', + `times` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '验证次数', + `ip` varchar(30) DEFAULT '' COMMENT 'IP', + `createtime` bigint(16) unsigned DEFAULT '0' COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='短信验证码表'; + +-- ---------------------------- +-- Table structure for fa_test +-- ---------------------------- +DROP TABLE IF EXISTS `fa_test`; +CREATE TABLE `fa_test` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `user_id` int(10) DEFAULT '0' COMMENT '会员ID', + `admin_id` int(10) DEFAULT '0' COMMENT '管理员ID', + `category_id` int(10) unsigned DEFAULT '0' COMMENT '分类ID(单选)', + `category_ids` varchar(100) COMMENT '分类ID(多选)', + `tags` varchar(255) DEFAULT '' COMMENT '标签', + `week` enum('monday','tuesday','wednesday') COMMENT '星期(单选):monday=星期一,tuesday=星期二,wednesday=星期三', + `flag` set('hot','index','recommend') DEFAULT '' COMMENT '标志(多选):hot=热门,index=首页,recommend=推荐', + `genderdata` enum('male','female') DEFAULT 'male' COMMENT '性别(单选):male=男,female=女', + `hobbydata` set('music','reading','swimming') COMMENT '爱好(多选):music=音乐,reading=读书,swimming=游泳', + `title` varchar(100) DEFAULT '' COMMENT '标题', + `content` text COMMENT '内容', + `image` varchar(100) DEFAULT '' COMMENT '图片', + `images` varchar(1500) DEFAULT '' COMMENT '图片组', + `attachfile` varchar(100) DEFAULT '' COMMENT '附件', + `keywords` varchar(255) DEFAULT '' COMMENT '关键字', + `description` varchar(255) DEFAULT '' COMMENT '描述', + `city` varchar(100) DEFAULT '' COMMENT '省市', + `json` varchar(255) DEFAULT NULL COMMENT '配置:key=名称,value=值', + `multiplejson` varchar(1500) DEFAULT '' COMMENT '二维数组:title=标题,intro=介绍,author=作者,age=年龄', + `price` decimal(10,2) unsigned DEFAULT '0.00' COMMENT '价格', + `views` int(10) unsigned DEFAULT '0' COMMENT '点击', + `workrange` varchar(100) DEFAULT '' COMMENT '时间区间', + `startdate` date DEFAULT NULL COMMENT '开始日期', + `activitytime` datetime DEFAULT NULL COMMENT '活动时间(datetime)', + `year` year(4) DEFAULT NULL COMMENT '年', + `times` time DEFAULT NULL COMMENT '时间', + `refreshtime` bigint(16) DEFAULT NULL COMMENT '刷新时间', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `deletetime` bigint(16) DEFAULT NULL COMMENT '删除时间', + `weigh` int(10) DEFAULT '0' COMMENT '权重', + `switch` tinyint(1) DEFAULT '0' COMMENT '开关', + `status` enum('normal','hidden') DEFAULT 'normal' COMMENT '状态', + `state` enum('0','1','2') DEFAULT '1' COMMENT '状态值:0=禁用,1=正常,2=推荐', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='测试表'; + +-- ---------------------------- +-- Records of fa_test +-- ---------------------------- +BEGIN; +INSERT INTO `fa_test` VALUES (1, 1, 1, 12, '12,13', '互联网,计算机', 'monday', 'hot,index', 'male', 'music,reading', '我是一篇测试文章', '

    我是测试内容

    ', '/assets/img/avatar.png', '/assets/img/avatar.png,/assets/img/qrcode.png', '/assets/img/avatar.png', '关键字', '描述', '广西壮族自治区/百色市/平果县', '{\"a\":\"1\",\"b\":\"2\"}', '[{\"title\":\"标题一\",\"intro\":\"介绍一\",\"author\":\"小明\",\"age\":\"21\"}]', 0.00, 0, '2020-10-01 00:00:00 - 2021-10-31 23:59:59', '2017-07-10', '2017-07-10 18:24:45', 2017, '18:24:45', 1491635035, 1491635035, 1491635035, NULL, 0, 1, 'normal', '1'); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_user +-- ---------------------------- +DROP TABLE IF EXISTS `fa_user`; +CREATE TABLE `fa_user` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `group_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '组别ID', + `username` varchar(32) DEFAULT '' COMMENT '用户名', + `nickname` varchar(50) DEFAULT '' COMMENT '昵称', + `password` varchar(32) DEFAULT '' COMMENT '密码', + `salt` varchar(30) DEFAULT '' COMMENT '密码盐', + `email` varchar(100) DEFAULT '' COMMENT '电子邮箱', + `mobile` varchar(11) DEFAULT '' COMMENT '手机号', + `avatar` varchar(255) DEFAULT '' COMMENT '头像', + `level` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '等级', + `gender` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '性别', + `birthday` date DEFAULT NULL COMMENT '生日', + `bio` varchar(100) DEFAULT '' COMMENT '格言', + `money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '余额', + `score` int(10) NOT NULL DEFAULT '0' COMMENT '积分', + `successions` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '连续登录天数', + `maxsuccessions` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '最大连续登录天数', + `prevtime` bigint(16) DEFAULT NULL COMMENT '上次登录时间', + `logintime` bigint(16) DEFAULT NULL COMMENT '登录时间', + `loginip` varchar(50) DEFAULT '' COMMENT '登录IP', + `loginfailure` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '失败次数', + `joinip` varchar(50) DEFAULT '' COMMENT '加入IP', + `jointime` bigint(16) DEFAULT NULL COMMENT '加入时间', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `token` varchar(50) DEFAULT '' COMMENT 'Token', + `status` varchar(30) DEFAULT '' COMMENT '状态', + `verification` varchar(255) DEFAULT '' COMMENT '验证', + PRIMARY KEY (`id`), + KEY `username` (`username`), + KEY `email` (`email`), + KEY `mobile` (`mobile`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员表'; + +-- ---------------------------- +-- Records of fa_user +-- ---------------------------- +BEGIN; +INSERT INTO `fa_user` VALUES (1, 1, 'admin', 'admin', '', '', 'admin@163.com', '13888888888', '', 0, 0, '2017-04-08', '', 0, 0, 1, 1, 1491635035, 1491635035, '127.0.0.1', 0, '127.0.0.1', 1491635035, 0, 1491635035, '', 'normal',''); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_user_group +-- ---------------------------- +DROP TABLE IF EXISTS `fa_user_group`; +CREATE TABLE `fa_user_group` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) DEFAULT '' COMMENT '组名', + `rules` text COMMENT '权限节点', + `createtime` bigint(16) DEFAULT NULL COMMENT '添加时间', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `status` enum('normal','hidden') DEFAULT NULL COMMENT '状态', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员组表'; + +-- ---------------------------- +-- Records of fa_user_group +-- ---------------------------- +BEGIN; +INSERT INTO `fa_user_group` VALUES (1, '默认组', '1,2,3,4,5,6,7,8,9,10,11,12', 1491635035, 1491635035, 'normal'); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_user_money_log +-- ---------------------------- +DROP TABLE IF EXISTS `fa_user_money_log`; +CREATE TABLE `fa_user_money_log` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', + `money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变更余额', + `before` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变更前余额', + `after` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '变更后余额', + `memo` varchar(255) DEFAULT '' COMMENT '备注', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员余额变动表'; + +-- ---------------------------- +-- Table structure for fa_user_rule +-- ---------------------------- +DROP TABLE IF EXISTS `fa_user_rule`; +CREATE TABLE `fa_user_rule` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `pid` int(10) DEFAULT NULL COMMENT '父ID', + `name` varchar(50) DEFAULT NULL COMMENT '名称', + `title` varchar(50) DEFAULT '' COMMENT '标题', + `remark` varchar(100) DEFAULT NULL COMMENT '备注', + `ismenu` tinyint(1) DEFAULT NULL COMMENT '是否菜单', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `weigh` int(10) DEFAULT '0' COMMENT '权重', + `status` enum('normal','hidden') DEFAULT NULL COMMENT '状态', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员规则表'; + +-- ---------------------------- +-- Records of fa_user_rule +-- ---------------------------- +BEGIN; +INSERT INTO `fa_user_rule` VALUES (1, 0, 'index', 'Frontend', '', 1, 1491635035, 1491635035, 1, 'normal'); +INSERT INTO `fa_user_rule` VALUES (2, 0, 'api', 'API Interface', '', 1, 1491635035, 1491635035, 2, 'normal'); +INSERT INTO `fa_user_rule` VALUES (3, 1, 'user', 'User Module', '', 1, 1491635035, 1491635035, 12, 'normal'); +INSERT INTO `fa_user_rule` VALUES (4, 2, 'user', 'User Module', '', 1, 1491635035, 1491635035, 11, 'normal'); +INSERT INTO `fa_user_rule` VALUES (5, 3, 'index/user/login', 'Login', '', 0, 1491635035, 1491635035, 5, 'normal'); +INSERT INTO `fa_user_rule` VALUES (6, 3, 'index/user/register', 'Register', '', 0, 1491635035, 1491635035, 7, 'normal'); +INSERT INTO `fa_user_rule` VALUES (7, 3, 'index/user/index', 'User Center', '', 0, 1491635035, 1491635035, 9, 'normal'); +INSERT INTO `fa_user_rule` VALUES (8, 3, 'index/user/profile', 'Profile', '', 0, 1491635035, 1491635035, 4, 'normal'); +INSERT INTO `fa_user_rule` VALUES (9, 4, 'api/user/login', 'Login', '', 0, 1491635035, 1491635035, 6, 'normal'); +INSERT INTO `fa_user_rule` VALUES (10, 4, 'api/user/register', 'Register', '', 0, 1491635035, 1491635035, 8, 'normal'); +INSERT INTO `fa_user_rule` VALUES (11, 4, 'api/user/index', 'User Center', '', 0, 1491635035, 1491635035, 10, 'normal'); +INSERT INTO `fa_user_rule` VALUES (12, 4, 'api/user/profile', 'Profile', '', 0, 1491635035, 1491635035, 3, 'normal'); +COMMIT; + +-- ---------------------------- +-- Table structure for fa_user_score_log +-- ---------------------------- +DROP TABLE IF EXISTS `fa_user_score_log`; +CREATE TABLE `fa_user_score_log` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', + `score` int(10) NOT NULL DEFAULT '0' COMMENT '变更积分', + `before` int(10) NOT NULL DEFAULT '0' COMMENT '变更前积分', + `after` int(10) NOT NULL DEFAULT '0' COMMENT '变更后积分', + `memo` varchar(255) DEFAULT '' COMMENT '备注', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员积分变动表'; + +-- ---------------------------- +-- Table structure for fa_user_token +-- ---------------------------- +DROP TABLE IF EXISTS `fa_user_token`; +CREATE TABLE `fa_user_token` ( + `token` varchar(50) NOT NULL COMMENT 'Token', + `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + `expiretime` bigint(16) DEFAULT NULL COMMENT '过期时间', + PRIMARY KEY (`token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='会员Token表'; + +-- ---------------------------- +-- Table structure for fa_version +-- ---------------------------- +DROP TABLE IF EXISTS `fa_version`; +CREATE TABLE `fa_version` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `oldversion` varchar(30) DEFAULT '' COMMENT '旧版本号', + `newversion` varchar(30) DEFAULT '' COMMENT '新版本号', + `packagesize` varchar(30) DEFAULT '' COMMENT '包大小', + `content` varchar(500) DEFAULT '' COMMENT '升级内容', + `downloadurl` varchar(255) DEFAULT '' COMMENT '下载地址', + `enforce` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '强制更新', + `createtime` bigint(16) DEFAULT NULL COMMENT '创建时间', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `weigh` int(10) NOT NULL DEFAULT 0 COMMENT '权重', + `status` varchar(30) DEFAULT '' COMMENT '状态', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='版本表'; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/application/admin/command/Install/install.html b/application/admin/command/Install/install.html new file mode 100644 index 0000000..1381c80 --- /dev/null +++ b/application/admin/command/Install/install.html @@ -0,0 +1,316 @@ + + + + + + {:__('Installing FastAdmin')} + + + + + + + +
    +

    + + + +

    +

    {:__('Installing FastAdmin')}

    +
    + +
    + {if $errInfo} +
    + {$errInfo} +
    + {/if} + + + + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + +
    + + + +
    +
    + + + + + +
    +
    + + diff --git a/application/admin/command/Install/install.lock b/application/admin/command/Install/install.lock new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/application/admin/command/Install/install.lock @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/application/admin/command/Install/zh-cn.php b/application/admin/command/Install/zh-cn.php new file mode 100644 index 0000000..46326bb --- /dev/null +++ b/application/admin/command/Install/zh-cn.php @@ -0,0 +1,35 @@ + '温馨提示', + 'Installing FastAdmin' => '安装FastAdmin', + 'Mysql Hostname' => 'MySQL 数据库地址', + 'Mysql Database' => 'MySQL 数据库名', + 'Mysql Username' => 'MySQL 用户名', + 'Mysql Password' => 'MySQL 密码', + 'Mysql Prefix' => 'MySQL 数据表前缀', + 'Mysql Hostport' => 'MySQL 端口号', + 'Admin Username' => '管理员用户名', + 'Admin Email' => '管理员Email', + 'Admin Password' => '管理员密码', + 'Repeat Password' => '重复管理员密码', + 'Website' => '网站名称', + 'My Website' => '我的网站', + 'Install now' => '点击安装', + 'Installing' => '安装中...', + 'Home' => '访问首页', + 'Dashboard' => '进入后台', + 'Go back' => '返回上一页', + 'Install Successed' => '安装成功!', + 'Security tips' => '温馨提示:请将以下后台登录入口添加到你的收藏夹,为了你的安全,不要泄漏或发送给他人!如有泄漏请及时修改!', + 'Please input correct database' => '请输入正确的数据库名', + 'Please input correct username' => '用户名只能由3-30位数字、字母、下划线组合', + 'Please input correct password' => '密码长度必须在6-30位之间,不能包含空格', + 'Password is too weak' => '密码太简单,请重新输入', + 'The two passwords you entered did not match' => '两次输入的密码不一致', + 'Please input correct website' => '网站名称输入不正确', + 'The current version %s is too low, please use PHP 7.1 or higher' => '当前版本%s过低,请使用PHP7.1以上版本', + 'PDO is not currently installed and cannot be installed' => '当前未开启PDO,无法进行安装', + 'The current permissions are insufficient to write the file %s' => '当前权限不足,无法写入文件%s', + 'Please go to the official website to download the full package or resource package and try to install' => '当前代码仅包含核心代码,请前往官网下载完整包或资源包覆盖后再尝试安装', + 'The system has been installed. If you need to reinstall, please remove %s first' => '当前已经安装成功,如果需要重新安装,请手动移除%s文件', +]; diff --git a/application/admin/command/Menu.php b/application/admin/command/Menu.php new file mode 100644 index 0000000..cc21f3b --- /dev/null +++ b/application/admin/command/Menu.php @@ -0,0 +1,327 @@ +setName('menu') + ->addOption('controller', 'c', Option::VALUE_REQUIRED | Option::VALUE_IS_ARRAY, 'controller name,use \'all-controller\' when build all menu', null) + ->addOption('delete', 'd', Option::VALUE_OPTIONAL, 'delete the specified menu', '') + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force delete menu,without tips', null) + ->addOption('equal', 'e', Option::VALUE_OPTIONAL, 'the controller must be equal', null) + ->setDescription('Build auth menu from controller'); + //要执行的controller必须一样,不适用模糊查询 + } + + protected function execute(Input $input, Output $output) + { + $this->model = new AuthRule(); + $adminPath = dirname(__DIR__) . DS; + //控制器名 + $controller = $input->getOption('controller') ?: ''; + if (!$controller) { + throw new Exception("please input controller name"); + } + $force = $input->getOption('force'); + //是否为删除模式 + $delete = $input->getOption('delete'); + //是否控制器完全匹配 + $equal = $input->getOption('equal'); + + + if ($delete) { + if (in_array('all-controller', $controller)) { + throw new Exception("could not delete all menu"); + } + $ids = []; + $list = $this->model->where(function ($query) use ($controller, $equal) { + foreach ($controller as $index => $item) { + if (stripos($item, '_') !== false) { + $item = Loader::parseName($item, 1); + } + if (stripos($item, '/') !== false) { + $controllerArr = explode('/', $item); + end($controllerArr); + $key = key($controllerArr); + $controllerArr[$key] = Loader::parseName($controllerArr[$key]); + } else { + $controllerArr = [Loader::parseName($item)]; + } + $item = str_replace('_', '\_', implode('/', $controllerArr)); + if ($equal) { + $query->whereOr('name', 'eq', $item); + } else { + $query->whereOr('name', 'like', strtolower($item) . "%"); + } + } + })->select(); + foreach ($list as $k => $v) { + $output->warning($v->name); + $ids[] = $v->id; + } + if (!$ids) { + throw new Exception("There is no menu to delete"); + } + if (!$force) { + $output->info("Are you sure you want to delete all those menu? Type 'yes' to continue: "); + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); + if (trim($line) != 'yes') { + throw new Exception("Operation is aborted!"); + } + } + AuthRule::destroy($ids); + + Cache::rm("__menu__"); + $output->info("Delete Successed"); + return; + } + + if (!in_array('all-controller', $controller)) { + foreach ($controller as $index => $item) { + if (stripos($item, '_') !== false) { + $item = Loader::parseName($item, 1); + } + if (stripos($item, '/') !== false) { + $controllerArr = explode('/', $item); + end($controllerArr); + $key = key($controllerArr); + $controllerArr[$key] = ucfirst($controllerArr[$key]); + } else { + $controllerArr = [ucfirst($item)]; + } + $adminPath = dirname(__DIR__) . DS . 'controller' . DS . implode(DS, $controllerArr) . '.php'; + if (!is_file($adminPath)) { + $output->error("controller not found"); + return; + } + $this->importRule($item); + } + } else { + $authRuleList = AuthRule::select(); + //生成权限规则备份文件 + file_put_contents(RUNTIME_PATH . 'authrule.json', json_encode(collection($authRuleList)->toArray())); + + $this->model->where('id', '>', 0)->delete(); + $controllerDir = $adminPath . 'controller' . DS; + // 扫描新的节点信息并导入 + $treelist = $this->import($this->scandir($controllerDir)); + } + Cache::rm("__menu__"); + $output->info("Build Successed!"); + } + + /** + * 递归扫描文件夹 + * @param string $dir + * @return array + */ + public function scandir($dir) + { + $result = []; + $cdir = scandir($dir); + foreach ($cdir as $value) { + if (!in_array($value, array(".", ".."))) { + if (is_dir($dir . DS . $value)) { + $result[$value] = $this->scandir($dir . DS . $value); + } else { + $result[] = $value; + } + } + } + return $result; + } + + /** + * 导入规则节点 + * @param array $dirarr + * @param array $parentdir + * @return array + */ + public function import($dirarr, $parentdir = []) + { + $menuarr = []; + foreach ($dirarr as $k => $v) { + if (is_array($v)) { + //当前是文件夹 + $nowparentdir = array_merge($parentdir, [$k]); + $this->import($v, $nowparentdir); + } else { + //只匹配PHP文件 + if (!preg_match('/^(\w+)\.php$/', $v, $matchone)) { + continue; + } + //导入文件 + $controller = ($parentdir ? implode('/', $parentdir) . '/' : '') . $matchone[1]; + $this->importRule($controller); + } + } + + return $menuarr; + } + + protected function importRule($controller) + { + $controller = str_replace('\\', '/', $controller); + if (stripos($controller, '/') !== false) { + $controllerArr = explode('/', $controller); + end($controllerArr); + $key = key($controllerArr); + $controllerArr[$key] = ucfirst($controllerArr[$key]); + } else { + $key = 0; + $controllerArr = [ucfirst($controller)]; + } + $classSuffix = Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : ''; + $className = "\\app\\admin\\controller\\" . implode("\\", $controllerArr) . $classSuffix; + + $pathArr = $controllerArr; + array_unshift($pathArr, '', 'application', 'admin', 'controller'); + $classFile = ROOT_PATH . implode(DS, $pathArr) . $classSuffix . ".php"; + $classContent = file_get_contents($classFile); + $uniqueName = uniqid("FastAdmin") . $classSuffix; + $classContent = str_replace("class " . $controllerArr[$key] . $classSuffix . " ", 'class ' . $uniqueName . ' ', $classContent); + $classContent = preg_replace("/namespace\s(.*);/", 'namespace ' . __NAMESPACE__ . ";", $classContent); + + //临时的类文件 + $tempClassFile = __DIR__ . DS . $uniqueName . ".php"; + file_put_contents($tempClassFile, $classContent); + $className = "\\app\\admin\\command\\" . $uniqueName; + + //删除临时文件 + register_shutdown_function(function () use ($tempClassFile) { + if ($tempClassFile) { + //删除临时文件 + @unlink($tempClassFile); + } + }); + + //反射机制调用类的注释和方法名 + $reflector = new ReflectionClass($className); + + //只匹配公共的方法 + $methods = $reflector->getMethods(ReflectionMethod::IS_PUBLIC); + $classComment = $reflector->getDocComment(); + //判断是否有启用软删除 + $softDeleteMethods = ['destroy', 'restore', 'recyclebin']; + $withSofeDelete = false; + $modelRegexArr = ["/\\\$this\->model\s*=\s*model\(['|\"](\w+)['|\"]\);/", "/\\\$this\->model\s*=\s*new\s+([a-zA-Z\\\]+);/"]; + $modelRegex = preg_match($modelRegexArr[0], $classContent) ? $modelRegexArr[0] : $modelRegexArr[1]; + preg_match_all($modelRegex, $classContent, $matches); + if (isset($matches[1]) && isset($matches[1][0]) && $matches[1][0]) { + \think\Request::instance()->module('admin'); + $model = model($matches[1][0]); + if (in_array('trashed', get_class_methods($model))) { + $withSofeDelete = true; + } + } + //忽略的类 + if (stripos($classComment, "@internal") !== false) { + return; + } + preg_match_all('#(@.*?)\n#s', $classComment, $annotations); + $controllerIcon = 'fa fa-circle-o'; + $controllerRemark = ''; + //判断注释中是否设置了icon值 + if (isset($annotations[1])) { + foreach ($annotations[1] as $tag) { + if (stripos($tag, '@icon') !== false) { + $controllerIcon = substr($tag, stripos($tag, ' ') + 1); + } + if (stripos($tag, '@remark') !== false) { + $controllerRemark = substr($tag, stripos($tag, ' ') + 1); + } + } + } + //过滤掉其它字符 + $controllerTitle = trim(preg_replace(array('/^\/\*\*(.*)[\n\r\t]/u', '/[\s]+\*\//u', '/\*\s@(.*)/u', '/[\s|\*]+/u'), '', $classComment)); + + //导入中文语言包 + \think\Lang::load(dirname(__DIR__) . DS . 'lang/zh-cn.php'); + + //先导入菜单的数据 + $pid = 0; + foreach ($controllerArr as $k => $v) { + $key = $k + 1; + //驼峰转下划线 + $controllerNameArr = array_slice($controllerArr, 0, $key); + foreach ($controllerNameArr as &$val) { + $val = strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $val), "_")); + } + unset($val); + $name = implode('/', $controllerNameArr); + $title = (!isset($controllerArr[$key]) ? $controllerTitle : ''); + $icon = (!isset($controllerArr[$key]) ? $controllerIcon : 'fa fa-list'); + $remark = (!isset($controllerArr[$key]) ? $controllerRemark : ''); + $title = $title ? $title : $v; + $rulemodel = $this->model->get(['name' => $name]); + if (!$rulemodel) { + $this->model + ->data(['pid' => $pid, 'name' => $name, 'title' => $title, 'icon' => $icon, 'remark' => $remark, 'ismenu' => 1, 'status' => 'normal']) + ->isUpdate(false) + ->save(); + $pid = $this->model->id; + } else { + $pid = $rulemodel->id; + } + } + $ruleArr = []; + foreach ($methods as $m => $n) { + //过滤特殊的类 + if (substr($n->name, 0, 2) == '__' || $n->name == '_initialize') { + continue; + } + //未启用软删除时过滤相关方法 + if (!$withSofeDelete && in_array($n->name, $softDeleteMethods)) { + continue; + } + //只匹配符合的方法 + if (!preg_match('/^(\w+)' . Config::get('action_suffix') . '/', $n->name, $matchtwo)) { + unset($methods[$m]); + continue; + } + $comment = $reflector->getMethod($n->name)->getDocComment(); + //忽略的方法 + if (stripos($comment, "@internal") !== false) { + continue; + } + //过滤掉其它字符 + $comment = preg_replace(array('/^\/\*\*(.*)[\n\r\t]/u', '/[\s]+\*\//u', '/\*\s@(.*)/u', '/[\s|\*]+/u'), '', $comment); + + $title = $comment ? $comment : ucfirst($n->name); + + //获取主键,作为AuthRule更新依据 + $id = $this->getAuthRulePK($name . "/" . strtolower($n->name)); + + $ruleArr[] = array('id' => $id, 'pid' => $pid, 'name' => $name . "/" . strtolower($n->name), 'icon' => 'fa fa-circle-o', 'title' => $title, 'ismenu' => 0, 'status' => 'normal'); + } + $this->model->isUpdate(false)->saveAll($ruleArr); + } + + //获取主键 + protected function getAuthRulePK($name) + { + if (!empty($name)) { + $id = $this->model + ->where('name', $name) + ->value('id'); + return $id ? $id : null; + } + } +} diff --git a/application/admin/command/Min.php b/application/admin/command/Min.php new file mode 100644 index 0000000..afc8164 --- /dev/null +++ b/application/admin/command/Min.php @@ -0,0 +1,162 @@ + 'public/assets/css/', + 'cssBaseName' => '{module}', + 'jsBaseUrl' => 'public/assets/js/', + 'jsBaseName' => 'require-{module}', + ]; + + protected function configure() + { + $this + ->setName('min') + ->addOption('module', 'm', Option::VALUE_REQUIRED, 'module name(frontend or backend),use \'all\' when build all modules', null) + ->addOption('resource', 'r', Option::VALUE_REQUIRED, 'resource name(js or css),use \'all\' when build all resources', null) + ->addOption('optimize', 'o', Option::VALUE_OPTIONAL, 'optimize type(uglify|closure|none)', 'none') + ->setDescription('Compress js and css file'); + } + + protected function execute(Input $input, Output $output) + { + $module = $input->getOption('module') ?: ''; + $resource = $input->getOption('resource') ?: ''; + $optimize = $input->getOption('optimize') ?: 'none'; + + if (!$module || !in_array($module, ['frontend', 'backend', 'all'])) { + throw new Exception('Please input correct module name'); + } + if (!$resource || !in_array($resource, ['js', 'css', 'all'])) { + throw new Exception('Please input correct resource name'); + } + + $moduleArr = $module == 'all' ? ['frontend', 'backend'] : [$module]; + $resourceArr = $resource == 'all' ? ['js', 'css'] : [$resource]; + + $minPath = __DIR__ . DS . 'Min' . DS; + $publicPath = ROOT_PATH . 'public' . DS; + $tempFile = $minPath . 'temp.js'; + + $nodeExec = ''; + + if (!$nodeExec) { + if (IS_WIN) { + // Winsows下请手动配置配置该值,一般将该值配置为 '"C:\Program Files\nodejs\node.exe"',除非你的Node安装路径有变更 + $nodeExec = 'C:\Program Files\nodejs\node.exe'; + if (file_exists($nodeExec)) { + $nodeExec = '"' . $nodeExec . '"'; + } else { + // 如果 '"C:\Program Files\nodejs\node.exe"' 不存在,可能是node安装路径有变更 + // 但安装node会自动配置环境变量,直接执行 '"node.exe"' 提高第一次使用压缩打包的成功率 + $nodeExec = '"node.exe"'; + } + } else { + try { + $nodeExec = exec("which node"); + if (!$nodeExec) { + throw new Exception("node environment not found!please install node first!"); + } + } catch (Exception $e) { + throw new Exception($e->getMessage()); + } + } + } + + foreach ($moduleArr as $mod) { + foreach ($resourceArr as $res) { + $data = [ + 'publicPath' => $publicPath, + 'jsBaseName' => str_replace('{module}', $mod, $this->options['jsBaseName']), + 'jsBaseUrl' => $this->options['jsBaseUrl'], + 'cssBaseName' => str_replace('{module}', $mod, $this->options['cssBaseName']), + 'cssBaseUrl' => $this->options['cssBaseUrl'], + 'jsBasePath' => str_replace(DS, '/', ROOT_PATH . $this->options['jsBaseUrl']), + 'cssBasePath' => str_replace(DS, '/', ROOT_PATH . $this->options['cssBaseUrl']), + 'optimize' => $optimize, + 'ds' => DS, + ]; + + //源文件 + $from = $data["{$res}BasePath"] . $data["{$res}BaseName"] . '.' . $res; + if (!is_file($from)) { + $output->error("{$res} source file not found!file:{$from}"); + continue; + } + if ($res == "js") { + $content = file_get_contents($from); + preg_match("/require\.config\(\{[\r\n]?[\n]?+(.*?)[\r\n]?[\n]?}\);/is", $content, $matches); + if (!isset($matches[1])) { + $output->error("js config not found!"); + continue; + } + $config = preg_replace("/(urlArgs|baseUrl):(.*)\n/", '', $matches[1]); + $data['config'] = $config; + } + // 生成压缩文件 + $this->writeToFile($res, $data, $tempFile); + + $output->info("Compress " . $data["{$res}BaseName"] . ".{$res}"); + + // 执行压缩 + $command = "{$nodeExec} \"{$minPath}r.js\" -o \"{$tempFile}\" >> \"{$minPath}node.log\""; + if ($output->isDebug()) { + $output->warning($command); + } + echo exec($command); + } + } + + if (!$output->isDebug()) { + @unlink($tempFile); + } + + $output->info("Build Successed!"); + } + + /** + * 写入到文件 + * @param string $name + * @param array $data + * @param string $pathname + * @return mixed + */ + protected function writeToFile($name, $data, $pathname) + { + $search = $replace = []; + foreach ($data as $k => $v) { + $search[] = "{%{$k}%}"; + $replace[] = $v; + } + $stub = file_get_contents($this->getStub($name)); + $content = str_replace($search, $replace, $stub); + + if (!is_dir(dirname($pathname))) { + mkdir(strtolower(dirname($pathname)), 0755, true); + } + return file_put_contents($pathname, $content); + } + + /** + * 获取基础模板 + * @param string $name + * @return string + */ + protected function getStub($name) + { + return __DIR__ . DS . 'Min' . DS . 'stubs' . DS . $name . '.stub'; + } +} diff --git a/application/admin/command/Min/r.js b/application/admin/command/Min/r.js new file mode 100644 index 0000000..e6c31b8 --- /dev/null +++ b/application/admin/command/Min/r.js @@ -0,0 +1,27959 @@ +/** + * @license r.js 2.3.2 Copyright jQuery Foundation and other contributors. + * Released under MIT license, http://github.com/requirejs/r.js/LICENSE + */ + +/* + * This is a bootstrap script to allow running RequireJS in the command line + * in either a Java/Rhino or Node environment. It is modified by the top-level + * dist.js file to inject other files to completely enable this file. It is + * the shell of the r.js file. + */ + +/*jslint evil: true, nomen: true, sloppy: true */ +/*global readFile: true, process: false, Packages: false, print: false, +console: false, java: false, module: false, requirejsVars, navigator, +document, importScripts, self, location, Components, FileUtils */ + +var requirejs, require, define, xpcUtil; +(function (console, args, readFileFunc) { + var fileName, env, fs, vm, path, exec, rhinoContext, dir, nodeRequire, + nodeDefine, exists, reqMain, loadedOptimizedLib, existsForNode, Cc, Ci, + version = '2.3.2', + jsSuffixRegExp = /\.js$/, + commandOption = '', + useLibLoaded = {}, + //Used by jslib/rhino/args.js + rhinoArgs = args, + //Used by jslib/xpconnect/args.js + xpconnectArgs = args, + readFile = typeof readFileFunc !== 'undefined' ? readFileFunc : null; + + function showHelp() { + console.log('See https://github.com/requirejs/r.js for usage.'); + } + + if (typeof process !== 'undefined' && process.versions && !!process.versions.node) { + env = 'node'; + + //Get the fs module via Node's require before it + //gets replaced. Used in require/node.js + fs = require('fs'); + vm = require('vm'); + path = require('path'); + //In Node 0.7+ existsSync is on fs. + existsForNode = fs.existsSync || path.existsSync; + + nodeRequire = require; + nodeDefine = define; + reqMain = require.main; + + //Temporarily hide require and define to allow require.js to define + //them. + require = undefined; + define = undefined; + + readFile = function (path) { + return fs.readFileSync(path, 'utf8'); + }; + + exec = function (string, name) { + return vm.runInThisContext(this.requirejsVars.require.makeNodeWrapper(string), + name ? fs.realpathSync(name) : ''); + }; + + exists = function (fileName) { + return existsForNode(fileName); + }; + + + fileName = process.argv[2]; + + if (fileName && fileName.indexOf('-') === 0) { + commandOption = fileName.substring(1); + fileName = process.argv[3]; + } + } else if (typeof Packages !== 'undefined') { + env = 'rhino'; + + fileName = args[0]; + + if (fileName && fileName.indexOf('-') === 0) { + commandOption = fileName.substring(1); + fileName = args[1]; + } + + //Exec/readFile differs between Rhino and Nashorn. Rhino has an + //importPackage where Nashorn does not, so branch on that. This is a + //coarser check -- detecting readFile existence might also be enough for + //this spot. However, sticking with importPackage to keep it the same + //as other Rhino/Nashorn detection branches. + if (typeof importPackage !== 'undefined') { + rhinoContext = Packages.org.mozilla.javascript.ContextFactory.getGlobal().enterContext(); + + exec = function (string, name) { + return rhinoContext.evaluateString(this, string, name, 0, null); + }; + } else { + exec = function (string, name) { + load({ script: string, name: name}); + }; + readFile = readFully; + } + + exists = function (fileName) { + return (new java.io.File(fileName)).exists(); + }; + + //Define a console.log for easier logging. Don't + //get fancy though. + if (typeof console === 'undefined') { + console = { + log: function () { + print.apply(undefined, arguments); + } + }; + } + } else if ((typeof navigator !== 'undefined' && typeof document !== 'undefined') || + (typeof importScripts !== 'undefined' && typeof self !== 'undefined')) { + env = 'browser'; + + readFile = function (path) { + return fs.readFileSync(path, 'utf8'); + }; + + exec = function (string) { + return eval(string); + }; + + exists = function () { + console.log('x.js exists not applicable in browser env'); + return false; + }; + + } else if (typeof Components !== 'undefined' && Components.classes && Components.interfaces) { + env = 'xpconnect'; + + Components.utils['import']('resource://gre/modules/FileUtils.jsm'); + Cc = Components.classes; + Ci = Components.interfaces; + + fileName = args[0]; + + if (fileName && fileName.indexOf('-') === 0) { + commandOption = fileName.substring(1); + fileName = args[1]; + } + + xpcUtil = { + isWindows: ('@mozilla.org/windows-registry-key;1' in Cc), + cwd: function () { + return FileUtils.getFile("CurWorkD", []).path; + }, + + //Remove . and .. from paths, normalize on front slashes + normalize: function (path) { + //There has to be an easier way to do this. + var i, part, ary, + firstChar = path.charAt(0); + + if (firstChar !== '/' && + firstChar !== '\\' && + path.indexOf(':') === -1) { + //A relative path. Use the current working directory. + path = xpcUtil.cwd() + '/' + path; + } + + ary = path.replace(/\\/g, '/').split('/'); + + for (i = 0; i < ary.length; i += 1) { + part = ary[i]; + if (part === '.') { + ary.splice(i, 1); + i -= 1; + } else if (part === '..') { + ary.splice(i - 1, 2); + i -= 2; + } + } + return ary.join('/'); + }, + + xpfile: function (path) { + var fullPath; + try { + fullPath = xpcUtil.normalize(path); + if (xpcUtil.isWindows) { + fullPath = fullPath.replace(/\//g, '\\'); + } + return new FileUtils.File(fullPath); + } catch (e) { + throw new Error((fullPath || path) + ' failed: ' + e); + } + }, + + readFile: function (/*String*/path, /*String?*/encoding) { + //A file read function that can deal with BOMs + encoding = encoding || "utf-8"; + + var inStream, convertStream, + readData = {}, + fileObj = xpcUtil.xpfile(path); + + //XPCOM, you so crazy + try { + inStream = Cc['@mozilla.org/network/file-input-stream;1'] + .createInstance(Ci.nsIFileInputStream); + inStream.init(fileObj, 1, 0, false); + + convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] + .createInstance(Ci.nsIConverterInputStream); + convertStream.init(inStream, encoding, inStream.available(), + Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + + convertStream.readString(inStream.available(), readData); + return readData.value; + } catch (e) { + throw new Error((fileObj && fileObj.path || '') + ': ' + e); + } finally { + if (convertStream) { + convertStream.close(); + } + if (inStream) { + inStream.close(); + } + } + } + }; + + readFile = xpcUtil.readFile; + + exec = function (string) { + return eval(string); + }; + + exists = function (fileName) { + return xpcUtil.xpfile(fileName).exists(); + }; + + //Define a console.log for easier logging. Don't + //get fancy though. + if (typeof console === 'undefined') { + console = { + log: function () { + print.apply(undefined, arguments); + } + }; + } + } + + /** vim: et:ts=4:sw=4:sts=4 + * @license RequireJS 2.3.2 Copyright jQuery Foundation and other contributors. + * Released under MIT license, https://github.com/requirejs/requirejs/blob/master/LICENSE + */ +//Not using strict: uneven strict support in browsers, #392, and causes +//problems with requirejs.exec()/transpiler plugins that may not be strict. +/*jslint regexp: true, nomen: true, sloppy: true */ +/*global window, navigator, document, importScripts, setTimeout, opera */ + + +(function (global, setTimeout) { + var req, s, head, baseElement, dataMain, src, + interactiveScript, currentlyAddingScript, mainScript, subPath, + version = '2.3.2', + commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/mg, + cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, + jsSuffixRegExp = /\.js$/, + currDirRegExp = /^\.\//, + op = Object.prototype, + ostring = op.toString, + hasOwn = op.hasOwnProperty, + isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), + isWebWorker = !isBrowser && typeof importScripts !== 'undefined', + //PS3 indicates loaded and complete, but need to wait for complete + //specifically. Sequence is 'loading', 'loaded', execution, + // then 'complete'. The UA check is unfortunate, but not sure how + //to feature test w/o causing perf issues. + readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? + /^complete$/ : /^(complete|loaded)$/, + defContextName = '_', + //Oh the tragedy, detecting opera. See the usage of isOpera for reason. + isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', + contexts = {}, + cfg = {}, + globalDefQueue = [], + useInteractive = false; + + //Could match something like ')//comment', do not lose the prefix to comment. + function commentReplace(match, singlePrefix) { + return singlePrefix || ''; + } + + function isFunction(it) { + return ostring.call(it) === '[object Function]'; + } + + function isArray(it) { + return ostring.call(it) === '[object Array]'; + } + + /** + * Helper function for iterating over an array. If the func returns + * a true value, it will break out of the loop. + */ + function each(ary, func) { + if (ary) { + var i; + for (i = 0; i < ary.length; i += 1) { + if (ary[i] && func(ary[i], i, ary)) { + break; + } + } + } + } + + /** + * Helper function for iterating over an array backwards. If the func + * returns a true value, it will break out of the loop. + */ + function eachReverse(ary, func) { + if (ary) { + var i; + for (i = ary.length - 1; i > -1; i -= 1) { + if (ary[i] && func(ary[i], i, ary)) { + break; + } + } + } + } + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + function getOwn(obj, prop) { + return hasProp(obj, prop) && obj[prop]; + } + + /** + * Cycles over properties in an object and calls a function for each + * property value. If the function returns a truthy value, then the + * iteration is stopped. + */ + function eachProp(obj, func) { + var prop; + for (prop in obj) { + if (hasProp(obj, prop)) { + if (func(obj[prop], prop)) { + break; + } + } + } + } + + /** + * Simple function to mix in properties from source into target, + * but only if target does not already have a property of the same name. + */ + function mixin(target, source, force, deepStringMixin) { + if (source) { + eachProp(source, function (value, prop) { + if (force || !hasProp(target, prop)) { + if (deepStringMixin && typeof value === 'object' && value && + !isArray(value) && !isFunction(value) && + !(value instanceof RegExp)) { + + if (!target[prop]) { + target[prop] = {}; + } + mixin(target[prop], value, force, deepStringMixin); + } else { + target[prop] = value; + } + } + }); + } + return target; + } + + //Similar to Function.prototype.bind, but the 'this' object is specified + //first, since it is easier to read/figure out what 'this' will be. + function bind(obj, fn) { + return function () { + return fn.apply(obj, arguments); + }; + } + + function scripts() { + return document.getElementsByTagName('script'); + } + + function defaultOnError(err) { + throw err; + } + + //Allow getting a global that is expressed in + //dot notation, like 'a.b.c'. + function getGlobal(value) { + if (!value) { + return value; + } + var g = global; + each(value.split('.'), function (part) { + g = g[part]; + }); + return g; + } + + /** + * Constructs an error with a pointer to an URL with more information. + * @param {String} id the error ID that maps to an ID on a web page. + * @param {String} message human readable error. + * @param {Error} [err] the original error, if there is one. + * + * @returns {Error} + */ + function makeError(id, msg, err, requireModules) { + var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); + e.requireType = id; + e.requireModules = requireModules; + if (err) { + e.originalError = err; + } + return e; + } + + if (typeof define !== 'undefined') { + //If a define is already in play via another AMD loader, + //do not overwrite. + return; + } + + if (typeof requirejs !== 'undefined') { + if (isFunction(requirejs)) { + //Do not overwrite an existing requirejs instance. + return; + } + cfg = requirejs; + requirejs = undefined; + } + + //Allow for a require config object + if (typeof require !== 'undefined' && !isFunction(require)) { + //assume it is a config object. + cfg = require; + require = undefined; + } + + function newContext(contextName) { + var inCheckLoaded, Module, context, handlers, + checkLoadedTimeoutId, + config = { + //Defaults. Do not set a default for map + //config to speed up normalize(), which + //will run faster if there is no default. + waitSeconds: 7, + baseUrl: './', + paths: {}, + bundles: {}, + pkgs: {}, + shim: {}, + config: {} + }, + registry = {}, + //registry of just enabled modules, to speed + //cycle breaking code when lots of modules + //are registered, but not activated. + enabledRegistry = {}, + undefEvents = {}, + defQueue = [], + defined = {}, + urlFetched = {}, + bundlesMap = {}, + requireCounter = 1, + unnormalizedCounter = 1; + + /** + * Trims the . and .. from an array of path segments. + * It will keep a leading path segment if a .. will become + * the first path segment, to help with module name lookups, + * which act like paths, but can be remapped. But the end result, + * all paths that use this function should look normalized. + * NOTE: this method MODIFIES the input array. + * @param {Array} ary the array of path segments. + */ + function trimDots(ary) { + var i, part; + for (i = 0; i < ary.length; i++) { + part = ary[i]; + if (part === '.') { + ary.splice(i, 1); + i -= 1; + } else if (part === '..') { + // If at the start, or previous value is still .., + // keep them so that when converted to a path it may + // still work when converted to a path, even though + // as an ID it is less than ideal. In larger point + // releases, may be better to just kick out an error. + if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') { + continue; + } else if (i > 0) { + ary.splice(i - 1, 2); + i -= 2; + } + } + } + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @param {Boolean} applyMap apply the map config to the value. Should + * only be done if this normalization is for a dependency ID. + * @returns {String} normalized name + */ + function normalize(name, baseName, applyMap) { + var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, + foundMap, foundI, foundStarMap, starI, normalizedBaseParts, + baseParts = (baseName && baseName.split('/')), + map = config.map, + starMap = map && map['*']; + + //Adjust any relative paths. + if (name) { + name = name.split('/'); + lastIndex = name.length - 1; + + // If wanting node ID compatibility, strip .js from end + // of IDs. Have to do this here, and not in nameToUrl + // because node allows either .js or non .js to map + // to same file. + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } + + // Starts with a '.' so need the baseName + if (name[0].charAt(0) === '.' && baseParts) { + //Convert baseName to array, and lop off the last part, + //so that . matches that 'directory' and not name of the baseName's + //module. For instance, baseName of 'one/two/three', maps to + //'one/two/three.js', but we want the directory, 'one/two' for + //this normalization. + normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); + name = normalizedBaseParts.concat(name); + } + + trimDots(name); + name = name.join('/'); + } + + //Apply map config if available. + if (applyMap && map && (baseParts || starMap)) { + nameParts = name.split('/'); + + outerLoop: for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join('/'); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = getOwn(map, baseParts.slice(0, j).join('/')); + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = getOwn(mapValue, nameSegment); + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break outerLoop; + } + } + } + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { + foundStarMap = getOwn(starMap, nameSegment); + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + // If the name points to a package's name, use + // the package main instead. + pkgMain = getOwn(config.pkgs, name); + + return pkgMain ? pkgMain : name; + } + + function removeScript(name) { + if (isBrowser) { + each(scripts(), function (scriptNode) { + if (scriptNode.getAttribute('data-requiremodule') === name && + scriptNode.getAttribute('data-requirecontext') === context.contextName) { + scriptNode.parentNode.removeChild(scriptNode); + return true; + } + }); + } + } + + function hasPathFallback(id) { + var pathConfig = getOwn(config.paths, id); + if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { + //Pop off the first array value, since it failed, and + //retry + pathConfig.shift(); + context.require.undef(id); + + //Custom require that does not do map translation, since + //ID is "absolute", already mapped/resolved. + context.makeRequire(null, { + skipMap: true + })([id]); + + return true; + } + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + /** + * Creates a module mapping that includes plugin prefix, module + * name, and path. If parentModuleMap is provided it will + * also normalize the name via require.normalize() + * + * @param {String} name the module name + * @param {String} [parentModuleMap] parent module map + * for the module name, used to resolve relative names. + * @param {Boolean} isNormalized: is the ID already normalized. + * This is true if this call is done for a define() module ID. + * @param {Boolean} applyMap: apply the map config to the ID. + * Should only be true if this map is for a dependency. + * + * @returns {Object} + */ + function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { + var url, pluginModule, suffix, nameParts, + prefix = null, + parentName = parentModuleMap ? parentModuleMap.name : null, + originalName = name, + isDefine = true, + normalizedName = ''; + + //If no name, then it means it is a require call, generate an + //internal name. + if (!name) { + isDefine = false; + name = '_@r' + (requireCounter += 1); + } + + nameParts = splitPrefix(name); + prefix = nameParts[0]; + name = nameParts[1]; + + if (prefix) { + prefix = normalize(prefix, parentName, applyMap); + pluginModule = getOwn(defined, prefix); + } + + //Account for relative paths if there is a base name. + if (name) { + if (prefix) { + if (pluginModule && pluginModule.normalize) { + //Plugin is loaded, use its normalize method. + normalizedName = pluginModule.normalize(name, function (name) { + return normalize(name, parentName, applyMap); + }); + } else { + // If nested plugin references, then do not try to + // normalize, as it will not normalize correctly. This + // places a restriction on resourceIds, and the longer + // term solution is not to normalize until plugins are + // loaded and all normalizations to allow for async + // loading of a loader plugin. But for now, fixes the + // common uses. Details in #1131 + normalizedName = name.indexOf('!') === -1 ? + normalize(name, parentName, applyMap) : + name; + } + } else { + //A regular module. + normalizedName = normalize(name, parentName, applyMap); + + //Normalized name may be a plugin ID due to map config + //application in normalize. The map config values must + //already be normalized, so do not need to redo that part. + nameParts = splitPrefix(normalizedName); + prefix = nameParts[0]; + normalizedName = nameParts[1]; + isNormalized = true; + + url = context.nameToUrl(normalizedName); + } + } + + //If the id is a plugin id that cannot be determined if it needs + //normalization, stamp it with a unique ID so two matching relative + //ids that may conflict can be separate. + suffix = prefix && !pluginModule && !isNormalized ? + '_unnormalized' + (unnormalizedCounter += 1) : + ''; + + return { + prefix: prefix, + name: normalizedName, + parentMap: parentModuleMap, + unnormalized: !!suffix, + url: url, + originalName: originalName, + isDefine: isDefine, + id: (prefix ? + prefix + '!' + normalizedName : + normalizedName) + suffix + }; + } + + function getModule(depMap) { + var id = depMap.id, + mod = getOwn(registry, id); + + if (!mod) { + mod = registry[id] = new context.Module(depMap); + } + + return mod; + } + + function on(depMap, name, fn) { + var id = depMap.id, + mod = getOwn(registry, id); + + if (hasProp(defined, id) && + (!mod || mod.defineEmitComplete)) { + if (name === 'defined') { + fn(defined[id]); + } + } else { + mod = getModule(depMap); + if (mod.error && name === 'error') { + fn(mod.error); + } else { + mod.on(name, fn); + } + } + } + + function onError(err, errback) { + var ids = err.requireModules, + notified = false; + + if (errback) { + errback(err); + } else { + each(ids, function (id) { + var mod = getOwn(registry, id); + if (mod) { + //Set error on module, so it skips timeout checks. + mod.error = err; + if (mod.events.error) { + notified = true; + mod.emit('error', err); + } + } + }); + + if (!notified) { + req.onError(err); + } + } + } + + /** + * Internal method to transfer globalQueue items to this context's + * defQueue. + */ + function takeGlobalQueue() { + //Push all the globalDefQueue items into the context's defQueue + if (globalDefQueue.length) { + each(globalDefQueue, function(queueItem) { + var id = queueItem[0]; + if (typeof id === 'string') { + context.defQueueMap[id] = true; + } + defQueue.push(queueItem); + }); + globalDefQueue = []; + } + } + + handlers = { + 'require': function (mod) { + if (mod.require) { + return mod.require; + } else { + return (mod.require = context.makeRequire(mod.map)); + } + }, + 'exports': function (mod) { + mod.usingExports = true; + if (mod.map.isDefine) { + if (mod.exports) { + return (defined[mod.map.id] = mod.exports); + } else { + return (mod.exports = defined[mod.map.id] = {}); + } + } + }, + 'module': function (mod) { + if (mod.module) { + return mod.module; + } else { + return (mod.module = { + id: mod.map.id, + uri: mod.map.url, + config: function () { + return getOwn(config.config, mod.map.id) || {}; + }, + exports: mod.exports || (mod.exports = {}) + }); + } + } + }; + + function cleanRegistry(id) { + //Clean up machinery used for waiting modules. + delete registry[id]; + delete enabledRegistry[id]; + } + + function breakCycle(mod, traced, processed) { + var id = mod.map.id; + + if (mod.error) { + mod.emit('error', mod.error); + } else { + traced[id] = true; + each(mod.depMaps, function (depMap, i) { + var depId = depMap.id, + dep = getOwn(registry, depId); + + //Only force things that have not completed + //being defined, so still in the registry, + //and only if it has not been matched up + //in the module already. + if (dep && !mod.depMatched[i] && !processed[depId]) { + if (getOwn(traced, depId)) { + mod.defineDep(i, defined[depId]); + mod.check(); //pass false? + } else { + breakCycle(dep, traced, processed); + } + } + }); + processed[id] = true; + } + } + + function checkLoaded() { + var err, usingPathFallback, + waitInterval = config.waitSeconds * 1000, + //It is possible to disable the wait interval by using waitSeconds of 0. + expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), + noLoads = [], + reqCalls = [], + stillLoading = false, + needCycleCheck = true; + + //Do not bother if this call was a result of a cycle break. + if (inCheckLoaded) { + return; + } + + inCheckLoaded = true; + + //Figure out the state of all the modules. + eachProp(enabledRegistry, function (mod) { + var map = mod.map, + modId = map.id; + + //Skip things that are not enabled or in error state. + if (!mod.enabled) { + return; + } + + if (!map.isDefine) { + reqCalls.push(mod); + } + + if (!mod.error) { + //If the module should be executed, and it has not + //been inited and time is up, remember it. + if (!mod.inited && expired) { + if (hasPathFallback(modId)) { + usingPathFallback = true; + stillLoading = true; + } else { + noLoads.push(modId); + removeScript(modId); + } + } else if (!mod.inited && mod.fetched && map.isDefine) { + stillLoading = true; + if (!map.prefix) { + //No reason to keep looking for unfinished + //loading. If the only stillLoading is a + //plugin resource though, keep going, + //because it may be that a plugin resource + //is waiting on a non-plugin cycle. + return (needCycleCheck = false); + } + } + } + }); + + if (expired && noLoads.length) { + //If wait time expired, throw error of unloaded modules. + err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); + err.contextName = context.contextName; + return onError(err); + } + + //Not expired, check for a cycle. + if (needCycleCheck) { + each(reqCalls, function (mod) { + breakCycle(mod, {}, {}); + }); + } + + //If still waiting on loads, and the waiting load is something + //other than a plugin resource, or there are still outstanding + //scripts, then just try back later. + if ((!expired || usingPathFallback) && stillLoading) { + //Something is still waiting to load. Wait for it, but only + //if a timeout is not already in effect. + if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { + checkLoadedTimeoutId = setTimeout(function () { + checkLoadedTimeoutId = 0; + checkLoaded(); + }, 50); + } + } + + inCheckLoaded = false; + } + + Module = function (map) { + this.events = getOwn(undefEvents, map.id) || {}; + this.map = map; + this.shim = getOwn(config.shim, map.id); + this.depExports = []; + this.depMaps = []; + this.depMatched = []; + this.pluginMaps = {}; + this.depCount = 0; + + /* this.exports this.factory + this.depMaps = [], + this.enabled, this.fetched + */ + }; + + Module.prototype = { + init: function (depMaps, factory, errback, options) { + options = options || {}; + + //Do not do more inits if already done. Can happen if there + //are multiple define calls for the same module. That is not + //a normal, common case, but it is also not unexpected. + if (this.inited) { + return; + } + + this.factory = factory; + + if (errback) { + //Register for errors on this module. + this.on('error', errback); + } else if (this.events.error) { + //If no errback already, but there are error listeners + //on this module, set up an errback to pass to the deps. + errback = bind(this, function (err) { + this.emit('error', err); + }); + } + + //Do a copy of the dependency array, so that + //source inputs are not modified. For example + //"shim" deps are passed in here directly, and + //doing a direct modification of the depMaps array + //would affect that config. + this.depMaps = depMaps && depMaps.slice(0); + + this.errback = errback; + + //Indicate this module has be initialized + this.inited = true; + + this.ignore = options.ignore; + + //Could have option to init this module in enabled mode, + //or could have been previously marked as enabled. However, + //the dependencies are not known until init is called. So + //if enabled previously, now trigger dependencies as enabled. + if (options.enabled || this.enabled) { + //Enable this module and dependencies. + //Will call this.check() + this.enable(); + } else { + this.check(); + } + }, + + defineDep: function (i, depExports) { + //Because of cycles, defined callback for a given + //export can be called more than once. + if (!this.depMatched[i]) { + this.depMatched[i] = true; + this.depCount -= 1; + this.depExports[i] = depExports; + } + }, + + fetch: function () { + if (this.fetched) { + return; + } + this.fetched = true; + + context.startTime = (new Date()).getTime(); + + var map = this.map; + + //If the manager is for a plugin managed resource, + //ask the plugin to load it now. + if (this.shim) { + context.makeRequire(this.map, { + enableBuildCallback: true + })(this.shim.deps || [], bind(this, function () { + return map.prefix ? this.callPlugin() : this.load(); + })); + } else { + //Regular dependency. + return map.prefix ? this.callPlugin() : this.load(); + } + }, + + load: function () { + var url = this.map.url; + + //Regular dependency. + if (!urlFetched[url]) { + urlFetched[url] = true; + context.load(this.map.id, url); + } + }, + + /** + * Checks if the module is ready to define itself, and if so, + * define it. + */ + check: function () { + if (!this.enabled || this.enabling) { + return; + } + + var err, cjsModule, + id = this.map.id, + depExports = this.depExports, + exports = this.exports, + factory = this.factory; + + if (!this.inited) { + // Only fetch if not already in the defQueue. + if (!hasProp(context.defQueueMap, id)) { + this.fetch(); + } + } else if (this.error) { + this.emit('error', this.error); + } else if (!this.defining) { + //The factory could trigger another require call + //that would result in checking this module to + //define itself again. If already in the process + //of doing that, skip this work. + this.defining = true; + + if (this.depCount < 1 && !this.defined) { + if (isFunction(factory)) { + //If there is an error listener, favor passing + //to that instead of throwing an error. However, + //only do it for define()'d modules. require + //errbacks should not be called for failures in + //their callbacks (#699). However if a global + //onError is set, use that. + if ((this.events.error && this.map.isDefine) || + req.onError !== defaultOnError) { + try { + exports = context.execCb(id, factory, depExports, exports); + } catch (e) { + err = e; + } + } else { + exports = context.execCb(id, factory, depExports, exports); + } + + // Favor return value over exports. If node/cjs in play, + // then will not have a return value anyway. Favor + // module.exports assignment over exports object. + if (this.map.isDefine && exports === undefined) { + cjsModule = this.module; + if (cjsModule) { + exports = cjsModule.exports; + } else if (this.usingExports) { + //exports already set the defined value. + exports = this.exports; + } + } + + if (err) { + err.requireMap = this.map; + err.requireModules = this.map.isDefine ? [this.map.id] : null; + err.requireType = this.map.isDefine ? 'define' : 'require'; + return onError((this.error = err)); + } + + } else { + //Just a literal value + exports = factory; + } + + this.exports = exports; + + if (this.map.isDefine && !this.ignore) { + defined[id] = exports; + + if (req.onResourceLoad) { + var resLoadMaps = []; + each(this.depMaps, function (depMap) { + resLoadMaps.push(depMap.normalizedMap || depMap); + }); + req.onResourceLoad(context, this.map, resLoadMaps); + } + } + + //Clean up + cleanRegistry(id); + + this.defined = true; + } + + //Finished the define stage. Allow calling check again + //to allow define notifications below in the case of a + //cycle. + this.defining = false; + + if (this.defined && !this.defineEmitted) { + this.defineEmitted = true; + this.emit('defined', this.exports); + this.defineEmitComplete = true; + } + + } + }, + + callPlugin: function () { + var map = this.map, + id = map.id, + //Map already normalized the prefix. + pluginMap = makeModuleMap(map.prefix); + + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(pluginMap); + + on(pluginMap, 'defined', bind(this, function (plugin) { + var load, normalizedMap, normalizedMod, + bundleId = getOwn(bundlesMap, this.map.id), + name = this.map.name, + parentName = this.map.parentMap ? this.map.parentMap.name : null, + localRequire = context.makeRequire(map.parentMap, { + enableBuildCallback: true + }); + + //If current map is not normalized, wait for that + //normalized name to load instead of continuing. + if (this.map.unnormalized) { + //Normalize the ID if the plugin allows it. + if (plugin.normalize) { + name = plugin.normalize(name, function (name) { + return normalize(name, parentName, true); + }) || ''; + } + + //prefix and name should already be normalized, no need + //for applying map config again either. + normalizedMap = makeModuleMap(map.prefix + '!' + name, + this.map.parentMap); + on(normalizedMap, + 'defined', bind(this, function (value) { + this.map.normalizedMap = normalizedMap; + this.init([], function () { return value; }, null, { + enabled: true, + ignore: true + }); + })); + + normalizedMod = getOwn(registry, normalizedMap.id); + if (normalizedMod) { + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(normalizedMap); + + if (this.events.error) { + normalizedMod.on('error', bind(this, function (err) { + this.emit('error', err); + })); + } + normalizedMod.enable(); + } + + return; + } + + //If a paths config, then just load that file instead to + //resolve the plugin, as it is built into that paths layerobj. + if (bundleId) { + this.map.url = context.nameToUrl(bundleId); + this.load(); + return; + } + + load = bind(this, function (value) { + this.init([], function () { return value; }, null, { + enabled: true + }); + }); + + load.error = bind(this, function (err) { + this.inited = true; + this.error = err; + err.requireModules = [id]; + + //Remove temp unnormalized modules for this module, + //since they will never be resolved otherwise now. + eachProp(registry, function (mod) { + if (mod.map.id.indexOf(id + '_unnormalized') === 0) { + cleanRegistry(mod.map.id); + } + }); + + onError(err); + }); + + //Allow plugins to load other code without having to know the + //context or how to 'complete' the load. + load.fromText = bind(this, function (text, textAlt) { + /*jslint evil: true */ + var moduleName = map.name, + moduleMap = makeModuleMap(moduleName), + hasInteractive = useInteractive; + + //As of 2.1.0, support just passing the text, to reinforce + //fromText only being called once per resource. Still + //support old style of passing moduleName but discard + //that moduleName in favor of the internal ref. + if (textAlt) { + text = textAlt; + } + + //Turn off interactive script matching for IE for any define + //calls in the text, then turn it back on at the end. + if (hasInteractive) { + useInteractive = false; + } + + //Prime the system by creating a module instance for + //it. + getModule(moduleMap); + + //Transfer any config to this other module. + if (hasProp(config.config, id)) { + config.config[moduleName] = config.config[id]; + } + + try { + req.exec(text); + } catch (e) { + return onError(makeError('fromtexteval', + 'fromText eval for ' + id + + ' failed: ' + e, + e, + [id])); + } + + if (hasInteractive) { + useInteractive = true; + } + + //Mark this as a dependency for the plugin + //resource + this.depMaps.push(moduleMap); + + //Support anonymous modules. + context.completeLoad(moduleName); + + //Bind the value of that module to the value for this + //resource ID. + localRequire([moduleName], load); + }); + + //Use parentName here since the plugin's name is not reliable, + //could be some weird string with no path that actually wants to + //reference the parentName's path. + plugin.load(map.name, localRequire, load, config); + })); + + context.enable(pluginMap, this); + this.pluginMaps[pluginMap.id] = pluginMap; + }, + + enable: function () { + enabledRegistry[this.map.id] = this; + this.enabled = true; + + //Set flag mentioning that the module is enabling, + //so that immediate calls to the defined callbacks + //for dependencies do not trigger inadvertent load + //with the depCount still being zero. + this.enabling = true; + + //Enable each dependency + each(this.depMaps, bind(this, function (depMap, i) { + var id, mod, handler; + + if (typeof depMap === 'string') { + //Dependency needs to be converted to a depMap + //and wired up to this module. + depMap = makeModuleMap(depMap, + (this.map.isDefine ? this.map : this.map.parentMap), + false, + !this.skipMap); + this.depMaps[i] = depMap; + + handler = getOwn(handlers, depMap.id); + + if (handler) { + this.depExports[i] = handler(this); + return; + } + + this.depCount += 1; + + on(depMap, 'defined', bind(this, function (depExports) { + if (this.undefed) { + return; + } + this.defineDep(i, depExports); + this.check(); + })); + + if (this.errback) { + on(depMap, 'error', bind(this, this.errback)); + } else if (this.events.error) { + // No direct errback on this module, but something + // else is listening for errors, so be sure to + // propagate the error correctly. + on(depMap, 'error', bind(this, function(err) { + this.emit('error', err); + })); + } + } + + id = depMap.id; + mod = registry[id]; + + //Skip special modules like 'require', 'exports', 'module' + //Also, don't call enable if it is already enabled, + //important in circular dependency cases. + if (!hasProp(handlers, id) && mod && !mod.enabled) { + context.enable(depMap, this); + } + })); + + //Enable each plugin that is used in + //a dependency + eachProp(this.pluginMaps, bind(this, function (pluginMap) { + var mod = getOwn(registry, pluginMap.id); + if (mod && !mod.enabled) { + context.enable(pluginMap, this); + } + })); + + this.enabling = false; + + this.check(); + }, + + on: function (name, cb) { + var cbs = this.events[name]; + if (!cbs) { + cbs = this.events[name] = []; + } + cbs.push(cb); + }, + + emit: function (name, evt) { + each(this.events[name], function (cb) { + cb(evt); + }); + if (name === 'error') { + //Now that the error handler was triggered, remove + //the listeners, since this broken Module instance + //can stay around for a while in the registry. + delete this.events[name]; + } + } + }; + + function callGetModule(args) { + //Skip modules already defined. + if (!hasProp(defined, args[0])) { + getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); + } + } + + function removeListener(node, func, name, ieName) { + //Favor detachEvent because of IE9 + //issue, see attachEvent/addEventListener comment elsewhere + //in this file. + if (node.detachEvent && !isOpera) { + //Probably IE. If not it will throw an error, which will be + //useful to know. + if (ieName) { + node.detachEvent(ieName, func); + } + } else { + node.removeEventListener(name, func, false); + } + } + + /** + * Given an event from a script node, get the requirejs info from it, + * and then removes the event listeners on the node. + * @param {Event} evt + * @returns {Object} + */ + function getScriptData(evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + var node = evt.currentTarget || evt.srcElement; + + //Remove the listeners once here. + removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); + removeListener(node, context.onScriptError, 'error'); + + return { + node: node, + id: node && node.getAttribute('data-requiremodule') + }; + } + + function intakeDefines() { + var args; + + //Any defined modules in the global queue, intake them now. + takeGlobalQueue(); + + //Make sure any remaining defQueue items get properly processed. + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + + args[args.length - 1])); + } else { + //args are id, deps, factory. Should be normalized by the + //define() function. + callGetModule(args); + } + } + context.defQueueMap = {}; + } + + context = { + config: config, + contextName: contextName, + registry: registry, + defined: defined, + urlFetched: urlFetched, + defQueue: defQueue, + defQueueMap: {}, + Module: Module, + makeModuleMap: makeModuleMap, + nextTick: req.nextTick, + onError: onError, + + /** + * Set a configuration for the context. + * @param {Object} cfg config object to integrate. + */ + configure: function (cfg) { + //Make sure the baseUrl ends in a slash. + if (cfg.baseUrl) { + if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { + cfg.baseUrl += '/'; + } + } + + // Convert old style urlArgs string to a function. + if (typeof cfg.urlArgs === 'string') { + var urlArgs = cfg.urlArgs; + cfg.urlArgs = function(id, url) { + return (url.indexOf('?') === -1 ? '?' : '&') + urlArgs; + }; + } + + //Save off the paths since they require special processing, + //they are additive. + var shim = config.shim, + objs = { + paths: true, + bundles: true, + config: true, + map: true + }; + + eachProp(cfg, function (value, prop) { + if (objs[prop]) { + if (!config[prop]) { + config[prop] = {}; + } + mixin(config[prop], value, true, true); + } else { + config[prop] = value; + } + }); + + //Reverse map the bundles + if (cfg.bundles) { + eachProp(cfg.bundles, function (value, prop) { + each(value, function (v) { + if (v !== prop) { + bundlesMap[v] = prop; + } + }); + }); + } + + //Merge shim + if (cfg.shim) { + eachProp(cfg.shim, function (value, id) { + //Normalize the structure + if (isArray(value)) { + value = { + deps: value + }; + } + if ((value.exports || value.init) && !value.exportsFn) { + value.exportsFn = context.makeShimExports(value); + } + shim[id] = value; + }); + config.shim = shim; + } + + //Adjust packages if necessary. + if (cfg.packages) { + each(cfg.packages, function (pkgObj) { + var location, name; + + pkgObj = typeof pkgObj === 'string' ? {name: pkgObj} : pkgObj; + + name = pkgObj.name; + location = pkgObj.location; + if (location) { + config.paths[name] = pkgObj.location; + } + + //Save pointer to main module ID for pkg name. + //Remove leading dot in main, so main paths are normalized, + //and remove any trailing .js, since different package + //envs have different conventions: some use a module name, + //some use a file name. + config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main') + .replace(currDirRegExp, '') + .replace(jsSuffixRegExp, ''); + }); + } + + //If there are any "waiting to execute" modules in the registry, + //update the maps for them, since their info, like URLs to load, + //may have changed. + eachProp(registry, function (mod, id) { + //If module already has init called, since it is too + //late to modify them, and ignore unnormalized ones + //since they are transient. + if (!mod.inited && !mod.map.unnormalized) { + mod.map = makeModuleMap(id, null, true); + } + }); + + //If a deps array or a config callback is specified, then call + //require with those args. This is useful when require is defined as a + //config object before require.js is loaded. + if (cfg.deps || cfg.callback) { + context.require(cfg.deps || [], cfg.callback); + } + }, + + makeShimExports: function (value) { + function fn() { + var ret; + if (value.init) { + ret = value.init.apply(global, arguments); + } + return ret || (value.exports && getGlobal(value.exports)); + } + return fn; + }, + + makeRequire: function (relMap, options) { + options = options || {}; + + function localRequire(deps, callback, errback) { + var id, map, requireMod; + + if (options.enableBuildCallback && callback && isFunction(callback)) { + callback.__requireJsBuild = true; + } + + if (typeof deps === 'string') { + if (isFunction(callback)) { + //Invalid call + return onError(makeError('requireargs', 'Invalid require call'), errback); + } + + //If require|exports|module are requested, get the + //value for them from the special handlers. Caveat: + //this only works while module is being defined. + if (relMap && hasProp(handlers, deps)) { + return handlers[deps](registry[relMap.id]); + } + + //Synchronous access to one module. If require.get is + //available (as in the Node adapter), prefer that. + if (req.get) { + return req.get(context, deps, relMap, localRequire); + } + + //Normalize module name, if it contains . or .. + map = makeModuleMap(deps, relMap, false, true); + id = map.id; + + if (!hasProp(defined, id)) { + return onError(makeError('notloaded', 'Module name "' + + id + + '" has not been loaded yet for context: ' + + contextName + + (relMap ? '' : '. Use require([])'))); + } + return defined[id]; + } + + //Grab defines waiting in the global queue. + intakeDefines(); + + //Mark all the dependencies as needing to be loaded. + context.nextTick(function () { + //Some defines could have been added since the + //require call, collect them. + intakeDefines(); + + requireMod = getModule(makeModuleMap(null, relMap)); + + //Store if map config should be applied to this require + //call for dependencies. + requireMod.skipMap = options.skipMap; + + requireMod.init(deps, callback, errback, { + enabled: true + }); + + checkLoaded(); + }); + + return localRequire; + } + + mixin(localRequire, { + isBrowser: isBrowser, + + /** + * Converts a module name + .extension into an URL path. + * *Requires* the use of a module name. It does not support using + * plain URLs like nameToUrl. + */ + toUrl: function (moduleNamePlusExt) { + var ext, + index = moduleNamePlusExt.lastIndexOf('.'), + segment = moduleNamePlusExt.split('/')[0], + isRelative = segment === '.' || segment === '..'; + + //Have a file extension alias, and it is not the + //dots from a relative path. + if (index !== -1 && (!isRelative || index > 1)) { + ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); + moduleNamePlusExt = moduleNamePlusExt.substring(0, index); + } + + return context.nameToUrl(normalize(moduleNamePlusExt, + relMap && relMap.id, true), ext, true); + }, + + defined: function (id) { + return hasProp(defined, makeModuleMap(id, relMap, false, true).id); + }, + + specified: function (id) { + id = makeModuleMap(id, relMap, false, true).id; + return hasProp(defined, id) || hasProp(registry, id); + } + }); + + //Only allow undef on top level require calls + if (!relMap) { + localRequire.undef = function (id) { + //Bind any waiting define() calls to this context, + //fix for #408 + takeGlobalQueue(); + + var map = makeModuleMap(id, relMap, true), + mod = getOwn(registry, id); + + mod.undefed = true; + removeScript(id); + + delete defined[id]; + delete urlFetched[map.url]; + delete undefEvents[id]; + + //Clean queued defines too. Go backwards + //in array so that the splices do not + //mess up the iteration. + eachReverse(defQueue, function(args, i) { + if (args[0] === id) { + defQueue.splice(i, 1); + } + }); + delete context.defQueueMap[id]; + + if (mod) { + //Hold on to listeners in case the + //module will be attempted to be reloaded + //using a different config. + if (mod.events.defined) { + undefEvents[id] = mod.events; + } + + cleanRegistry(id); + } + }; + } + + return localRequire; + }, + + /** + * Called to enable a module if it is still in the registry + * awaiting enablement. A second arg, parent, the parent module, + * is passed in for context, when this method is overridden by + * the optimizer. Not shown here to keep code compact. + */ + enable: function (depMap) { + var mod = getOwn(registry, depMap.id); + if (mod) { + getModule(depMap).enable(); + } + }, + + /** + * Internal method used by environment adapters to complete a load event. + * A load event could be a script load or just a load pass from a synchronous + * load call. + * @param {String} moduleName the name of the module to potentially complete. + */ + completeLoad: function (moduleName) { + var found, args, mod, + shim = getOwn(config.shim, moduleName) || {}, + shExports = shim.exports; + + takeGlobalQueue(); + + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + args[0] = moduleName; + //If already found an anonymous module and bound it + //to this name, then this is some other anon module + //waiting for its completeLoad to fire. + if (found) { + break; + } + found = true; + } else if (args[0] === moduleName) { + //Found matching define call for this script! + found = true; + } + + callGetModule(args); + } + context.defQueueMap = {}; + + //Do this after the cycle of callGetModule in case the result + //of those calls/init calls changes the registry. + mod = getOwn(registry, moduleName); + + if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { + if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { + if (hasPathFallback(moduleName)) { + return; + } else { + return onError(makeError('nodefine', + 'No define call for ' + moduleName, + null, + [moduleName])); + } + } else { + //A script that does not call define(), so just simulate + //the call for it. + callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); + } + } + + checkLoaded(); + }, + + /** + * Converts a module name to a file path. Supports cases where + * moduleName may actually be just an URL. + * Note that it **does not** call normalize on the moduleName, + * it is assumed to have already been normalized. This is an + * internal API, not a public one. Use toUrl for the public API. + */ + nameToUrl: function (moduleName, ext, skipExt) { + var paths, syms, i, parentModule, url, + parentPath, bundleId, + pkgMain = getOwn(config.pkgs, moduleName); + + if (pkgMain) { + moduleName = pkgMain; + } + + bundleId = getOwn(bundlesMap, moduleName); + + if (bundleId) { + return context.nameToUrl(bundleId, ext, skipExt); + } + + //If a colon is in the URL, it indicates a protocol is used and it is just + //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) + //or ends with .js, then assume the user meant to use an url and not a module id. + //The slash is important for protocol-less URLs as well as full paths. + if (req.jsExtRegExp.test(moduleName)) { + //Just a plain path, not module name lookup, so just return it. + //Add extension if it is included. This is a bit wonky, only non-.js things pass + //an extension, this method probably needs to be reworked. + url = moduleName + (ext || ''); + } else { + //A module that needs to be converted to a path. + paths = config.paths; + + syms = moduleName.split('/'); + //For each module name segment, see if there is a path + //registered for it. Start with most specific name + //and work up from it. + for (i = syms.length; i > 0; i -= 1) { + parentModule = syms.slice(0, i).join('/'); + + parentPath = getOwn(paths, parentModule); + if (parentPath) { + //If an array, it means there are a few choices, + //Choose the one that is desired + if (isArray(parentPath)) { + parentPath = parentPath[0]; + } + syms.splice(0, i, parentPath); + break; + } + } + + //Join the path parts together, then figure out if baseUrl is needed. + url = syms.join('/'); + url += (ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? '' : '.js')); + url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; + } + + return config.urlArgs && !/^blob\:/.test(url) ? + url + config.urlArgs(moduleName, url) : url; + }, + + //Delegates to req.load. Broken out as a separate function to + //allow overriding in the optimizer. + load: function (id, url) { + req.load(context, id, url); + }, + + /** + * Executes a module callback function. Broken out as a separate function + * solely to allow the build system to sequence the files in the built + * layerobj in the right sequence. + * + * @private + */ + execCb: function (name, callback, args, exports) { + return callback.apply(exports, args); + }, + + /** + * callback for script loads, used to check status of loading. + * + * @param {Event} evt the event from the browser for the script + * that was loaded. + */ + onScriptLoad: function (evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + if (evt.type === 'load' || + (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { + //Reset interactive script so a script node is not held onto for + //to long. + interactiveScript = null; + + //Pull out the name of the module and the context. + var data = getScriptData(evt); + context.completeLoad(data.id); + } + }, + + /** + * Callback for script errors. + */ + onScriptError: function (evt) { + var data = getScriptData(evt); + if (!hasPathFallback(data.id)) { + var parents = []; + eachProp(registry, function(value, key) { + if (key.indexOf('_@r') !== 0) { + each(value.depMaps, function(depMap) { + if (depMap.id === data.id) { + parents.push(key); + return true; + } + }); + } + }); + return onError(makeError('scripterror', 'Script error for "' + data.id + + (parents.length ? + '", needed by: ' + parents.join(', ') : + '"'), evt, [data.id])); + } + } + }; + + context.require = context.makeRequire(); + return context; + } + + /** + * Main entry point. + * + * If the only argument to require is a string, then the module that + * is represented by that string is fetched for the appropriate context. + * + * If the first argument is an array, then it will be treated as an array + * of dependency string names to fetch. An optional function callback can + * be specified to execute when all of those dependencies are available. + * + * Make a local req variable to help Caja compliance (it assumes things + * on a require that are not standardized), and to give a short + * name for minification/local scope use. + */ + req = requirejs = function (deps, callback, errback, optional) { + + //Find the right context, use default + var context, config, + contextName = defContextName; + + // Determine if have config object in the call. + if (!isArray(deps) && typeof deps !== 'string') { + // deps is a config object + config = deps; + if (isArray(callback)) { + // Adjust args if there are dependencies + deps = callback; + callback = errback; + errback = optional; + } else { + deps = []; + } + } + + if (config && config.context) { + contextName = config.context; + } + + context = getOwn(contexts, contextName); + if (!context) { + context = contexts[contextName] = req.s.newContext(contextName); + } + + if (config) { + context.configure(config); + } + + return context.require(deps, callback, errback); + }; + + /** + * Support require.config() to make it easier to cooperate with other + * AMD loaders on globally agreed names. + */ + req.config = function (config) { + return req(config); + }; + + /** + * Execute something after the current tick + * of the event loop. Override for other envs + * that have a better solution than setTimeout. + * @param {Function} fn function to execute later. + */ + req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { + setTimeout(fn, 4); + } : function (fn) { fn(); }; + + /** + * Export require as a global, but only if it does not already exist. + */ + if (!require) { + require = req; + } + + req.version = version; + + //Used to filter out dependencies that are already paths. + req.jsExtRegExp = /^\/|:|\?|\.js$/; + req.isBrowser = isBrowser; + s = req.s = { + contexts: contexts, + newContext: newContext + }; + + //Create default context. + req({}); + + //Exports some context-sensitive methods on global require. + each([ + 'toUrl', + 'undef', + 'defined', + 'specified' + ], function (prop) { + //Reference from contexts instead of early binding to default context, + //so that during builds, the latest instance of the default context + //with its config gets used. + req[prop] = function () { + var ctx = contexts[defContextName]; + return ctx.require[prop].apply(ctx, arguments); + }; + }); + + if (isBrowser) { + head = s.head = document.getElementsByTagName('head')[0]; + //If BASE tag is in play, using appendChild is a problem for IE6. + //When that browser dies, this can be removed. Details in this jQuery bug: + //http://dev.jquery.com/ticket/2709 + baseElement = document.getElementsByTagName('base')[0]; + if (baseElement) { + head = s.head = baseElement.parentNode; + } + } + + /** + * Any errors that require explicitly generates will be passed to this + * function. Intercept/override it if you want custom error handling. + * @param {Error} err the error object. + */ + req.onError = defaultOnError; + + /** + * Creates the node for the load command. Only used in browser envs. + */ + req.createNode = function (config, moduleName, url) { + var node = config.xhtml ? + document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : + document.createElement('script'); + node.type = config.scriptType || 'text/javascript'; + node.charset = 'utf-8'; + node.async = true; + return node; + }; + + /** + * Does the request to load a module for the browser case. + * Make this a separate function to allow other environments + * to override it. + * + * @param {Object} context the require context to find state. + * @param {String} moduleName the name of the module. + * @param {Object} url the URL to the module. + */ + req.load = function (context, moduleName, url) { + var config = (context && context.config) || {}, + node; + if (isBrowser) { + //In the browser so use a script tag + node = req.createNode(config, moduleName, url); + + node.setAttribute('data-requirecontext', context.contextName); + node.setAttribute('data-requiremodule', moduleName); + + //Set up load listener. Test attachEvent first because IE9 has + //a subtle issue in its addEventListener and script onload firings + //that do not match the behavior of all other browsers with + //addEventListener support, which fire the onload event for a + //script right after the script execution. See: + //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution + //UNFORTUNATELY Opera implements attachEvent but does not follow the script + //script execution mode. + if (node.attachEvent && + //Check if node.attachEvent is artificially added by custom script or + //natively supported by browser + //read https://github.com/requirejs/requirejs/issues/187 + //if we can NOT find [native code] then it must NOT natively supported. + //in IE8, node.attachEvent does not have toString() + //Note the test for "[native code" with no closing brace, see: + //https://github.com/requirejs/requirejs/issues/273 + !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && + !isOpera) { + //Probably IE. IE (at least 6-8) do not fire + //script onload right after executing the script, so + //we cannot tie the anonymous define call to a name. + //However, IE reports the script as being in 'interactive' + //readyState at the time of the define call. + useInteractive = true; + + node.attachEvent('onreadystatechange', context.onScriptLoad); + //It would be great to add an error handler here to catch + //404s in IE9+. However, onreadystatechange will fire before + //the error handler, so that does not help. If addEventListener + //is used, then IE will fire error before load, but we cannot + //use that pathway given the connect.microsoft.com issue + //mentioned above about not doing the 'script execute, + //then fire the script load event listener before execute + //next script' that other browsers do. + //Best hope: IE10 fixes the issues, + //and then destroys all installs of IE 6-9. + //node.attachEvent('onerror', context.onScriptError); + } else { + node.addEventListener('load', context.onScriptLoad, false); + node.addEventListener('error', context.onScriptError, false); + } + node.src = url; + + //Calling onNodeCreated after all properties on the node have been + //set, but before it is placed in the DOM. + if (config.onNodeCreated) { + config.onNodeCreated(node, config, moduleName, url); + } + + //For some cache cases in IE 6-8, the script executes before the end + //of the appendChild execution, so to tie an anonymous define + //call to the module name (which is stored on the node), hold on + //to a reference to this node, but clear after the DOM insertion. + currentlyAddingScript = node; + if (baseElement) { + head.insertBefore(node, baseElement); + } else { + head.appendChild(node); + } + currentlyAddingScript = null; + + return node; + } else if (isWebWorker) { + try { + //In a web worker, use importScripts. This is not a very + //efficient use of importScripts, importScripts will block until + //its script is downloaded and evaluated. However, if web workers + //are in play, the expectation is that a build has been done so + //that only one script needs to be loaded anyway. This may need + //to be reevaluated if other use cases become common. + + // Post a task to the event loop to work around a bug in WebKit + // where the worker gets garbage-collected after calling + // importScripts(): https://webkit.org/b/153317 + setTimeout(function() {}, 0); + importScripts(url); + + //Account for anonymous modules + context.completeLoad(moduleName); + } catch (e) { + context.onError(makeError('importscripts', + 'importScripts failed for ' + + moduleName + ' at ' + url, + e, + [moduleName])); + } + } + }; + + function getInteractiveScript() { + if (interactiveScript && interactiveScript.readyState === 'interactive') { + return interactiveScript; + } + + eachReverse(scripts(), function (script) { + if (script.readyState === 'interactive') { + return (interactiveScript = script); + } + }); + return interactiveScript; + } + + //Look for a data-main script attribute, which could also adjust the baseUrl. + if (isBrowser && !cfg.skipDataMain) { + //Figure out baseUrl. Get it from the script tag with require.js in it. + eachReverse(scripts(), function (script) { + //Set the 'head' where we can append children by + //using the script's parent. + if (!head) { + head = script.parentNode; + } + + //Look for a data-main attribute to set main script for the page + //to load. If it is there, the path to data main becomes the + //baseUrl, if it is not already set. + dataMain = script.getAttribute('data-main'); + if (dataMain) { + //Preserve dataMain in case it is a path (i.e. contains '?') + mainScript = dataMain; + + //Set final baseUrl if there is not already an explicit one, + //but only do so if the data-main value is not a loader plugin + //module ID. + if (!cfg.baseUrl && mainScript.indexOf('!') === -1) { + //Pull off the directory of data-main for use as the + //baseUrl. + src = mainScript.split('/'); + mainScript = src.pop(); + subPath = src.length ? src.join('/') + '/' : './'; + + cfg.baseUrl = subPath; + } + + //Strip off any trailing .js since mainScript is now + //like a module name. + mainScript = mainScript.replace(jsSuffixRegExp, ''); + + //If mainScript is still a path, fall back to dataMain + if (req.jsExtRegExp.test(mainScript)) { + mainScript = dataMain; + } + + //Put the data-main script in the files to load. + cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; + + return true; + } + }); + } + + /** + * The function that handles definitions of modules. Differs from + * require() in that a string for the module should be the first argument, + * and the function to execute after dependencies are loaded should + * return a value to define the module corresponding to the first argument's + * name. + */ + define = function (name, deps, callback) { + var node, context; + + //Allow for anonymous modules + if (typeof name !== 'string') { + //Adjust args appropriately + callback = deps; + deps = name; + name = null; + } + + //This module may not have dependencies + if (!isArray(deps)) { + callback = deps; + deps = null; + } + + //If no name, and callback is a function, then figure out if it a + //CommonJS thing with dependencies. + if (!deps && isFunction(callback)) { + deps = []; + //Remove comments from the callback string, + //look for require calls, and pull them into the dependencies, + //but only if there are function args. + if (callback.length) { + callback + .toString() + .replace(commentRegExp, commentReplace) + .replace(cjsRequireRegExp, function (match, dep) { + deps.push(dep); + }); + + //May be a CommonJS thing even without require calls, but still + //could use exports, and module. Avoid doing exports and module + //work though if it just needs require. + //REQUIRES the function to expect the CommonJS variables in the + //order listed below. + deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); + } + } + + //If in IE 6-8 and hit an anonymous define() call, do the interactive + //work. + if (useInteractive) { + node = currentlyAddingScript || getInteractiveScript(); + if (node) { + if (!name) { + name = node.getAttribute('data-requiremodule'); + } + context = contexts[node.getAttribute('data-requirecontext')]; + } + } + + //Always save off evaluating the def call until the script onload handler. + //This allows multiple modules to be in a file without prematurely + //tracing dependencies, and allows for anonymous module support, + //where the module name is not known until the script onload event + //occurs. If no context, use the global queue, and get it processed + //in the onscript load callback. + if (context) { + context.defQueue.push([name, deps, callback]); + context.defQueueMap[name] = true; + } else { + globalDefQueue.push([name, deps, callback]); + } + }; + + define.amd = { + jQuery: true + }; + + /** + * Executes the text. Normally just uses eval, but can be modified + * to use a better, environment-specific call. Only used for transpiling + * loader plugins, not for plain JS modules. + * @param {String} text the text to execute/evaluate. + */ + req.exec = function (text) { + /*jslint evil: true */ + return eval(text); + }; + + //Set up with config info. + req(cfg); +}(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout))); + + + + this.requirejsVars = { + require: require, + requirejs: require, + define: define + }; + + if (env === 'browser') { + //sloppy since eval enclosed with use strict causes problems if the source +//text is not strict-compliant. +/*jslint sloppy: true, evil: true */ +/*global require, XMLHttpRequest */ + +(function () { + // Separate function to avoid eval pollution, same with arguments use. + function exec() { + eval(arguments[0]); + } + + require.load = function (context, moduleName, url) { + var xhr = new XMLHttpRequest(); + + xhr.open('GET', url, true); + xhr.send(); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + exec(xhr.responseText); + + //Support anonymous modules. + context.completeLoad(moduleName); + } + }; + }; +}()); + } else if (env === 'rhino') { + /*global require: false, java: false, load: false */ + +(function () { + 'use strict'; + require.load = function (context, moduleName, url) { + + load(url); + + //Support anonymous modules. + context.completeLoad(moduleName); + }; + +}()); + } else if (env === 'node') { + this.requirejsVars.nodeRequire = nodeRequire; + require.nodeRequire = nodeRequire; + + //Explicity not strict since this file contains an eval call, and do not want +//to enforce strict on code evaluated that way. See +//https://github.com/requirejs/r.js/issues/774 +/*jslint regexp: false, sloppy: true*/ +/*global require: false, define: false, requirejsVars: false, process: false */ + +/** + * This adapter assumes that x.js has loaded it and set up + * some variables. This adapter just allows limited RequireJS + * usage from within the requirejs directory. The general + * node adapater is r.js. + */ + +(function () { + var nodeReq = requirejsVars.nodeRequire, + req = requirejsVars.require, + def = requirejsVars.define, + fs = nodeReq('fs'), + path = nodeReq('path'), + vm = nodeReq('vm'), + //In Node 0.7+ existsSync is on fs. + exists = fs.existsSync || path.existsSync, + hasOwn = Object.prototype.hasOwnProperty; + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + function syncTick(fn) { + fn(); + } + + function makeError(message, moduleName) { + var err = new Error(message); + err.requireModules = [moduleName]; + return err; + } + + //Supply an implementation that allows synchronous get of a module. + req.get = function (context, moduleName, relModuleMap, localRequire) { + if (moduleName === "require" || moduleName === "exports" || moduleName === "module") { + context.onError(makeError("Explicit require of " + moduleName + " is not allowed.", moduleName)); + } + + var ret, oldTick, + moduleMap = context.makeModuleMap(moduleName, relModuleMap, false, true); + + //Normalize module name, if it contains . or .. + moduleName = moduleMap.id; + + if (hasProp(context.defined, moduleName)) { + ret = context.defined[moduleName]; + } else { + if (ret === undefined) { + //Make sure nextTick for this type of call is sync-based. + oldTick = context.nextTick; + context.nextTick = syncTick; + try { + if (moduleMap.prefix) { + //A plugin, call requirejs to handle it. Now that + //nextTick is syncTick, the require will complete + //synchronously. + localRequire([moduleMap.originalName]); + + //Now that plugin is loaded, can regenerate the moduleMap + //to get the final, normalized ID. + moduleMap = context.makeModuleMap(moduleMap.originalName, relModuleMap, false, true); + moduleName = moduleMap.id; + } else { + //Try to dynamically fetch it. + req.load(context, moduleName, moduleMap.url); + + //Enable the module + context.enable(moduleMap, relModuleMap); + } + + //Break any cycles by requiring it normally, but this will + //finish synchronously + context.require([moduleName]); + + //The above calls are sync, so can do the next thing safely. + ret = context.defined[moduleName]; + } finally { + context.nextTick = oldTick; + } + } + } + + return ret; + }; + + req.nextTick = function (fn) { + process.nextTick(fn); + }; + + //Add wrapper around the code so that it gets the requirejs + //API instead of the Node API, and it is done lexically so + //that it survives later execution. + req.makeNodeWrapper = function (contents) { + return '(function (require, requirejs, define) { ' + + contents + + '\n}(requirejsVars.require, requirejsVars.requirejs, requirejsVars.define));'; + }; + + req.load = function (context, moduleName, url) { + var contents, err, + config = context.config; + + if (config.shim[moduleName] && (!config.suppress || !config.suppress.nodeShim)) { + console.warn('Shim config not supported in Node, may or may not work. Detected ' + + 'for module: ' + moduleName); + } + + if (exists(url)) { + contents = fs.readFileSync(url, 'utf8'); + + contents = req.makeNodeWrapper(contents); + try { + vm.runInThisContext(contents, fs.realpathSync(url)); + } catch (e) { + err = new Error('Evaluating ' + url + ' as module "' + + moduleName + '" failed with error: ' + e); + err.originalError = e; + err.moduleName = moduleName; + err.requireModules = [moduleName]; + err.fileName = url; + return context.onError(err); + } + } else { + def(moduleName, function () { + //Get the original name, since relative requires may be + //resolved differently in node (issue #202). Also, if relative, + //make it relative to the URL of the item requesting it + //(issue #393) + var dirName, + map = hasProp(context.registry, moduleName) && + context.registry[moduleName].map, + parentMap = map && map.parentMap, + originalName = map && map.originalName; + + if (originalName.charAt(0) === '.' && parentMap) { + dirName = parentMap.url.split('/'); + dirName.pop(); + originalName = dirName.join('/') + '/' + originalName; + } + + try { + return (context.config.nodeRequire || req.nodeRequire)(originalName); + } catch (e) { + err = new Error('Tried loading "' + moduleName + '" at ' + + url + ' then tried node\'s require("' + + originalName + '") and it failed ' + + 'with error: ' + e); + err.originalError = e; + err.moduleName = originalName; + err.requireModules = [moduleName]; + throw err; + } + }); + } + + //Support anonymous modules. + context.completeLoad(moduleName); + }; + + //Override to provide the function wrapper for define/require. + req.exec = function (text) { + /*jslint evil: true */ + text = req.makeNodeWrapper(text); + return eval(text); + }; +}()); + + } else if (env === 'xpconnect') { + /*jslint */ +/*global require, load */ + +(function () { + 'use strict'; + require.load = function (context, moduleName, url) { + + load(url); + + //Support anonymous modules. + context.completeLoad(moduleName); + }; + +}()); + + } + + //Support a default file name to execute. Useful for hosted envs + //like Joyent where it defaults to a server.js as the only executed + //script. But only do it if this is not an optimization run. + if (commandOption !== 'o' && (!fileName || !jsSuffixRegExp.test(fileName))) { + fileName = 'main.js'; + } + + /** + * Loads the library files that can be used for the optimizer, or for other + * tasks. + */ + function loadLib() { + /*jslint strict: false */ +/*global Packages: false, process: false, window: false, navigator: false, + document: false, define: false */ + +/** + * A plugin that modifies any /env/ path to be the right path based on + * the host environment. Right now only works for Node, Rhino and browser. + */ +(function () { + var pathRegExp = /(\/|^)env\/|\{env\}/, + env = 'unknown'; + + if (typeof process !== 'undefined' && process.versions && !!process.versions.node) { + env = 'node'; + } else if (typeof Packages !== 'undefined') { + env = 'rhino'; + } else if ((typeof navigator !== 'undefined' && typeof document !== 'undefined') || + (typeof importScripts !== 'undefined' && typeof self !== 'undefined')) { + env = 'browser'; + } else if (typeof Components !== 'undefined' && Components.classes && Components.interfaces) { + env = 'xpconnect'; + } + + define('env', { + get: function () { + return env; + }, + + load: function (name, req, load, config) { + //Allow override in the config. + if (config.env) { + env = config.env; + } + + name = name.replace(pathRegExp, function (match, prefix) { + if (match.indexOf('{') === -1) { + return prefix + env + '/'; + } else { + return env; + } + }); + + req([name], function (mod) { + load(mod); + }); + } + }); +}()); +/*jslint plusplus: true */ +/*global define, java */ + +define('lang', function () { + 'use strict'; + + var lang, isJavaObj, + hasOwn = Object.prototype.hasOwnProperty; + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + isJavaObj = function () { + return false; + }; + + //Rhino, but not Nashorn (detected by importPackage not existing) + //Can have some strange foreign objects. + if (typeof java !== 'undefined' && java.lang && java.lang.Object && typeof importPackage !== 'undefined') { + isJavaObj = function (obj) { + return obj instanceof java.lang.Object; + }; + } + + lang = { + backSlashRegExp: /\\/g, + ostring: Object.prototype.toString, + + isArray: Array.isArray || function (it) { + return lang.ostring.call(it) === "[object Array]"; + }, + + isFunction: function(it) { + return lang.ostring.call(it) === "[object Function]"; + }, + + isRegExp: function(it) { + return it && it instanceof RegExp; + }, + + hasProp: hasProp, + + //returns true if the object does not have an own property prop, + //or if it does, it is a falsy value. + falseProp: function (obj, prop) { + return !hasProp(obj, prop) || !obj[prop]; + }, + + //gets own property value for given prop on object + getOwn: function (obj, prop) { + return hasProp(obj, prop) && obj[prop]; + }, + + _mixin: function(dest, source, override){ + var name; + for (name in source) { + if(source.hasOwnProperty(name) && + (override || !dest.hasOwnProperty(name))) { + dest[name] = source[name]; + } + } + + return dest; // Object + }, + + /** + * mixin({}, obj1, obj2) is allowed. If the last argument is a boolean, + * then the source objects properties are force copied over to dest. + */ + mixin: function(dest){ + var parameters = Array.prototype.slice.call(arguments), + override, i, l; + + if (!dest) { dest = {}; } + + if (parameters.length > 2 && typeof arguments[parameters.length-1] === 'boolean') { + override = parameters.pop(); + } + + for (i = 1, l = parameters.length; i < l; i++) { + lang._mixin(dest, parameters[i], override); + } + return dest; // Object + }, + + /** + * Does a deep mix of source into dest, where source values override + * dest values if a winner is needed. + * @param {Object} dest destination object that receives the mixed + * values. + * @param {Object} source source object contributing properties to mix + * in. + * @return {[Object]} returns dest object with the modification. + */ + deepMix: function(dest, source) { + lang.eachProp(source, function (value, prop) { + if (typeof value === 'object' && value && + !lang.isArray(value) && !lang.isFunction(value) && + !(value instanceof RegExp)) { + + if (!dest[prop]) { + dest[prop] = {}; + } + lang.deepMix(dest[prop], value); + } else { + dest[prop] = value; + } + }); + return dest; + }, + + /** + * Does a type of deep copy. Do not give it anything fancy, best + * for basic object copies of objects that also work well as + * JSON-serialized things, or has properties pointing to functions. + * For non-array/object values, just returns the same object. + * @param {Object} obj copy properties from this object + * @param {Object} [ignoredProps] optional object whose own properties + * are keys that should be ignored. + * @return {Object} + */ + deeplikeCopy: function (obj, ignoredProps) { + var type, result; + + if (lang.isArray(obj)) { + result = []; + obj.forEach(function(value) { + result.push(lang.deeplikeCopy(value, ignoredProps)); + }); + return result; + } + + type = typeof obj; + if (obj === null || obj === undefined || type === 'boolean' || + type === 'string' || type === 'number' || lang.isFunction(obj) || + lang.isRegExp(obj)|| isJavaObj(obj)) { + return obj; + } + + //Anything else is an object, hopefully. + result = {}; + lang.eachProp(obj, function(value, key) { + if (!ignoredProps || !hasProp(ignoredProps, key)) { + result[key] = lang.deeplikeCopy(value, ignoredProps); + } + }); + return result; + }, + + delegate: (function () { + // boodman/crockford delegation w/ cornford optimization + function TMP() {} + return function (obj, props) { + TMP.prototype = obj; + var tmp = new TMP(); + TMP.prototype = null; + if (props) { + lang.mixin(tmp, props); + } + return tmp; // Object + }; + }()), + + /** + * Helper function for iterating over an array. If the func returns + * a true value, it will break out of the loop. + */ + each: function each(ary, func) { + if (ary) { + var i; + for (i = 0; i < ary.length; i += 1) { + if (func(ary[i], i, ary)) { + break; + } + } + } + }, + + /** + * Cycles over properties in an object and calls a function for each + * property value. If the function returns a truthy value, then the + * iteration is stopped. + */ + eachProp: function eachProp(obj, func) { + var prop; + for (prop in obj) { + if (hasProp(obj, prop)) { + if (func(obj[prop], prop)) { + break; + } + } + } + }, + + //Similar to Function.prototype.bind, but the "this" object is specified + //first, since it is easier to read/figure out what "this" will be. + bind: function bind(obj, fn) { + return function () { + return fn.apply(obj, arguments); + }; + }, + + //Escapes a content string to be be a string that has characters escaped + //for inclusion as part of a JS string. + jsEscape: function (content) { + return content.replace(/(["'\\])/g, '\\$1') + .replace(/[\f]/g, "\\f") + .replace(/[\b]/g, "\\b") + .replace(/[\n]/g, "\\n") + .replace(/[\t]/g, "\\t") + .replace(/[\r]/g, "\\r"); + } + }; + return lang; +}); +/** + * prim 0.0.1 Copyright (c) 2012-2014, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/requirejs/prim for details + */ + +/*global setImmediate, process, setTimeout, define, module */ + +//Set prime.hideResolutionConflict = true to allow "resolution-races" +//in promise-tests to pass. +//Since the goal of prim is to be a small impl for trusted code, it is +//more important to normally throw in this case so that we can find +//logic errors quicker. + +var prim; +(function () { + 'use strict'; + var op = Object.prototype, + hasOwn = op.hasOwnProperty; + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + /** + * Helper function for iterating over an array. If the func returns + * a true value, it will break out of the loop. + */ + function each(ary, func) { + if (ary) { + var i; + for (i = 0; i < ary.length; i += 1) { + if (ary[i]) { + func(ary[i], i, ary); + } + } + } + } + + function check(p) { + if (hasProp(p, 'e') || hasProp(p, 'v')) { + if (!prim.hideResolutionConflict) { + throw new Error('Prim promise already resolved: ' + + JSON.stringify(p)); + } + return false; + } + return true; + } + + function notify(ary, value) { + prim.nextTick(function () { + each(ary, function (item) { + item(value); + }); + }); + } + + prim = function prim() { + var p, + ok = [], + fail = []; + + return (p = { + callback: function (yes, no) { + if (no) { + p.errback(no); + } + + if (hasProp(p, 'v')) { + prim.nextTick(function () { + yes(p.v); + }); + } else { + ok.push(yes); + } + }, + + errback: function (no) { + if (hasProp(p, 'e')) { + prim.nextTick(function () { + no(p.e); + }); + } else { + fail.push(no); + } + }, + + finished: function () { + return hasProp(p, 'e') || hasProp(p, 'v'); + }, + + rejected: function () { + return hasProp(p, 'e'); + }, + + resolve: function (v) { + if (check(p)) { + p.v = v; + notify(ok, v); + } + return p; + }, + reject: function (e) { + if (check(p)) { + p.e = e; + notify(fail, e); + } + return p; + }, + + start: function (fn) { + p.resolve(); + return p.promise.then(fn); + }, + + promise: { + then: function (yes, no) { + var next = prim(); + + p.callback(function (v) { + try { + if (yes && typeof yes === 'function') { + v = yes(v); + } + + if (v && v.then) { + v.then(next.resolve, next.reject); + } else { + next.resolve(v); + } + } catch (e) { + next.reject(e); + } + }, function (e) { + var err; + + try { + if (!no || typeof no !== 'function') { + next.reject(e); + } else { + err = no(e); + + if (err && err.then) { + err.then(next.resolve, next.reject); + } else { + next.resolve(err); + } + } + } catch (e2) { + next.reject(e2); + } + }); + + return next.promise; + }, + + fail: function (no) { + return p.promise.then(null, no); + }, + + end: function () { + p.errback(function (e) { + throw e; + }); + } + } + }); + }; + + prim.serial = function (ary) { + var result = prim().resolve().promise; + each(ary, function (item) { + result = result.then(function () { + return item(); + }); + }); + return result; + }; + + prim.nextTick = typeof setImmediate === 'function' ? setImmediate : + (typeof process !== 'undefined' && process.nextTick ? + process.nextTick : (typeof setTimeout !== 'undefined' ? + function (fn) { + setTimeout(fn, 0); + } : function (fn) { + fn(); + })); + + if (typeof define === 'function' && define.amd) { + define('prim', function () { return prim; }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = prim; + } +}()); +if(env === 'browser') { +/*jslint strict: false */ +/*global define: false, load: false */ + +//Just a stub for use with uglify's consolidator.js +define('browser/assert', function () { + return {}; +}); + +} + +if(env === 'node') { +/*jslint strict: false */ +/*global define: false, load: false */ + +//Needed so that rhino/assert can return a stub for uglify's consolidator.js +define('node/assert', ['assert'], function (assert) { + return assert; +}); + +} + +if(env === 'rhino') { +/*jslint strict: false */ +/*global define: false, load: false */ + +//Just a stub for use with uglify's consolidator.js +define('rhino/assert', function () { + return {}; +}); + +} + +if(env === 'xpconnect') { +/*jslint strict: false */ +/*global define: false, load: false */ + +//Just a stub for use with uglify's consolidator.js +define('xpconnect/assert', function () { + return {}; +}); + +} + +if(env === 'browser') { +/*jslint strict: false */ +/*global define: false, process: false */ + +define('browser/args', function () { + //Always expect config via an API call + return []; +}); + +} + +if(env === 'node') { +/*jslint strict: false */ +/*global define: false, process: false */ + +define('node/args', function () { + //Do not return the "node" or "r.js" arguments + var args = process.argv.slice(2); + + //Ignore any command option used for main x.js branching + if (args[0] && args[0].indexOf('-') === 0) { + args = args.slice(1); + } + + return args; +}); + +} + +if(env === 'rhino') { +/*jslint strict: false */ +/*global define: false, process: false */ + +var jsLibRhinoArgs = (typeof rhinoArgs !== 'undefined' && rhinoArgs) || [].concat(Array.prototype.slice.call(arguments, 0)); + +define('rhino/args', function () { + var args = jsLibRhinoArgs; + + //Ignore any command option used for main x.js branching + if (args[0] && args[0].indexOf('-') === 0) { + args = args.slice(1); + } + + return args; +}); + +} + +if(env === 'xpconnect') { +/*jslint strict: false */ +/*global define, xpconnectArgs */ + +var jsLibXpConnectArgs = (typeof xpconnectArgs !== 'undefined' && xpconnectArgs) || [].concat(Array.prototype.slice.call(arguments, 0)); + +define('xpconnect/args', function () { + var args = jsLibXpConnectArgs; + + //Ignore any command option used for main x.js branching + if (args[0] && args[0].indexOf('-') === 0) { + args = args.slice(1); + } + + return args; +}); + +} + +if(env === 'browser') { +/*jslint strict: false */ +/*global define: false, console: false */ + +define('browser/load', ['./file'], function (file) { + function load(fileName) { + eval(file.readFile(fileName)); + } + + return load; +}); + +} + +if(env === 'node') { +/*jslint strict: false */ +/*global define: false, console: false */ + +define('node/load', ['fs'], function (fs) { + function load(fileName) { + var contents = fs.readFileSync(fileName, 'utf8'); + process.compile(contents, fileName); + } + + return load; +}); + +} + +if(env === 'rhino') { +/*jslint strict: false */ +/*global define: false, load: false */ + +define('rhino/load', function () { + return load; +}); + +} + +if(env === 'xpconnect') { +/*jslint strict: false */ +/*global define: false, load: false */ + +define('xpconnect/load', function () { + return load; +}); + +} + +if(env === 'browser') { +/*jslint sloppy: true, nomen: true */ +/*global require, define, console, XMLHttpRequest, requirejs, location */ + +define('browser/file', ['prim'], function (prim) { + + var file, + currDirRegExp = /^\.(\/|$)/; + + function frontSlash(path) { + return path.replace(/\\/g, '/'); + } + + function exists(path) { + var status, xhr = new XMLHttpRequest(); + + //Oh yeah, that is right SYNC IO. Behold its glory + //and horrible blocking behavior. + xhr.open('HEAD', path, false); + xhr.send(); + status = xhr.status; + + return status === 200 || status === 304; + } + + function mkDir(dir) { + console.log('mkDir is no-op in browser'); + } + + function mkFullDir(dir) { + console.log('mkFullDir is no-op in browser'); + } + + file = { + backSlashRegExp: /\\/g, + exclusionRegExp: /^\./, + getLineSeparator: function () { + return '/'; + }, + + exists: function (fileName) { + return exists(fileName); + }, + + parent: function (fileName) { + var parts = fileName.split('/'); + parts.pop(); + return parts.join('/'); + }, + + /** + * Gets the absolute file path as a string, normalized + * to using front slashes for path separators. + * @param {String} fileName + */ + absPath: function (fileName) { + var dir; + if (currDirRegExp.test(fileName)) { + dir = frontSlash(location.href); + if (dir.indexOf('/') !== -1) { + dir = dir.split('/'); + + //Pull off protocol and host, just want + //to allow paths (other build parts, like + //require._isSupportedBuildUrl do not support + //full URLs), but a full path from + //the root. + dir.splice(0, 3); + + dir.pop(); + dir = '/' + dir.join('/'); + } + + fileName = dir + fileName.substring(1); + } + + return fileName; + }, + + normalize: function (fileName) { + return fileName; + }, + + isFile: function (path) { + return true; + }, + + isDirectory: function (path) { + return false; + }, + + getFilteredFileList: function (startDir, regExpFilters, makeUnixPaths) { + console.log('file.getFilteredFileList is no-op in browser'); + }, + + copyDir: function (srcDir, destDir, regExpFilter, onlyCopyNew) { + console.log('file.copyDir is no-op in browser'); + + }, + + copyFile: function (srcFileName, destFileName, onlyCopyNew) { + console.log('file.copyFile is no-op in browser'); + }, + + /** + * Renames a file. May fail if "to" already exists or is on another drive. + */ + renameFile: function (from, to) { + console.log('file.renameFile is no-op in browser'); + }, + + /** + * Reads a *text* file. + */ + readFile: function (path, encoding) { + var xhr = new XMLHttpRequest(); + + //Oh yeah, that is right SYNC IO. Behold its glory + //and horrible blocking behavior. + xhr.open('GET', path, false); + xhr.send(); + + return xhr.responseText; + }, + + readFileAsync: function (path, encoding) { + var xhr = new XMLHttpRequest(), + d = prim(); + + xhr.open('GET', path, true); + xhr.send(); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status > 400) { + d.reject(new Error('Status: ' + xhr.status + ': ' + xhr.statusText)); + } else { + d.resolve(xhr.responseText); + } + } + }; + + return d.promise; + }, + + saveUtf8File: function (fileName, fileContents) { + //summary: saves a *text* file using UTF-8 encoding. + file.saveFile(fileName, fileContents, "utf8"); + }, + + saveFile: function (fileName, fileContents, encoding) { + requirejs.browser.saveFile(fileName, fileContents, encoding); + }, + + deleteFile: function (fileName) { + console.log('file.deleteFile is no-op in browser'); + }, + + /** + * Deletes any empty directories under the given directory. + */ + deleteEmptyDirs: function (startDir) { + console.log('file.deleteEmptyDirs is no-op in browser'); + } + }; + + return file; + +}); + +} + +if(env === 'node') { +/*jslint plusplus: false, octal:false, strict: false */ +/*global define: false, process: false */ + +define('node/file', ['fs', 'path', 'prim'], function (fs, path, prim) { + + var isWindows = process.platform === 'win32', + windowsDriveRegExp = /^[a-zA-Z]\:\/$/, + file; + + function frontSlash(path) { + return path.replace(/\\/g, '/'); + } + + function exists(path) { + if (isWindows && path.charAt(path.length - 1) === '/' && + path.charAt(path.length - 2) !== ':') { + path = path.substring(0, path.length - 1); + } + + try { + fs.statSync(path); + return true; + } catch (e) { + return false; + } + } + + function mkDir(dir) { + if (!exists(dir) && (!isWindows || !windowsDriveRegExp.test(dir))) { + fs.mkdirSync(dir, 511); + } + } + + function mkFullDir(dir) { + var parts = dir.split('/'), + currDir = '', + first = true; + + parts.forEach(function (part) { + //First part may be empty string if path starts with a slash. + currDir += part + '/'; + first = false; + + if (part) { + mkDir(currDir); + } + }); + } + + file = { + backSlashRegExp: /\\/g, + exclusionRegExp: /^\./, + getLineSeparator: function () { + return '/'; + }, + + exists: function (fileName) { + return exists(fileName); + }, + + parent: function (fileName) { + var parts = fileName.split('/'); + parts.pop(); + return parts.join('/'); + }, + + /** + * Gets the absolute file path as a string, normalized + * to using front slashes for path separators. + * @param {String} fileName + */ + absPath: function (fileName) { + return frontSlash(path.normalize(frontSlash(fs.realpathSync(fileName)))); + }, + + normalize: function (fileName) { + return frontSlash(path.normalize(fileName)); + }, + + isFile: function (path) { + return fs.statSync(path).isFile(); + }, + + isDirectory: function (path) { + return fs.statSync(path).isDirectory(); + }, + + getFilteredFileList: function (/*String*/startDir, /*RegExp*/regExpFilters, /*boolean?*/makeUnixPaths) { + //summary: Recurses startDir and finds matches to the files that match regExpFilters.include + //and do not match regExpFilters.exclude. Or just one regexp can be passed in for regExpFilters, + //and it will be treated as the "include" case. + //Ignores files/directories that start with a period (.) unless exclusionRegExp + //is set to another value. + var files = [], topDir, regExpInclude, regExpExclude, dirFileArray, + i, stat, filePath, ok, dirFiles, fileName; + + topDir = startDir; + + regExpInclude = regExpFilters.include || regExpFilters; + regExpExclude = regExpFilters.exclude || null; + + if (file.exists(topDir)) { + dirFileArray = fs.readdirSync(topDir); + for (i = 0; i < dirFileArray.length; i++) { + fileName = dirFileArray[i]; + filePath = path.join(topDir, fileName); + stat = fs.statSync(filePath); + if (stat.isFile()) { + if (makeUnixPaths) { + //Make sure we have a JS string. + if (filePath.indexOf("/") === -1) { + filePath = frontSlash(filePath); + } + } + + ok = true; + if (regExpInclude) { + ok = filePath.match(regExpInclude); + } + if (ok && regExpExclude) { + ok = !filePath.match(regExpExclude); + } + + if (ok && (!file.exclusionRegExp || + !file.exclusionRegExp.test(fileName))) { + files.push(filePath); + } + } else if (stat.isDirectory() && + (!file.exclusionRegExp || !file.exclusionRegExp.test(fileName))) { + dirFiles = this.getFilteredFileList(filePath, regExpFilters, makeUnixPaths); + //Do not use push.apply for dir listings, can hit limit of max number + //of arguments to a function call, #921. + dirFiles.forEach(function (dirFile) { + files.push(dirFile); + }); + } + } + } + + return files; //Array + }, + + copyDir: function (/*String*/srcDir, /*String*/destDir, /*RegExp?*/regExpFilter, /*boolean?*/onlyCopyNew) { + //summary: copies files from srcDir to destDir using the regExpFilter to determine if the + //file should be copied. Returns a list file name strings of the destinations that were copied. + regExpFilter = regExpFilter || /\w/; + + //Normalize th directory names, but keep front slashes. + //path module on windows now returns backslashed paths. + srcDir = frontSlash(path.normalize(srcDir)); + destDir = frontSlash(path.normalize(destDir)); + + var fileNames = file.getFilteredFileList(srcDir, regExpFilter, true), + copiedFiles = [], i, srcFileName, destFileName; + + for (i = 0; i < fileNames.length; i++) { + srcFileName = fileNames[i]; + destFileName = srcFileName.replace(srcDir, destDir); + + if (file.copyFile(srcFileName, destFileName, onlyCopyNew)) { + copiedFiles.push(destFileName); + } + } + + return copiedFiles.length ? copiedFiles : null; //Array or null + }, + + copyFile: function (/*String*/srcFileName, /*String*/destFileName, /*boolean?*/onlyCopyNew) { + //summary: copies srcFileName to destFileName. If onlyCopyNew is set, it only copies the file if + //srcFileName is newer than destFileName. Returns a boolean indicating if the copy occurred. + var parentDir; + + //logger.trace("Src filename: " + srcFileName); + //logger.trace("Dest filename: " + destFileName); + + //If onlyCopyNew is true, then compare dates and only copy if the src is newer + //than dest. + if (onlyCopyNew) { + if (file.exists(destFileName) && fs.statSync(destFileName).mtime.getTime() >= fs.statSync(srcFileName).mtime.getTime()) { + return false; //Boolean + } + } + + //Make sure destination dir exists. + parentDir = path.dirname(destFileName); + if (!file.exists(parentDir)) { + mkFullDir(parentDir); + } + + fs.writeFileSync(destFileName, fs.readFileSync(srcFileName, 'binary'), 'binary'); + + return true; //Boolean + }, + + /** + * Renames a file. May fail if "to" already exists or is on another drive. + */ + renameFile: function (from, to) { + return fs.renameSync(from, to); + }, + + /** + * Reads a *text* file. + */ + readFile: function (/*String*/path, /*String?*/encoding) { + if (encoding === 'utf-8') { + encoding = 'utf8'; + } + if (!encoding) { + encoding = 'utf8'; + } + + var text = fs.readFileSync(path, encoding); + + //Hmm, would not expect to get A BOM, but it seems to happen, + //remove it just in case. + if (text.indexOf('\uFEFF') === 0) { + text = text.substring(1, text.length); + } + + return text; + }, + + readFileAsync: function (path, encoding) { + var d = prim(); + try { + d.resolve(file.readFile(path, encoding)); + } catch (e) { + d.reject(e); + } + return d.promise; + }, + + saveUtf8File: function (/*String*/fileName, /*String*/fileContents) { + //summary: saves a *text* file using UTF-8 encoding. + file.saveFile(fileName, fileContents, "utf8"); + }, + + saveFile: function (/*String*/fileName, /*String*/fileContents, /*String?*/encoding) { + //summary: saves a *text* file. + var parentDir; + + if (encoding === 'utf-8') { + encoding = 'utf8'; + } + if (!encoding) { + encoding = 'utf8'; + } + + //Make sure destination directories exist. + parentDir = path.dirname(fileName); + if (!file.exists(parentDir)) { + mkFullDir(parentDir); + } + + fs.writeFileSync(fileName, fileContents, encoding); + }, + + deleteFile: function (/*String*/fileName) { + //summary: deletes a file or directory if it exists. + var files, i, stat; + if (file.exists(fileName)) { + stat = fs.lstatSync(fileName); + if (stat.isDirectory()) { + files = fs.readdirSync(fileName); + for (i = 0; i < files.length; i++) { + this.deleteFile(path.join(fileName, files[i])); + } + fs.rmdirSync(fileName); + } else { + fs.unlinkSync(fileName); + } + } + }, + + + /** + * Deletes any empty directories under the given directory. + */ + deleteEmptyDirs: function (startDir) { + var dirFileArray, i, fileName, filePath, stat; + + if (file.exists(startDir)) { + dirFileArray = fs.readdirSync(startDir); + for (i = 0; i < dirFileArray.length; i++) { + fileName = dirFileArray[i]; + filePath = path.join(startDir, fileName); + stat = fs.lstatSync(filePath); + if (stat.isDirectory()) { + file.deleteEmptyDirs(filePath); + } + } + + //If directory is now empty, remove it. + if (fs.readdirSync(startDir).length === 0) { + file.deleteFile(startDir); + } + } + } + }; + + return file; + +}); + +} + +if(env === 'rhino') { +//Helper functions to deal with file I/O. + +/*jslint plusplus: false */ +/*global java: false, define: false */ + +define('rhino/file', ['prim'], function (prim) { + var file = { + backSlashRegExp: /\\/g, + + exclusionRegExp: /^\./, + + getLineSeparator: function () { + return file.lineSeparator; + }, + + lineSeparator: java.lang.System.getProperty("line.separator"), //Java String + + exists: function (fileName) { + return (new java.io.File(fileName)).exists(); + }, + + parent: function (fileName) { + return file.absPath((new java.io.File(fileName)).getParentFile()); + }, + + normalize: function (fileName) { + return file.absPath(fileName); + }, + + isFile: function (path) { + return (new java.io.File(path)).isFile(); + }, + + isDirectory: function (path) { + return (new java.io.File(path)).isDirectory(); + }, + + /** + * Gets the absolute file path as a string, normalized + * to using front slashes for path separators. + * @param {java.io.File||String} file + */ + absPath: function (fileObj) { + if (typeof fileObj === "string") { + fileObj = new java.io.File(fileObj); + } + return (fileObj.getCanonicalPath() + "").replace(file.backSlashRegExp, "/"); + }, + + getFilteredFileList: function (/*String*/startDir, /*RegExp*/regExpFilters, /*boolean?*/makeUnixPaths, /*boolean?*/startDirIsJavaObject) { + //summary: Recurses startDir and finds matches to the files that match regExpFilters.include + //and do not match regExpFilters.exclude. Or just one regexp can be passed in for regExpFilters, + //and it will be treated as the "include" case. + //Ignores files/directories that start with a period (.) unless exclusionRegExp + //is set to another value. + var files = [], topDir, regExpInclude, regExpExclude, dirFileArray, + i, fileObj, filePath, ok, dirFiles; + + topDir = startDir; + if (!startDirIsJavaObject) { + topDir = new java.io.File(startDir); + } + + regExpInclude = regExpFilters.include || regExpFilters; + regExpExclude = regExpFilters.exclude || null; + + if (topDir.exists()) { + dirFileArray = topDir.listFiles(); + for (i = 0; i < dirFileArray.length; i++) { + fileObj = dirFileArray[i]; + if (fileObj.isFile()) { + filePath = fileObj.getPath(); + if (makeUnixPaths) { + //Make sure we have a JS string. + filePath = String(filePath); + if (filePath.indexOf("/") === -1) { + filePath = filePath.replace(/\\/g, "/"); + } + } + + ok = true; + if (regExpInclude) { + ok = filePath.match(regExpInclude); + } + if (ok && regExpExclude) { + ok = !filePath.match(regExpExclude); + } + + if (ok && (!file.exclusionRegExp || + !file.exclusionRegExp.test(fileObj.getName()))) { + files.push(filePath); + } + } else if (fileObj.isDirectory() && + (!file.exclusionRegExp || !file.exclusionRegExp.test(fileObj.getName()))) { + dirFiles = this.getFilteredFileList(fileObj, regExpFilters, makeUnixPaths, true); + //Do not use push.apply for dir listings, can hit limit of max number + //of arguments to a function call, #921. + dirFiles.forEach(function (dirFile) { + files.push(dirFile); + }); + } + } + } + + return files; //Array + }, + + copyDir: function (/*String*/srcDir, /*String*/destDir, /*RegExp?*/regExpFilter, /*boolean?*/onlyCopyNew) { + //summary: copies files from srcDir to destDir using the regExpFilter to determine if the + //file should be copied. Returns a list file name strings of the destinations that were copied. + regExpFilter = regExpFilter || /\w/; + + var fileNames = file.getFilteredFileList(srcDir, regExpFilter, true), + copiedFiles = [], i, srcFileName, destFileName; + + for (i = 0; i < fileNames.length; i++) { + srcFileName = fileNames[i]; + destFileName = srcFileName.replace(srcDir, destDir); + + if (file.copyFile(srcFileName, destFileName, onlyCopyNew)) { + copiedFiles.push(destFileName); + } + } + + return copiedFiles.length ? copiedFiles : null; //Array or null + }, + + copyFile: function (/*String*/srcFileName, /*String*/destFileName, /*boolean?*/onlyCopyNew) { + //summary: copies srcFileName to destFileName. If onlyCopyNew is set, it only copies the file if + //srcFileName is newer than destFileName. Returns a boolean indicating if the copy occurred. + var destFile = new java.io.File(destFileName), srcFile, parentDir, + srcChannel, destChannel; + + //logger.trace("Src filename: " + srcFileName); + //logger.trace("Dest filename: " + destFileName); + + //If onlyCopyNew is true, then compare dates and only copy if the src is newer + //than dest. + if (onlyCopyNew) { + srcFile = new java.io.File(srcFileName); + if (destFile.exists() && destFile.lastModified() >= srcFile.lastModified()) { + return false; //Boolean + } + } + + //Make sure destination dir exists. + parentDir = destFile.getParentFile(); + if (!parentDir.exists()) { + if (!parentDir.mkdirs()) { + throw "Could not create directory: " + parentDir.getCanonicalPath(); + } + } + + //Java's version of copy file. + srcChannel = new java.io.FileInputStream(srcFileName).getChannel(); + destChannel = new java.io.FileOutputStream(destFileName).getChannel(); + destChannel.transferFrom(srcChannel, 0, srcChannel.size()); + srcChannel.close(); + destChannel.close(); + + return true; //Boolean + }, + + /** + * Renames a file. May fail if "to" already exists or is on another drive. + */ + renameFile: function (from, to) { + return (new java.io.File(from)).renameTo((new java.io.File(to))); + }, + + readFile: function (/*String*/path, /*String?*/encoding) { + //A file read function that can deal with BOMs + encoding = encoding || "utf-8"; + var fileObj = new java.io.File(path), + input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(fileObj), encoding)), + stringBuffer, line; + try { + stringBuffer = new java.lang.StringBuffer(); + line = input.readLine(); + + // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 + // http://www.unicode.org/faq/utf_bom.html + + // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 + if (line && line.length() && line.charAt(0) === 0xfeff) { + // Eat the BOM, since we've already found the encoding on this file, + // and we plan to concatenating this buffer with others; the BOM should + // only appear at the top of a file. + line = line.substring(1); + } + while (line !== null) { + stringBuffer.append(line); + stringBuffer.append(file.lineSeparator); + line = input.readLine(); + } + //Make sure we return a JavaScript string and not a Java string. + return String(stringBuffer.toString()); //String + } finally { + input.close(); + } + }, + + readFileAsync: function (path, encoding) { + var d = prim(); + try { + d.resolve(file.readFile(path, encoding)); + } catch (e) { + d.reject(e); + } + return d.promise; + }, + + saveUtf8File: function (/*String*/fileName, /*String*/fileContents) { + //summary: saves a file using UTF-8 encoding. + file.saveFile(fileName, fileContents, "utf-8"); + }, + + saveFile: function (/*String*/fileName, /*String*/fileContents, /*String?*/encoding) { + //summary: saves a file. + var outFile = new java.io.File(fileName), outWriter, parentDir, os; + + parentDir = outFile.getAbsoluteFile().getParentFile(); + if (!parentDir.exists()) { + if (!parentDir.mkdirs()) { + throw "Could not create directory: " + parentDir.getAbsolutePath(); + } + } + + if (encoding) { + outWriter = new java.io.OutputStreamWriter(new java.io.FileOutputStream(outFile), encoding); + } else { + outWriter = new java.io.OutputStreamWriter(new java.io.FileOutputStream(outFile)); + } + + os = new java.io.BufferedWriter(outWriter); + try { + //If in Nashorn, need to coerce the JS string to a Java string so that + //writer.write method dispatch correctly detects the type. + if (typeof importPackage !== 'undefined') { + os.write(fileContents); + } else { + os.write(new java.lang.String(fileContents)); + } + } finally { + os.close(); + } + }, + + deleteFile: function (/*String*/fileName) { + //summary: deletes a file or directory if it exists. + var fileObj = new java.io.File(fileName), files, i; + if (fileObj.exists()) { + if (fileObj.isDirectory()) { + files = fileObj.listFiles(); + for (i = 0; i < files.length; i++) { + this.deleteFile(files[i]); + } + } + fileObj["delete"](); + } + }, + + /** + * Deletes any empty directories under the given directory. + * The startDirIsJavaObject is private to this implementation's + * recursion needs. + */ + deleteEmptyDirs: function (startDir, startDirIsJavaObject) { + var topDir = startDir, + dirFileArray, i, fileObj; + + if (!startDirIsJavaObject) { + topDir = new java.io.File(startDir); + } + + if (topDir.exists()) { + dirFileArray = topDir.listFiles(); + for (i = 0; i < dirFileArray.length; i++) { + fileObj = dirFileArray[i]; + if (fileObj.isDirectory()) { + file.deleteEmptyDirs(fileObj, true); + } + } + + //If the directory is empty now, delete it. + if (topDir.listFiles().length === 0) { + file.deleteFile(String(topDir.getPath())); + } + } + } + }; + + return file; +}); + +} + +if(env === 'xpconnect') { +//Helper functions to deal with file I/O. + +/*jslint plusplus: false */ +/*global define, Components, xpcUtil */ + +define('xpconnect/file', ['prim'], function (prim) { + var file, + Cc = Components.classes, + Ci = Components.interfaces, + //Depends on xpcUtil which is set up in x.js + xpfile = xpcUtil.xpfile; + + function mkFullDir(dirObj) { + //1 is DIRECTORY_TYPE, 511 is 0777 permissions + if (!dirObj.exists()) { + dirObj.create(1, 511); + } + } + + file = { + backSlashRegExp: /\\/g, + + exclusionRegExp: /^\./, + + getLineSeparator: function () { + return file.lineSeparator; + }, + + lineSeparator: ('@mozilla.org/windows-registry-key;1' in Cc) ? + '\r\n' : '\n', + + exists: function (fileName) { + return xpfile(fileName).exists(); + }, + + parent: function (fileName) { + return xpfile(fileName).parent; + }, + + normalize: function (fileName) { + return file.absPath(fileName); + }, + + isFile: function (path) { + return xpfile(path).isFile(); + }, + + isDirectory: function (path) { + return xpfile(path).isDirectory(); + }, + + /** + * Gets the absolute file path as a string, normalized + * to using front slashes for path separators. + * @param {java.io.File||String} file + */ + absPath: function (fileObj) { + if (typeof fileObj === "string") { + fileObj = xpfile(fileObj); + } + return fileObj.path; + }, + + getFilteredFileList: function (/*String*/startDir, /*RegExp*/regExpFilters, /*boolean?*/makeUnixPaths, /*boolean?*/startDirIsObject) { + //summary: Recurses startDir and finds matches to the files that match regExpFilters.include + //and do not match regExpFilters.exclude. Or just one regexp can be passed in for regExpFilters, + //and it will be treated as the "include" case. + //Ignores files/directories that start with a period (.) unless exclusionRegExp + //is set to another value. + var files = [], topDir, regExpInclude, regExpExclude, dirFileArray, + fileObj, filePath, ok, dirFiles; + + topDir = startDir; + if (!startDirIsObject) { + topDir = xpfile(startDir); + } + + regExpInclude = regExpFilters.include || regExpFilters; + regExpExclude = regExpFilters.exclude || null; + + if (topDir.exists()) { + dirFileArray = topDir.directoryEntries; + while (dirFileArray.hasMoreElements()) { + fileObj = dirFileArray.getNext().QueryInterface(Ci.nsILocalFile); + if (fileObj.isFile()) { + filePath = fileObj.path; + if (makeUnixPaths) { + if (filePath.indexOf("/") === -1) { + filePath = filePath.replace(/\\/g, "/"); + } + } + + ok = true; + if (regExpInclude) { + ok = filePath.match(regExpInclude); + } + if (ok && regExpExclude) { + ok = !filePath.match(regExpExclude); + } + + if (ok && (!file.exclusionRegExp || + !file.exclusionRegExp.test(fileObj.leafName))) { + files.push(filePath); + } + } else if (fileObj.isDirectory() && + (!file.exclusionRegExp || !file.exclusionRegExp.test(fileObj.leafName))) { + dirFiles = this.getFilteredFileList(fileObj, regExpFilters, makeUnixPaths, true); + //Do not use push.apply for dir listings, can hit limit of max number + //of arguments to a function call, #921. + dirFiles.forEach(function (dirFile) { + files.push(dirFile); + }); + } + } + } + + return files; //Array + }, + + copyDir: function (/*String*/srcDir, /*String*/destDir, /*RegExp?*/regExpFilter, /*boolean?*/onlyCopyNew) { + //summary: copies files from srcDir to destDir using the regExpFilter to determine if the + //file should be copied. Returns a list file name strings of the destinations that were copied. + regExpFilter = regExpFilter || /\w/; + + var fileNames = file.getFilteredFileList(srcDir, regExpFilter, true), + copiedFiles = [], i, srcFileName, destFileName; + + for (i = 0; i < fileNames.length; i += 1) { + srcFileName = fileNames[i]; + destFileName = srcFileName.replace(srcDir, destDir); + + if (file.copyFile(srcFileName, destFileName, onlyCopyNew)) { + copiedFiles.push(destFileName); + } + } + + return copiedFiles.length ? copiedFiles : null; //Array or null + }, + + copyFile: function (/*String*/srcFileName, /*String*/destFileName, /*boolean?*/onlyCopyNew) { + //summary: copies srcFileName to destFileName. If onlyCopyNew is set, it only copies the file if + //srcFileName is newer than destFileName. Returns a boolean indicating if the copy occurred. + var destFile = xpfile(destFileName), + srcFile = xpfile(srcFileName); + + //logger.trace("Src filename: " + srcFileName); + //logger.trace("Dest filename: " + destFileName); + + //If onlyCopyNew is true, then compare dates and only copy if the src is newer + //than dest. + if (onlyCopyNew) { + if (destFile.exists() && destFile.lastModifiedTime >= srcFile.lastModifiedTime) { + return false; //Boolean + } + } + + srcFile.copyTo(destFile.parent, destFile.leafName); + + return true; //Boolean + }, + + /** + * Renames a file. May fail if "to" already exists or is on another drive. + */ + renameFile: function (from, to) { + var toFile = xpfile(to); + return xpfile(from).moveTo(toFile.parent, toFile.leafName); + }, + + readFile: xpcUtil.readFile, + + readFileAsync: function (path, encoding) { + var d = prim(); + try { + d.resolve(file.readFile(path, encoding)); + } catch (e) { + d.reject(e); + } + return d.promise; + }, + + saveUtf8File: function (/*String*/fileName, /*String*/fileContents) { + //summary: saves a file using UTF-8 encoding. + file.saveFile(fileName, fileContents, "utf-8"); + }, + + saveFile: function (/*String*/fileName, /*String*/fileContents, /*String?*/encoding) { + var outStream, convertStream, + fileObj = xpfile(fileName); + + mkFullDir(fileObj.parent); + + try { + outStream = Cc['@mozilla.org/network/file-output-stream;1'] + .createInstance(Ci.nsIFileOutputStream); + //438 is decimal for 0777 + outStream.init(fileObj, 0x02 | 0x08 | 0x20, 511, 0); + + convertStream = Cc['@mozilla.org/intl/converter-output-stream;1'] + .createInstance(Ci.nsIConverterOutputStream); + + convertStream.init(outStream, encoding, 0, 0); + convertStream.writeString(fileContents); + } catch (e) { + throw new Error((fileObj && fileObj.path || '') + ': ' + e); + } finally { + if (convertStream) { + convertStream.close(); + } + if (outStream) { + outStream.close(); + } + } + }, + + deleteFile: function (/*String*/fileName) { + //summary: deletes a file or directory if it exists. + var fileObj = xpfile(fileName); + if (fileObj.exists()) { + fileObj.remove(true); + } + }, + + /** + * Deletes any empty directories under the given directory. + * The startDirIsJavaObject is private to this implementation's + * recursion needs. + */ + deleteEmptyDirs: function (startDir, startDirIsObject) { + var topDir = startDir, + dirFileArray, fileObj; + + if (!startDirIsObject) { + topDir = xpfile(startDir); + } + + if (topDir.exists()) { + dirFileArray = topDir.directoryEntries; + while (dirFileArray.hasMoreElements()) { + fileObj = dirFileArray.getNext().QueryInterface(Ci.nsILocalFile); + + if (fileObj.isDirectory()) { + file.deleteEmptyDirs(fileObj, true); + } + } + + //If the directory is empty now, delete it. + dirFileArray = topDir.directoryEntries; + if (!dirFileArray.hasMoreElements()) { + file.deleteFile(topDir.path); + } + } + } + }; + + return file; +}); + +} + +if(env === 'browser') { +/*global process */ +define('browser/quit', function () { + 'use strict'; + return function (code) { + }; +}); +} + +if(env === 'node') { +/*global process */ +define('node/quit', function () { + 'use strict'; + return function (code) { + var draining = 0; + var exit = function () { + if (draining === 0) { + process.exit(code); + } else { + draining -= 1; + } + }; + if (process.stdout.bufferSize) { + draining += 1; + process.stdout.once('drain', exit); + } + if (process.stderr.bufferSize) { + draining += 1; + process.stderr.once('drain', exit); + } + exit(); + }; +}); + +} + +if(env === 'rhino') { +/*global quit */ +define('rhino/quit', function () { + 'use strict'; + return function (code) { + return quit(code); + }; +}); + +} + +if(env === 'xpconnect') { +/*global quit */ +define('xpconnect/quit', function () { + 'use strict'; + return function (code) { + return quit(code); + }; +}); + +} + +if(env === 'browser') { +/*jslint strict: false */ +/*global define: false, console: false */ + +define('browser/print', function () { + function print(msg) { + console.log(msg); + } + + return print; +}); + +} + +if(env === 'node') { +/*jslint strict: false */ +/*global define: false, console: false */ + +define('node/print', function () { + function print(msg) { + console.log(msg); + } + + return print; +}); + +} + +if(env === 'rhino') { +/*jslint strict: false */ +/*global define: false, print: false */ + +define('rhino/print', function () { + return print; +}); + +} + +if(env === 'xpconnect') { +/*jslint strict: false */ +/*global define: false, print: false */ + +define('xpconnect/print', function () { + return print; +}); + +} +/*jslint nomen: false, strict: false */ +/*global define: false */ + +define('logger', ['env!env/print'], function (print) { + var logger = { + TRACE: 0, + INFO: 1, + WARN: 2, + ERROR: 3, + SILENT: 4, + level: 0, + logPrefix: "", + + logLevel: function( level ) { + this.level = level; + }, + + trace: function (message) { + if (this.level <= this.TRACE) { + this._print(message); + } + }, + + info: function (message) { + if (this.level <= this.INFO) { + this._print(message); + } + }, + + warn: function (message) { + if (this.level <= this.WARN) { + this._print(message); + } + }, + + error: function (message) { + if (this.level <= this.ERROR) { + this._print(message); + } + }, + + _print: function (message) { + this._sysPrint((this.logPrefix ? (this.logPrefix + " ") : "") + message); + }, + + _sysPrint: function (message) { + print(message); + } + }; + + return logger; +}); +//Just a blank file to use when building the optimizer with the optimizer, +//so that the build does not attempt to inline some env modules, +//like Node's fs and path. + +/* + Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +(function (root, factory) { + 'use strict'; + + // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, + // Rhino, and plain browser loading. + + /* istanbul ignore next */ + if (typeof define === 'function' && define.amd) { + define('esprima', ['exports'], factory); + } else if (typeof exports !== 'undefined') { + factory(exports); + } else { + factory((root.esprima = {})); + } +}(this, function (exports) { + 'use strict'; + + var Token, + TokenName, + FnExprTokens, + Syntax, + PlaceHolders, + Messages, + Regex, + source, + strict, + index, + lineNumber, + lineStart, + hasLineTerminator, + lastIndex, + lastLineNumber, + lastLineStart, + startIndex, + startLineNumber, + startLineStart, + scanning, + length, + lookahead, + state, + extra, + isBindingElement, + isAssignmentTarget, + firstCoverInitializedNameError; + + Token = { + BooleanLiteral: 1, + EOF: 2, + Identifier: 3, + Keyword: 4, + NullLiteral: 5, + NumericLiteral: 6, + Punctuator: 7, + StringLiteral: 8, + RegularExpression: 9, + Template: 10 + }; + + TokenName = {}; + TokenName[Token.BooleanLiteral] = 'Boolean'; + TokenName[Token.EOF] = ''; + TokenName[Token.Identifier] = 'Identifier'; + TokenName[Token.Keyword] = 'Keyword'; + TokenName[Token.NullLiteral] = 'Null'; + TokenName[Token.NumericLiteral] = 'Numeric'; + TokenName[Token.Punctuator] = 'Punctuator'; + TokenName[Token.StringLiteral] = 'String'; + TokenName[Token.RegularExpression] = 'RegularExpression'; + TokenName[Token.Template] = 'Template'; + + // A function following one of those tokens is an expression. + FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new', + 'return', 'case', 'delete', 'throw', 'void', + // assignment operators + '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=', + '&=', '|=', '^=', ',', + // binary/unary operators + '+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&', + '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=', + '<=', '<', '>', '!=', '!==']; + + Syntax = { + AssignmentExpression: 'AssignmentExpression', + AssignmentPattern: 'AssignmentPattern', + ArrayExpression: 'ArrayExpression', + ArrayPattern: 'ArrayPattern', + ArrowFunctionExpression: 'ArrowFunctionExpression', + BlockStatement: 'BlockStatement', + BinaryExpression: 'BinaryExpression', + BreakStatement: 'BreakStatement', + CallExpression: 'CallExpression', + CatchClause: 'CatchClause', + ClassBody: 'ClassBody', + ClassDeclaration: 'ClassDeclaration', + ClassExpression: 'ClassExpression', + ConditionalExpression: 'ConditionalExpression', + ContinueStatement: 'ContinueStatement', + DoWhileStatement: 'DoWhileStatement', + DebuggerStatement: 'DebuggerStatement', + EmptyStatement: 'EmptyStatement', + ExportAllDeclaration: 'ExportAllDeclaration', + ExportDefaultDeclaration: 'ExportDefaultDeclaration', + ExportNamedDeclaration: 'ExportNamedDeclaration', + ExportSpecifier: 'ExportSpecifier', + ExpressionStatement: 'ExpressionStatement', + ForStatement: 'ForStatement', + ForOfStatement: 'ForOfStatement', + ForInStatement: 'ForInStatement', + FunctionDeclaration: 'FunctionDeclaration', + FunctionExpression: 'FunctionExpression', + Identifier: 'Identifier', + IfStatement: 'IfStatement', + ImportDeclaration: 'ImportDeclaration', + ImportDefaultSpecifier: 'ImportDefaultSpecifier', + ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', + ImportSpecifier: 'ImportSpecifier', + Literal: 'Literal', + LabeledStatement: 'LabeledStatement', + LogicalExpression: 'LogicalExpression', + MemberExpression: 'MemberExpression', + MetaProperty: 'MetaProperty', + MethodDefinition: 'MethodDefinition', + NewExpression: 'NewExpression', + ObjectExpression: 'ObjectExpression', + ObjectPattern: 'ObjectPattern', + Program: 'Program', + Property: 'Property', + RestElement: 'RestElement', + ReturnStatement: 'ReturnStatement', + SequenceExpression: 'SequenceExpression', + SpreadElement: 'SpreadElement', + Super: 'Super', + SwitchCase: 'SwitchCase', + SwitchStatement: 'SwitchStatement', + TaggedTemplateExpression: 'TaggedTemplateExpression', + TemplateElement: 'TemplateElement', + TemplateLiteral: 'TemplateLiteral', + ThisExpression: 'ThisExpression', + ThrowStatement: 'ThrowStatement', + TryStatement: 'TryStatement', + UnaryExpression: 'UnaryExpression', + UpdateExpression: 'UpdateExpression', + VariableDeclaration: 'VariableDeclaration', + VariableDeclarator: 'VariableDeclarator', + WhileStatement: 'WhileStatement', + WithStatement: 'WithStatement', + YieldExpression: 'YieldExpression' + }; + + PlaceHolders = { + ArrowParameterPlaceHolder: 'ArrowParameterPlaceHolder' + }; + + // Error messages should be identical to V8. + Messages = { + UnexpectedToken: 'Unexpected token %0', + UnexpectedNumber: 'Unexpected number', + UnexpectedString: 'Unexpected string', + UnexpectedIdentifier: 'Unexpected identifier', + UnexpectedReserved: 'Unexpected reserved word', + UnexpectedTemplate: 'Unexpected quasi %0', + UnexpectedEOS: 'Unexpected end of input', + NewlineAfterThrow: 'Illegal newline after throw', + InvalidRegExp: 'Invalid regular expression', + UnterminatedRegExp: 'Invalid regular expression: missing /', + InvalidLHSInAssignment: 'Invalid left-hand side in assignment', + InvalidLHSInForIn: 'Invalid left-hand side in for-in', + InvalidLHSInForLoop: 'Invalid left-hand side in for-loop', + MultipleDefaultsInSwitch: 'More than one default clause in switch statement', + NoCatchOrFinally: 'Missing catch or finally after try', + UnknownLabel: 'Undefined label \'%0\'', + Redeclaration: '%0 \'%1\' has already been declared', + IllegalContinue: 'Illegal continue statement', + IllegalBreak: 'Illegal break statement', + IllegalReturn: 'Illegal return statement', + StrictModeWith: 'Strict mode code may not include a with statement', + StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', + StrictVarName: 'Variable name may not be eval or arguments in strict mode', + StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', + StrictParamDupe: 'Strict mode function may not have duplicate parameter names', + StrictFunctionName: 'Function name may not be eval or arguments in strict mode', + StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', + StrictDelete: 'Delete of an unqualified identifier in strict mode.', + StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', + StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', + StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', + StrictReservedWord: 'Use of future reserved word in strict mode', + TemplateOctalLiteral: 'Octal literals are not allowed in template strings.', + ParameterAfterRestParameter: 'Rest parameter must be last formal parameter', + DefaultRestParameter: 'Unexpected token =', + ObjectPatternAsRestParameter: 'Unexpected token {', + DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals', + ConstructorSpecialMethod: 'Class constructor may not be an accessor', + DuplicateConstructor: 'A class may only have one constructor', + StaticPrototype: 'Classes may not have static property named prototype', + MissingFromClause: 'Unexpected token', + NoAsAfterImportNamespace: 'Unexpected token', + InvalidModuleSpecifier: 'Unexpected token', + IllegalImportDeclaration: 'Unexpected token', + IllegalExportDeclaration: 'Unexpected token', + DuplicateBinding: 'Duplicate binding %0' + }; + + // See also tools/generate-unicode-regex.js. + Regex = { + // ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierStart: + NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B2\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDE00-\uDE11\uDE13-\uDE2B\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDE00-\uDE2F\uDE44\uDE80-\uDEAA]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|\uD809[\uDC00-\uDC6E]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]/, + + // ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierPart: + NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B2\u08E4-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA69D\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2D\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDD0-\uDDDA\uDE00-\uDE11\uDE13-\uDE37\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF01-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|\uD809[\uDC00-\uDC6E]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/ + }; + + // Ensure the condition is true, otherwise throw an error. + // This is only to have a better contract semantic, i.e. another safety net + // to catch a logic error. The condition shall be fulfilled in normal case. + // Do NOT use this to enforce a certain condition on any user input. + + function assert(condition, message) { + /* istanbul ignore if */ + if (!condition) { + throw new Error('ASSERT: ' + message); + } + } + + function isDecimalDigit(ch) { + return (ch >= 0x30 && ch <= 0x39); // 0..9 + } + + function isHexDigit(ch) { + return '0123456789abcdefABCDEF'.indexOf(ch) >= 0; + } + + function isOctalDigit(ch) { + return '01234567'.indexOf(ch) >= 0; + } + + function octalToDecimal(ch) { + // \0 is not octal escape sequence + var octal = (ch !== '0'), code = '01234567'.indexOf(ch); + + if (index < length && isOctalDigit(source[index])) { + octal = true; + code = code * 8 + '01234567'.indexOf(source[index++]); + + // 3 digits are only allowed when string starts + // with 0, 1, 2, 3 + if ('0123'.indexOf(ch) >= 0 && + index < length && + isOctalDigit(source[index])) { + code = code * 8 + '01234567'.indexOf(source[index++]); + } + } + + return { + code: code, + octal: octal + }; + } + + // ECMA-262 11.2 White Space + + function isWhiteSpace(ch) { + return (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) || + (ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(ch) >= 0); + } + + // ECMA-262 11.3 Line Terminators + + function isLineTerminator(ch) { + return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029); + } + + // ECMA-262 11.6 Identifier Names and Identifiers + + function fromCodePoint(cp) { + return (cp < 0x10000) ? String.fromCharCode(cp) : + String.fromCharCode(0xD800 + ((cp - 0x10000) >> 10)) + + String.fromCharCode(0xDC00 + ((cp - 0x10000) & 1023)); + } + + function isIdentifierStart(ch) { + return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore) + (ch >= 0x41 && ch <= 0x5A) || // A..Z + (ch >= 0x61 && ch <= 0x7A) || // a..z + (ch === 0x5C) || // \ (backslash) + ((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch))); + } + + function isIdentifierPart(ch) { + return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore) + (ch >= 0x41 && ch <= 0x5A) || // A..Z + (ch >= 0x61 && ch <= 0x7A) || // a..z + (ch >= 0x30 && ch <= 0x39) || // 0..9 + (ch === 0x5C) || // \ (backslash) + ((ch >= 0x80) && Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch))); + } + + // ECMA-262 11.6.2.2 Future Reserved Words + + function isFutureReservedWord(id) { + switch (id) { + case 'enum': + case 'export': + case 'import': + case 'super': + return true; + default: + return false; + } + } + + function isStrictModeReservedWord(id) { + switch (id) { + case 'implements': + case 'interface': + case 'package': + case 'private': + case 'protected': + case 'public': + case 'static': + case 'yield': + case 'let': + return true; + default: + return false; + } + } + + function isRestrictedWord(id) { + return id === 'eval' || id === 'arguments'; + } + + // ECMA-262 11.6.2.1 Keywords + + function isKeyword(id) { + switch (id.length) { + case 2: + return (id === 'if') || (id === 'in') || (id === 'do'); + case 3: + return (id === 'var') || (id === 'for') || (id === 'new') || + (id === 'try') || (id === 'let'); + case 4: + return (id === 'this') || (id === 'else') || (id === 'case') || + (id === 'void') || (id === 'with') || (id === 'enum'); + case 5: + return (id === 'while') || (id === 'break') || (id === 'catch') || + (id === 'throw') || (id === 'const') || (id === 'yield') || + (id === 'class') || (id === 'super'); + case 6: + return (id === 'return') || (id === 'typeof') || (id === 'delete') || + (id === 'switch') || (id === 'export') || (id === 'import'); + case 7: + return (id === 'default') || (id === 'finally') || (id === 'extends'); + case 8: + return (id === 'function') || (id === 'continue') || (id === 'debugger'); + case 10: + return (id === 'instanceof'); + default: + return false; + } + } + + // ECMA-262 11.4 Comments + + function addComment(type, value, start, end, loc) { + var comment; + + assert(typeof start === 'number', 'Comment must have valid position'); + + state.lastCommentStart = start; + + comment = { + type: type, + value: value + }; + if (extra.range) { + comment.range = [start, end]; + } + if (extra.loc) { + comment.loc = loc; + } + extra.comments.push(comment); + if (extra.attachComment) { + extra.leadingComments.push(comment); + extra.trailingComments.push(comment); + } + if (extra.tokenize) { + comment.type = comment.type + 'Comment'; + if (extra.delegate) { + comment = extra.delegate(comment); + } + extra.tokens.push(comment); + } + } + + function skipSingleLineComment(offset) { + var start, loc, ch, comment; + + start = index - offset; + loc = { + start: { + line: lineNumber, + column: index - lineStart - offset + } + }; + + while (index < length) { + ch = source.charCodeAt(index); + ++index; + if (isLineTerminator(ch)) { + hasLineTerminator = true; + if (extra.comments) { + comment = source.slice(start + offset, index - 1); + loc.end = { + line: lineNumber, + column: index - lineStart - 1 + }; + addComment('Line', comment, start, index - 1, loc); + } + if (ch === 13 && source.charCodeAt(index) === 10) { + ++index; + } + ++lineNumber; + lineStart = index; + return; + } + } + + if (extra.comments) { + comment = source.slice(start + offset, index); + loc.end = { + line: lineNumber, + column: index - lineStart + }; + addComment('Line', comment, start, index, loc); + } + } + + function skipMultiLineComment() { + var start, loc, ch, comment; + + if (extra.comments) { + start = index - 2; + loc = { + start: { + line: lineNumber, + column: index - lineStart - 2 + } + }; + } + + while (index < length) { + ch = source.charCodeAt(index); + if (isLineTerminator(ch)) { + if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) { + ++index; + } + hasLineTerminator = true; + ++lineNumber; + ++index; + lineStart = index; + } else if (ch === 0x2A) { + // Block comment ends with '*/'. + if (source.charCodeAt(index + 1) === 0x2F) { + ++index; + ++index; + if (extra.comments) { + comment = source.slice(start + 2, index - 2); + loc.end = { + line: lineNumber, + column: index - lineStart + }; + addComment('Block', comment, start, index, loc); + } + return; + } + ++index; + } else { + ++index; + } + } + + // Ran off the end of the file - the whole thing is a comment + if (extra.comments) { + loc.end = { + line: lineNumber, + column: index - lineStart + }; + comment = source.slice(start + 2, index); + addComment('Block', comment, start, index, loc); + } + tolerateUnexpectedToken(); + } + + function skipComment() { + var ch, start; + hasLineTerminator = false; + + start = (index === 0); + while (index < length) { + ch = source.charCodeAt(index); + + if (isWhiteSpace(ch)) { + ++index; + } else if (isLineTerminator(ch)) { + hasLineTerminator = true; + ++index; + if (ch === 0x0D && source.charCodeAt(index) === 0x0A) { + ++index; + } + ++lineNumber; + lineStart = index; + start = true; + } else if (ch === 0x2F) { // U+002F is '/' + ch = source.charCodeAt(index + 1); + if (ch === 0x2F) { + ++index; + ++index; + skipSingleLineComment(2); + start = true; + } else if (ch === 0x2A) { // U+002A is '*' + ++index; + ++index; + skipMultiLineComment(); + } else { + break; + } + } else if (start && ch === 0x2D) { // U+002D is '-' + // U+003E is '>' + if ((source.charCodeAt(index + 1) === 0x2D) && (source.charCodeAt(index + 2) === 0x3E)) { + // '-->' is a single-line comment + index += 3; + skipSingleLineComment(3); + } else { + break; + } + } else if (ch === 0x3C) { // U+003C is '<' + if (source.slice(index + 1, index + 4) === '!--') { + ++index; // `<` + ++index; // `!` + ++index; // `-` + ++index; // `-` + skipSingleLineComment(4); + } else { + break; + } + } else { + break; + } + } + } + + function scanHexEscape(prefix) { + var i, len, ch, code = 0; + + len = (prefix === 'u') ? 4 : 2; + for (i = 0; i < len; ++i) { + if (index < length && isHexDigit(source[index])) { + ch = source[index++]; + code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); + } else { + return ''; + } + } + return String.fromCharCode(code); + } + + function scanUnicodeCodePointEscape() { + var ch, code; + + ch = source[index]; + code = 0; + + // At least, one hex digit is required. + if (ch === '}') { + throwUnexpectedToken(); + } + + while (index < length) { + ch = source[index++]; + if (!isHexDigit(ch)) { + break; + } + code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); + } + + if (code > 0x10FFFF || ch !== '}') { + throwUnexpectedToken(); + } + + return fromCodePoint(code); + } + + function codePointAt(i) { + var cp, first, second; + + cp = source.charCodeAt(i); + if (cp >= 0xD800 && cp <= 0xDBFF) { + second = source.charCodeAt(i + 1); + if (second >= 0xDC00 && second <= 0xDFFF) { + first = cp; + cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + } + } + + return cp; + } + + function getComplexIdentifier() { + var cp, ch, id; + + cp = codePointAt(index); + id = fromCodePoint(cp); + index += id.length; + + // '\u' (U+005C, U+0075) denotes an escaped character. + if (cp === 0x5C) { + if (source.charCodeAt(index) !== 0x75) { + throwUnexpectedToken(); + } + ++index; + if (source[index] === '{') { + ++index; + ch = scanUnicodeCodePointEscape(); + } else { + ch = scanHexEscape('u'); + cp = ch.charCodeAt(0); + if (!ch || ch === '\\' || !isIdentifierStart(cp)) { + throwUnexpectedToken(); + } + } + id = ch; + } + + while (index < length) { + cp = codePointAt(index); + if (!isIdentifierPart(cp)) { + break; + } + ch = fromCodePoint(cp); + id += ch; + index += ch.length; + + // '\u' (U+005C, U+0075) denotes an escaped character. + if (cp === 0x5C) { + id = id.substr(0, id.length - 1); + if (source.charCodeAt(index) !== 0x75) { + throwUnexpectedToken(); + } + ++index; + if (source[index] === '{') { + ++index; + ch = scanUnicodeCodePointEscape(); + } else { + ch = scanHexEscape('u'); + cp = ch.charCodeAt(0); + if (!ch || ch === '\\' || !isIdentifierPart(cp)) { + throwUnexpectedToken(); + } + } + id += ch; + } + } + + return id; + } + + function getIdentifier() { + var start, ch; + + start = index++; + while (index < length) { + ch = source.charCodeAt(index); + if (ch === 0x5C) { + // Blackslash (U+005C) marks Unicode escape sequence. + index = start; + return getComplexIdentifier(); + } else if (ch >= 0xD800 && ch < 0xDFFF) { + // Need to handle surrogate pairs. + index = start; + return getComplexIdentifier(); + } + if (isIdentifierPart(ch)) { + ++index; + } else { + break; + } + } + + return source.slice(start, index); + } + + function scanIdentifier() { + var start, id, type; + + start = index; + + // Backslash (U+005C) starts an escaped character. + id = (source.charCodeAt(index) === 0x5C) ? getComplexIdentifier() : getIdentifier(); + + // There is no keyword or literal with only one character. + // Thus, it must be an identifier. + if (id.length === 1) { + type = Token.Identifier; + } else if (isKeyword(id)) { + type = Token.Keyword; + } else if (id === 'null') { + type = Token.NullLiteral; + } else if (id === 'true' || id === 'false') { + type = Token.BooleanLiteral; + } else { + type = Token.Identifier; + } + + return { + type: type, + value: id, + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; + } + + + // ECMA-262 11.7 Punctuators + + function scanPunctuator() { + var token, str; + + token = { + type: Token.Punctuator, + value: '', + lineNumber: lineNumber, + lineStart: lineStart, + start: index, + end: index + }; + + // Check for most common single-character punctuators. + str = source[index]; + switch (str) { + + case '(': + if (extra.tokenize) { + extra.openParenToken = extra.tokenValues.length; + } + ++index; + break; + + case '{': + if (extra.tokenize) { + extra.openCurlyToken = extra.tokenValues.length; + } + state.curlyStack.push('{'); + ++index; + break; + + case '.': + ++index; + if (source[index] === '.' && source[index + 1] === '.') { + // Spread operator: ... + index += 2; + str = '...'; + } + break; + + case '}': + ++index; + state.curlyStack.pop(); + break; + case ')': + case ';': + case ',': + case '[': + case ']': + case ':': + case '?': + case '~': + ++index; + break; + + default: + // 4-character punctuator. + str = source.substr(index, 4); + if (str === '>>>=') { + index += 4; + } else { + + // 3-character punctuators. + str = str.substr(0, 3); + if (str === '===' || str === '!==' || str === '>>>' || + str === '<<=' || str === '>>=') { + index += 3; + } else { + + // 2-character punctuators. + str = str.substr(0, 2); + if (str === '&&' || str === '||' || str === '==' || str === '!=' || + str === '+=' || str === '-=' || str === '*=' || str === '/=' || + str === '++' || str === '--' || str === '<<' || str === '>>' || + str === '&=' || str === '|=' || str === '^=' || str === '%=' || + str === '<=' || str === '>=' || str === '=>') { + index += 2; + } else { + + // 1-character punctuators. + str = source[index]; + if ('<>=!+-*%&|^/'.indexOf(str) >= 0) { + ++index; + } + } + } + } + } + + if (index === token.start) { + throwUnexpectedToken(); + } + + token.end = index; + token.value = str; + return token; + } + + // ECMA-262 11.8.3 Numeric Literals + + function scanHexLiteral(start) { + var number = ''; + + while (index < length) { + if (!isHexDigit(source[index])) { + break; + } + number += source[index++]; + } + + if (number.length === 0) { + throwUnexpectedToken(); + } + + if (isIdentifierStart(source.charCodeAt(index))) { + throwUnexpectedToken(); + } + + return { + type: Token.NumericLiteral, + value: parseInt('0x' + number, 16), + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; + } + + function scanBinaryLiteral(start) { + var ch, number; + + number = ''; + + while (index < length) { + ch = source[index]; + if (ch !== '0' && ch !== '1') { + break; + } + number += source[index++]; + } + + if (number.length === 0) { + // only 0b or 0B + throwUnexpectedToken(); + } + + if (index < length) { + ch = source.charCodeAt(index); + /* istanbul ignore else */ + if (isIdentifierStart(ch) || isDecimalDigit(ch)) { + throwUnexpectedToken(); + } + } + + return { + type: Token.NumericLiteral, + value: parseInt(number, 2), + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; + } + + function scanOctalLiteral(prefix, start) { + var number, octal; + + if (isOctalDigit(prefix)) { + octal = true; + number = '0' + source[index++]; + } else { + octal = false; + ++index; + number = ''; + } + + while (index < length) { + if (!isOctalDigit(source[index])) { + break; + } + number += source[index++]; + } + + if (!octal && number.length === 0) { + // only 0o or 0O + throwUnexpectedToken(); + } + + if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) { + throwUnexpectedToken(); + } + + return { + type: Token.NumericLiteral, + value: parseInt(number, 8), + octal: octal, + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; + } + + function isImplicitOctalLiteral() { + var i, ch; + + // Implicit octal, unless there is a non-octal digit. + // (Annex B.1.1 on Numeric Literals) + for (i = index + 1; i < length; ++i) { + ch = source[i]; + if (ch === '8' || ch === '9') { + return false; + } + if (!isOctalDigit(ch)) { + return true; + } + } + + return true; + } + + function scanNumericLiteral() { + var number, start, ch; + + ch = source[index]; + assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), + 'Numeric literal must start with a decimal digit or a decimal point'); + + start = index; + number = ''; + if (ch !== '.') { + number = source[index++]; + ch = source[index]; + + // Hex number starts with '0x'. + // Octal number starts with '0'. + // Octal number in ES6 starts with '0o'. + // Binary number in ES6 starts with '0b'. + if (number === '0') { + if (ch === 'x' || ch === 'X') { + ++index; + return scanHexLiteral(start); + } + if (ch === 'b' || ch === 'B') { + ++index; + return scanBinaryLiteral(start); + } + if (ch === 'o' || ch === 'O') { + return scanOctalLiteral(ch, start); + } + + if (isOctalDigit(ch)) { + if (isImplicitOctalLiteral()) { + return scanOctalLiteral(ch, start); + } + } + } + + while (isDecimalDigit(source.charCodeAt(index))) { + number += source[index++]; + } + ch = source[index]; + } + + if (ch === '.') { + number += source[index++]; + while (isDecimalDigit(source.charCodeAt(index))) { + number += source[index++]; + } + ch = source[index]; + } + + if (ch === 'e' || ch === 'E') { + number += source[index++]; + + ch = source[index]; + if (ch === '+' || ch === '-') { + number += source[index++]; + } + if (isDecimalDigit(source.charCodeAt(index))) { + while (isDecimalDigit(source.charCodeAt(index))) { + number += source[index++]; + } + } else { + throwUnexpectedToken(); + } + } + + if (isIdentifierStart(source.charCodeAt(index))) { + throwUnexpectedToken(); + } + + return { + type: Token.NumericLiteral, + value: parseFloat(number), + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; + } + + // ECMA-262 11.8.4 String Literals + + function scanStringLiteral() { + var str = '', quote, start, ch, unescaped, octToDec, octal = false; + + quote = source[index]; + assert((quote === '\'' || quote === '"'), + 'String literal must starts with a quote'); + + start = index; + ++index; + + while (index < length) { + ch = source[index++]; + + if (ch === quote) { + quote = ''; + break; + } else if (ch === '\\') { + ch = source[index++]; + if (!ch || !isLineTerminator(ch.charCodeAt(0))) { + switch (ch) { + case 'u': + case 'x': + if (source[index] === '{') { + ++index; + str += scanUnicodeCodePointEscape(); + } else { + unescaped = scanHexEscape(ch); + if (!unescaped) { + throw throwUnexpectedToken(); + } + str += unescaped; + } + break; + case 'n': + str += '\n'; + break; + case 'r': + str += '\r'; + break; + case 't': + str += '\t'; + break; + case 'b': + str += '\b'; + break; + case 'f': + str += '\f'; + break; + case 'v': + str += '\x0B'; + break; + case '8': + case '9': + str += ch; + tolerateUnexpectedToken(); + break; + + default: + if (isOctalDigit(ch)) { + octToDec = octalToDecimal(ch); + + octal = octToDec.octal || octal; + str += String.fromCharCode(octToDec.code); + } else { + str += ch; + } + break; + } + } else { + ++lineNumber; + if (ch === '\r' && source[index] === '\n') { + ++index; + } + lineStart = index; + } + } else if (isLineTerminator(ch.charCodeAt(0))) { + break; + } else { + str += ch; + } + } + + if (quote !== '') { + index = start; + throwUnexpectedToken(); + } + + return { + type: Token.StringLiteral, + value: str, + octal: octal, + lineNumber: startLineNumber, + lineStart: startLineStart, + start: start, + end: index + }; + } + + // ECMA-262 11.8.6 Template Literal Lexical Components + + function scanTemplate() { + var cooked = '', ch, start, rawOffset, terminated, head, tail, restore, unescaped; + + terminated = false; + tail = false; + start = index; + head = (source[index] === '`'); + rawOffset = 2; + + ++index; + + while (index < length) { + ch = source[index++]; + if (ch === '`') { + rawOffset = 1; + tail = true; + terminated = true; + break; + } else if (ch === '$') { + if (source[index] === '{') { + state.curlyStack.push('${'); + ++index; + terminated = true; + break; + } + cooked += ch; + } else if (ch === '\\') { + ch = source[index++]; + if (!isLineTerminator(ch.charCodeAt(0))) { + switch (ch) { + case 'n': + cooked += '\n'; + break; + case 'r': + cooked += '\r'; + break; + case 't': + cooked += '\t'; + break; + case 'u': + case 'x': + if (source[index] === '{') { + ++index; + cooked += scanUnicodeCodePointEscape(); + } else { + restore = index; + unescaped = scanHexEscape(ch); + if (unescaped) { + cooked += unescaped; + } else { + index = restore; + cooked += ch; + } + } + break; + case 'b': + cooked += '\b'; + break; + case 'f': + cooked += '\f'; + break; + case 'v': + cooked += '\v'; + break; + + default: + if (ch === '0') { + if (isDecimalDigit(source.charCodeAt(index))) { + // Illegal: \01 \02 and so on + throwError(Messages.TemplateOctalLiteral); + } + cooked += '\0'; + } else if (isOctalDigit(ch)) { + // Illegal: \1 \2 + throwError(Messages.TemplateOctalLiteral); + } else { + cooked += ch; + } + break; + } + } else { + ++lineNumber; + if (ch === '\r' && source[index] === '\n') { + ++index; + } + lineStart = index; + } + } else if (isLineTerminator(ch.charCodeAt(0))) { + ++lineNumber; + if (ch === '\r' && source[index] === '\n') { + ++index; + } + lineStart = index; + cooked += '\n'; + } else { + cooked += ch; + } + } + + if (!terminated) { + throwUnexpectedToken(); + } + + if (!head) { + state.curlyStack.pop(); + } + + return { + type: Token.Template, + value: { + cooked: cooked, + raw: source.slice(start + 1, index - rawOffset) + }, + head: head, + tail: tail, + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; + } + + // ECMA-262 11.8.5 Regular Expression Literals + + function testRegExp(pattern, flags) { + // The BMP character to use as a replacement for astral symbols when + // translating an ES6 "u"-flagged pattern to an ES5-compatible + // approximation. + // Note: replacing with '\uFFFF' enables false positives in unlikely + // scenarios. For example, `[\u{1044f}-\u{10440}]` is an invalid + // pattern that would not be detected by this substitution. + var astralSubstitute = '\uFFFF', + tmp = pattern; + + if (flags.indexOf('u') >= 0) { + tmp = tmp + // Replace every Unicode escape sequence with the equivalent + // BMP character or a constant ASCII code point in the case of + // astral symbols. (See the above note on `astralSubstitute` + // for more information.) + .replace(/\\u\{([0-9a-fA-F]+)\}|\\u([a-fA-F0-9]{4})/g, function ($0, $1, $2) { + var codePoint = parseInt($1 || $2, 16); + if (codePoint > 0x10FFFF) { + throwUnexpectedToken(null, Messages.InvalidRegExp); + } + if (codePoint <= 0xFFFF) { + return String.fromCharCode(codePoint); + } + return astralSubstitute; + }) + // Replace each paired surrogate with a single ASCII symbol to + // avoid throwing on regular expressions that are only valid in + // combination with the "u" flag. + .replace( + /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, + astralSubstitute + ); + } + + // First, detect invalid regular expressions. + try { + RegExp(tmp); + } catch (e) { + throwUnexpectedToken(null, Messages.InvalidRegExp); + } + + // Return a regular expression object for this pattern-flag pair, or + // `null` in case the current environment doesn't support the flags it + // uses. + try { + return new RegExp(pattern, flags); + } catch (exception) { + /* istanbul ignore next */ + return null; + } + } + + function scanRegExpBody() { + var ch, str, classMarker, terminated, body; + + ch = source[index]; + assert(ch === '/', 'Regular expression literal must start with a slash'); + str = source[index++]; + + classMarker = false; + terminated = false; + while (index < length) { + ch = source[index++]; + str += ch; + if (ch === '\\') { + ch = source[index++]; + // ECMA-262 7.8.5 + if (isLineTerminator(ch.charCodeAt(0))) { + throwUnexpectedToken(null, Messages.UnterminatedRegExp); + } + str += ch; + } else if (isLineTerminator(ch.charCodeAt(0))) { + throwUnexpectedToken(null, Messages.UnterminatedRegExp); + } else if (classMarker) { + if (ch === ']') { + classMarker = false; + } + } else { + if (ch === '/') { + terminated = true; + break; + } else if (ch === '[') { + classMarker = true; + } + } + } + + if (!terminated) { + throwUnexpectedToken(null, Messages.UnterminatedRegExp); + } + + // Exclude leading and trailing slash. + body = str.substr(1, str.length - 2); + return { + value: body, + literal: str + }; + } + + function scanRegExpFlags() { + var ch, str, flags, restore; + + str = ''; + flags = ''; + while (index < length) { + ch = source[index]; + if (!isIdentifierPart(ch.charCodeAt(0))) { + break; + } + + ++index; + if (ch === '\\' && index < length) { + ch = source[index]; + if (ch === 'u') { + ++index; + restore = index; + ch = scanHexEscape('u'); + if (ch) { + flags += ch; + for (str += '\\u'; restore < index; ++restore) { + str += source[restore]; + } + } else { + index = restore; + flags += 'u'; + str += '\\u'; + } + tolerateUnexpectedToken(); + } else { + str += '\\'; + tolerateUnexpectedToken(); + } + } else { + flags += ch; + str += ch; + } + } + + return { + value: flags, + literal: str + }; + } + + function scanRegExp() { + var start, body, flags, value; + scanning = true; + + lookahead = null; + skipComment(); + start = index; + + body = scanRegExpBody(); + flags = scanRegExpFlags(); + value = testRegExp(body.value, flags.value); + scanning = false; + if (extra.tokenize) { + return { + type: Token.RegularExpression, + value: value, + regex: { + pattern: body.value, + flags: flags.value + }, + lineNumber: lineNumber, + lineStart: lineStart, + start: start, + end: index + }; + } + + return { + literal: body.literal + flags.literal, + value: value, + regex: { + pattern: body.value, + flags: flags.value + }, + start: start, + end: index + }; + } + + function collectRegex() { + var pos, loc, regex, token; + + skipComment(); + + pos = index; + loc = { + start: { + line: lineNumber, + column: index - lineStart + } + }; + + regex = scanRegExp(); + + loc.end = { + line: lineNumber, + column: index - lineStart + }; + + /* istanbul ignore next */ + if (!extra.tokenize) { + // Pop the previous token, which is likely '/' or '/=' + if (extra.tokens.length > 0) { + token = extra.tokens[extra.tokens.length - 1]; + if (token.range[0] === pos && token.type === 'Punctuator') { + if (token.value === '/' || token.value === '/=') { + extra.tokens.pop(); + } + } + } + + extra.tokens.push({ + type: 'RegularExpression', + value: regex.literal, + regex: regex.regex, + range: [pos, index], + loc: loc + }); + } + + return regex; + } + + function isIdentifierName(token) { + return token.type === Token.Identifier || + token.type === Token.Keyword || + token.type === Token.BooleanLiteral || + token.type === Token.NullLiteral; + } + + // Using the following algorithm: + // https://github.com/mozilla/sweet.js/wiki/design + + function advanceSlash() { + var regex, previous, check; + + function testKeyword(value) { + return value && (value.length > 1) && (value[0] >= 'a') && (value[0] <= 'z'); + } + + previous = extra.tokenValues[extra.tokenValues.length - 1]; + regex = (previous !== null); + + switch (previous) { + case 'this': + case ']': + regex = false; + break; + + case ')': + check = extra.tokenValues[extra.openParenToken - 1]; + regex = (check === 'if' || check === 'while' || check === 'for' || check === 'with'); + break; + + case '}': + // Dividing a function by anything makes little sense, + // but we have to check for that. + regex = false; + if (testKeyword(extra.tokenValues[extra.openCurlyToken - 3])) { + // Anonymous function, e.g. function(){} /42 + check = extra.tokenValues[extra.openCurlyToken - 4]; + regex = check ? (FnExprTokens.indexOf(check) < 0) : false; + } else if (testKeyword(extra.tokenValues[extra.openCurlyToken - 4])) { + // Named function, e.g. function f(){} /42/ + check = extra.tokenValues[extra.openCurlyToken - 5]; + regex = check ? (FnExprTokens.indexOf(check) < 0) : true; + } + } + + return regex ? collectRegex() : scanPunctuator(); + } + + function advance() { + var cp, token; + + if (index >= length) { + return { + type: Token.EOF, + lineNumber: lineNumber, + lineStart: lineStart, + start: index, + end: index + }; + } + + cp = source.charCodeAt(index); + + if (isIdentifierStart(cp)) { + token = scanIdentifier(); + if (strict && isStrictModeReservedWord(token.value)) { + token.type = Token.Keyword; + } + return token; + } + + // Very common: ( and ) and ; + if (cp === 0x28 || cp === 0x29 || cp === 0x3B) { + return scanPunctuator(); + } + + // String literal starts with single quote (U+0027) or double quote (U+0022). + if (cp === 0x27 || cp === 0x22) { + return scanStringLiteral(); + } + + // Dot (.) U+002E can also start a floating-point number, hence the need + // to check the next character. + if (cp === 0x2E) { + if (isDecimalDigit(source.charCodeAt(index + 1))) { + return scanNumericLiteral(); + } + return scanPunctuator(); + } + + if (isDecimalDigit(cp)) { + return scanNumericLiteral(); + } + + // Slash (/) U+002F can also start a regex. + if (extra.tokenize && cp === 0x2F) { + return advanceSlash(); + } + + // Template literals start with ` (U+0060) for template head + // or } (U+007D) for template middle or template tail. + if (cp === 0x60 || (cp === 0x7D && state.curlyStack[state.curlyStack.length - 1] === '${')) { + return scanTemplate(); + } + + // Possible identifier start in a surrogate pair. + if (cp >= 0xD800 && cp < 0xDFFF) { + cp = codePointAt(index); + if (isIdentifierStart(cp)) { + return scanIdentifier(); + } + } + + return scanPunctuator(); + } + + function collectToken() { + var loc, token, value, entry; + + loc = { + start: { + line: lineNumber, + column: index - lineStart + } + }; + + token = advance(); + loc.end = { + line: lineNumber, + column: index - lineStart + }; + + if (token.type !== Token.EOF) { + value = source.slice(token.start, token.end); + entry = { + type: TokenName[token.type], + value: value, + range: [token.start, token.end], + loc: loc + }; + if (token.regex) { + entry.regex = { + pattern: token.regex.pattern, + flags: token.regex.flags + }; + } + if (extra.tokenValues) { + extra.tokenValues.push((entry.type === 'Punctuator' || entry.type === 'Keyword') ? entry.value : null); + } + if (extra.tokenize) { + if (!extra.range) { + delete entry.range; + } + if (!extra.loc) { + delete entry.loc; + } + if (extra.delegate) { + entry = extra.delegate(entry); + } + } + extra.tokens.push(entry); + } + + return token; + } + + function lex() { + var token; + scanning = true; + + lastIndex = index; + lastLineNumber = lineNumber; + lastLineStart = lineStart; + + skipComment(); + + token = lookahead; + + startIndex = index; + startLineNumber = lineNumber; + startLineStart = lineStart; + + lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance(); + scanning = false; + return token; + } + + function peek() { + scanning = true; + + skipComment(); + + lastIndex = index; + lastLineNumber = lineNumber; + lastLineStart = lineStart; + + startIndex = index; + startLineNumber = lineNumber; + startLineStart = lineStart; + + lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance(); + scanning = false; + } + + function Position() { + this.line = startLineNumber; + this.column = startIndex - startLineStart; + } + + function SourceLocation() { + this.start = new Position(); + this.end = null; + } + + function WrappingSourceLocation(startToken) { + this.start = { + line: startToken.lineNumber, + column: startToken.start - startToken.lineStart + }; + this.end = null; + } + + function Node() { + if (extra.range) { + this.range = [startIndex, 0]; + } + if (extra.loc) { + this.loc = new SourceLocation(); + } + } + + function WrappingNode(startToken) { + if (extra.range) { + this.range = [startToken.start, 0]; + } + if (extra.loc) { + this.loc = new WrappingSourceLocation(startToken); + } + } + + WrappingNode.prototype = Node.prototype = { + + processComment: function () { + var lastChild, + innerComments, + leadingComments, + trailingComments, + bottomRight = extra.bottomRightStack, + i, + comment, + last = bottomRight[bottomRight.length - 1]; + + if (this.type === Syntax.Program) { + if (this.body.length > 0) { + return; + } + } + /** + * patch innnerComments for properties empty block + * `function a() {/** comments **\/}` + */ + + if (this.type === Syntax.BlockStatement && this.body.length === 0) { + innerComments = []; + for (i = extra.leadingComments.length - 1; i >= 0; --i) { + comment = extra.leadingComments[i]; + if (this.range[1] >= comment.range[1]) { + innerComments.unshift(comment); + extra.leadingComments.splice(i, 1); + extra.trailingComments.splice(i, 1); + } + } + if (innerComments.length) { + this.innerComments = innerComments; + //bottomRight.push(this); + return; + } + } + + if (extra.trailingComments.length > 0) { + trailingComments = []; + for (i = extra.trailingComments.length - 1; i >= 0; --i) { + comment = extra.trailingComments[i]; + if (comment.range[0] >= this.range[1]) { + trailingComments.unshift(comment); + extra.trailingComments.splice(i, 1); + } + } + extra.trailingComments = []; + } else { + if (last && last.trailingComments && last.trailingComments[0].range[0] >= this.range[1]) { + trailingComments = last.trailingComments; + delete last.trailingComments; + } + } + + // Eating the stack. + while (last && last.range[0] >= this.range[0]) { + lastChild = bottomRight.pop(); + last = bottomRight[bottomRight.length - 1]; + } + + if (lastChild) { + if (lastChild.leadingComments) { + leadingComments = []; + for (i = lastChild.leadingComments.length - 1; i >= 0; --i) { + comment = lastChild.leadingComments[i]; + if (comment.range[1] <= this.range[0]) { + leadingComments.unshift(comment); + lastChild.leadingComments.splice(i, 1); + } + } + + if (!lastChild.leadingComments.length) { + lastChild.leadingComments = undefined; + } + } + } else if (extra.leadingComments.length > 0) { + leadingComments = []; + for (i = extra.leadingComments.length - 1; i >= 0; --i) { + comment = extra.leadingComments[i]; + if (comment.range[1] <= this.range[0]) { + leadingComments.unshift(comment); + extra.leadingComments.splice(i, 1); + } + } + } + + + if (leadingComments && leadingComments.length > 0) { + this.leadingComments = leadingComments; + } + if (trailingComments && trailingComments.length > 0) { + this.trailingComments = trailingComments; + } + + bottomRight.push(this); + }, + + finish: function () { + if (extra.range) { + this.range[1] = lastIndex; + } + if (extra.loc) { + this.loc.end = { + line: lastLineNumber, + column: lastIndex - lastLineStart + }; + if (extra.source) { + this.loc.source = extra.source; + } + } + + if (extra.attachComment) { + this.processComment(); + } + }, + + finishArrayExpression: function (elements) { + this.type = Syntax.ArrayExpression; + this.elements = elements; + this.finish(); + return this; + }, + + finishArrayPattern: function (elements) { + this.type = Syntax.ArrayPattern; + this.elements = elements; + this.finish(); + return this; + }, + + finishArrowFunctionExpression: function (params, defaults, body, expression) { + this.type = Syntax.ArrowFunctionExpression; + this.id = null; + this.params = params; + this.defaults = defaults; + this.body = body; + this.generator = false; + this.expression = expression; + this.finish(); + return this; + }, + + finishAssignmentExpression: function (operator, left, right) { + this.type = Syntax.AssignmentExpression; + this.operator = operator; + this.left = left; + this.right = right; + this.finish(); + return this; + }, + + finishAssignmentPattern: function (left, right) { + this.type = Syntax.AssignmentPattern; + this.left = left; + this.right = right; + this.finish(); + return this; + }, + + finishBinaryExpression: function (operator, left, right) { + this.type = (operator === '||' || operator === '&&') ? Syntax.LogicalExpression : Syntax.BinaryExpression; + this.operator = operator; + this.left = left; + this.right = right; + this.finish(); + return this; + }, + + finishBlockStatement: function (body) { + this.type = Syntax.BlockStatement; + this.body = body; + this.finish(); + return this; + }, + + finishBreakStatement: function (label) { + this.type = Syntax.BreakStatement; + this.label = label; + this.finish(); + return this; + }, + + finishCallExpression: function (callee, args) { + this.type = Syntax.CallExpression; + this.callee = callee; + this.arguments = args; + this.finish(); + return this; + }, + + finishCatchClause: function (param, body) { + this.type = Syntax.CatchClause; + this.param = param; + this.body = body; + this.finish(); + return this; + }, + + finishClassBody: function (body) { + this.type = Syntax.ClassBody; + this.body = body; + this.finish(); + return this; + }, + + finishClassDeclaration: function (id, superClass, body) { + this.type = Syntax.ClassDeclaration; + this.id = id; + this.superClass = superClass; + this.body = body; + this.finish(); + return this; + }, + + finishClassExpression: function (id, superClass, body) { + this.type = Syntax.ClassExpression; + this.id = id; + this.superClass = superClass; + this.body = body; + this.finish(); + return this; + }, + + finishConditionalExpression: function (test, consequent, alternate) { + this.type = Syntax.ConditionalExpression; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + this.finish(); + return this; + }, + + finishContinueStatement: function (label) { + this.type = Syntax.ContinueStatement; + this.label = label; + this.finish(); + return this; + }, + + finishDebuggerStatement: function () { + this.type = Syntax.DebuggerStatement; + this.finish(); + return this; + }, + + finishDoWhileStatement: function (body, test) { + this.type = Syntax.DoWhileStatement; + this.body = body; + this.test = test; + this.finish(); + return this; + }, + + finishEmptyStatement: function () { + this.type = Syntax.EmptyStatement; + this.finish(); + return this; + }, + + finishExpressionStatement: function (expression) { + this.type = Syntax.ExpressionStatement; + this.expression = expression; + this.finish(); + return this; + }, + + finishForStatement: function (init, test, update, body) { + this.type = Syntax.ForStatement; + this.init = init; + this.test = test; + this.update = update; + this.body = body; + this.finish(); + return this; + }, + + finishForOfStatement: function (left, right, body) { + this.type = Syntax.ForOfStatement; + this.left = left; + this.right = right; + this.body = body; + this.finish(); + return this; + }, + + finishForInStatement: function (left, right, body) { + this.type = Syntax.ForInStatement; + this.left = left; + this.right = right; + this.body = body; + this.each = false; + this.finish(); + return this; + }, + + finishFunctionDeclaration: function (id, params, defaults, body, generator) { + this.type = Syntax.FunctionDeclaration; + this.id = id; + this.params = params; + this.defaults = defaults; + this.body = body; + this.generator = generator; + this.expression = false; + this.finish(); + return this; + }, + + finishFunctionExpression: function (id, params, defaults, body, generator) { + this.type = Syntax.FunctionExpression; + this.id = id; + this.params = params; + this.defaults = defaults; + this.body = body; + this.generator = generator; + this.expression = false; + this.finish(); + return this; + }, + + finishIdentifier: function (name) { + this.type = Syntax.Identifier; + this.name = name; + this.finish(); + return this; + }, + + finishIfStatement: function (test, consequent, alternate) { + this.type = Syntax.IfStatement; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + this.finish(); + return this; + }, + + finishLabeledStatement: function (label, body) { + this.type = Syntax.LabeledStatement; + this.label = label; + this.body = body; + this.finish(); + return this; + }, + + finishLiteral: function (token) { + this.type = Syntax.Literal; + this.value = token.value; + this.raw = source.slice(token.start, token.end); + if (token.regex) { + this.regex = token.regex; + } + this.finish(); + return this; + }, + + finishMemberExpression: function (accessor, object, property) { + this.type = Syntax.MemberExpression; + this.computed = accessor === '['; + this.object = object; + this.property = property; + this.finish(); + return this; + }, + + finishMetaProperty: function (meta, property) { + this.type = Syntax.MetaProperty; + this.meta = meta; + this.property = property; + this.finish(); + return this; + }, + + finishNewExpression: function (callee, args) { + this.type = Syntax.NewExpression; + this.callee = callee; + this.arguments = args; + this.finish(); + return this; + }, + + finishObjectExpression: function (properties) { + this.type = Syntax.ObjectExpression; + this.properties = properties; + this.finish(); + return this; + }, + + finishObjectPattern: function (properties) { + this.type = Syntax.ObjectPattern; + this.properties = properties; + this.finish(); + return this; + }, + + finishPostfixExpression: function (operator, argument) { + this.type = Syntax.UpdateExpression; + this.operator = operator; + this.argument = argument; + this.prefix = false; + this.finish(); + return this; + }, + + finishProgram: function (body, sourceType) { + this.type = Syntax.Program; + this.body = body; + this.sourceType = sourceType; + this.finish(); + return this; + }, + + finishProperty: function (kind, key, computed, value, method, shorthand) { + this.type = Syntax.Property; + this.key = key; + this.computed = computed; + this.value = value; + this.kind = kind; + this.method = method; + this.shorthand = shorthand; + this.finish(); + return this; + }, + + finishRestElement: function (argument) { + this.type = Syntax.RestElement; + this.argument = argument; + this.finish(); + return this; + }, + + finishReturnStatement: function (argument) { + this.type = Syntax.ReturnStatement; + this.argument = argument; + this.finish(); + return this; + }, + + finishSequenceExpression: function (expressions) { + this.type = Syntax.SequenceExpression; + this.expressions = expressions; + this.finish(); + return this; + }, + + finishSpreadElement: function (argument) { + this.type = Syntax.SpreadElement; + this.argument = argument; + this.finish(); + return this; + }, + + finishSwitchCase: function (test, consequent) { + this.type = Syntax.SwitchCase; + this.test = test; + this.consequent = consequent; + this.finish(); + return this; + }, + + finishSuper: function () { + this.type = Syntax.Super; + this.finish(); + return this; + }, + + finishSwitchStatement: function (discriminant, cases) { + this.type = Syntax.SwitchStatement; + this.discriminant = discriminant; + this.cases = cases; + this.finish(); + return this; + }, + + finishTaggedTemplateExpression: function (tag, quasi) { + this.type = Syntax.TaggedTemplateExpression; + this.tag = tag; + this.quasi = quasi; + this.finish(); + return this; + }, + + finishTemplateElement: function (value, tail) { + this.type = Syntax.TemplateElement; + this.value = value; + this.tail = tail; + this.finish(); + return this; + }, + + finishTemplateLiteral: function (quasis, expressions) { + this.type = Syntax.TemplateLiteral; + this.quasis = quasis; + this.expressions = expressions; + this.finish(); + return this; + }, + + finishThisExpression: function () { + this.type = Syntax.ThisExpression; + this.finish(); + return this; + }, + + finishThrowStatement: function (argument) { + this.type = Syntax.ThrowStatement; + this.argument = argument; + this.finish(); + return this; + }, + + finishTryStatement: function (block, handler, finalizer) { + this.type = Syntax.TryStatement; + this.block = block; + this.guardedHandlers = []; + this.handlers = handler ? [handler] : []; + this.handler = handler; + this.finalizer = finalizer; + this.finish(); + return this; + }, + + finishUnaryExpression: function (operator, argument) { + this.type = (operator === '++' || operator === '--') ? Syntax.UpdateExpression : Syntax.UnaryExpression; + this.operator = operator; + this.argument = argument; + this.prefix = true; + this.finish(); + return this; + }, + + finishVariableDeclaration: function (declarations) { + this.type = Syntax.VariableDeclaration; + this.declarations = declarations; + this.kind = 'var'; + this.finish(); + return this; + }, + + finishLexicalDeclaration: function (declarations, kind) { + this.type = Syntax.VariableDeclaration; + this.declarations = declarations; + this.kind = kind; + this.finish(); + return this; + }, + + finishVariableDeclarator: function (id, init) { + this.type = Syntax.VariableDeclarator; + this.id = id; + this.init = init; + this.finish(); + return this; + }, + + finishWhileStatement: function (test, body) { + this.type = Syntax.WhileStatement; + this.test = test; + this.body = body; + this.finish(); + return this; + }, + + finishWithStatement: function (object, body) { + this.type = Syntax.WithStatement; + this.object = object; + this.body = body; + this.finish(); + return this; + }, + + finishExportSpecifier: function (local, exported) { + this.type = Syntax.ExportSpecifier; + this.exported = exported || local; + this.local = local; + this.finish(); + return this; + }, + + finishImportDefaultSpecifier: function (local) { + this.type = Syntax.ImportDefaultSpecifier; + this.local = local; + this.finish(); + return this; + }, + + finishImportNamespaceSpecifier: function (local) { + this.type = Syntax.ImportNamespaceSpecifier; + this.local = local; + this.finish(); + return this; + }, + + finishExportNamedDeclaration: function (declaration, specifiers, src) { + this.type = Syntax.ExportNamedDeclaration; + this.declaration = declaration; + this.specifiers = specifiers; + this.source = src; + this.finish(); + return this; + }, + + finishExportDefaultDeclaration: function (declaration) { + this.type = Syntax.ExportDefaultDeclaration; + this.declaration = declaration; + this.finish(); + return this; + }, + + finishExportAllDeclaration: function (src) { + this.type = Syntax.ExportAllDeclaration; + this.source = src; + this.finish(); + return this; + }, + + finishImportSpecifier: function (local, imported) { + this.type = Syntax.ImportSpecifier; + this.local = local || imported; + this.imported = imported; + this.finish(); + return this; + }, + + finishImportDeclaration: function (specifiers, src) { + this.type = Syntax.ImportDeclaration; + this.specifiers = specifiers; + this.source = src; + this.finish(); + return this; + }, + + finishYieldExpression: function (argument, delegate) { + this.type = Syntax.YieldExpression; + this.argument = argument; + this.delegate = delegate; + this.finish(); + return this; + } + }; + + + function recordError(error) { + var e, existing; + + for (e = 0; e < extra.errors.length; e++) { + existing = extra.errors[e]; + // Prevent duplicated error. + /* istanbul ignore next */ + if (existing.index === error.index && existing.message === error.message) { + return; + } + } + + extra.errors.push(error); + } + + function constructError(msg, column) { + var error = new Error(msg); + try { + throw error; + } catch (base) { + /* istanbul ignore else */ + if (Object.create && Object.defineProperty) { + error = Object.create(base); + Object.defineProperty(error, 'column', { value: column }); + } + } finally { + return error; + } + } + + function createError(line, pos, description) { + var msg, column, error; + + msg = 'Line ' + line + ': ' + description; + column = pos - (scanning ? lineStart : lastLineStart) + 1; + error = constructError(msg, column); + error.lineNumber = line; + error.description = description; + error.index = pos; + return error; + } + + // Throw an exception + + function throwError(messageFormat) { + var args, msg; + + args = Array.prototype.slice.call(arguments, 1); + msg = messageFormat.replace(/%(\d)/g, + function (whole, idx) { + assert(idx < args.length, 'Message reference must be in range'); + return args[idx]; + } + ); + + throw createError(lastLineNumber, lastIndex, msg); + } + + function tolerateError(messageFormat) { + var args, msg, error; + + args = Array.prototype.slice.call(arguments, 1); + /* istanbul ignore next */ + msg = messageFormat.replace(/%(\d)/g, + function (whole, idx) { + assert(idx < args.length, 'Message reference must be in range'); + return args[idx]; + } + ); + + error = createError(lineNumber, lastIndex, msg); + if (extra.errors) { + recordError(error); + } else { + throw error; + } + } + + // Throw an exception because of the token. + + function unexpectedTokenError(token, message) { + var value, msg = message || Messages.UnexpectedToken; + + if (token) { + if (!message) { + msg = (token.type === Token.EOF) ? Messages.UnexpectedEOS : + (token.type === Token.Identifier) ? Messages.UnexpectedIdentifier : + (token.type === Token.NumericLiteral) ? Messages.UnexpectedNumber : + (token.type === Token.StringLiteral) ? Messages.UnexpectedString : + (token.type === Token.Template) ? Messages.UnexpectedTemplate : + Messages.UnexpectedToken; + + if (token.type === Token.Keyword) { + if (isFutureReservedWord(token.value)) { + msg = Messages.UnexpectedReserved; + } else if (strict && isStrictModeReservedWord(token.value)) { + msg = Messages.StrictReservedWord; + } + } + } + + value = (token.type === Token.Template) ? token.value.raw : token.value; + } else { + value = 'ILLEGAL'; + } + + msg = msg.replace('%0', value); + + return (token && typeof token.lineNumber === 'number') ? + createError(token.lineNumber, token.start, msg) : + createError(scanning ? lineNumber : lastLineNumber, scanning ? index : lastIndex, msg); + } + + function throwUnexpectedToken(token, message) { + throw unexpectedTokenError(token, message); + } + + function tolerateUnexpectedToken(token, message) { + var error = unexpectedTokenError(token, message); + if (extra.errors) { + recordError(error); + } else { + throw error; + } + } + + // Expect the next token to match the specified punctuator. + // If not, an exception will be thrown. + + function expect(value) { + var token = lex(); + if (token.type !== Token.Punctuator || token.value !== value) { + throwUnexpectedToken(token); + } + } + + /** + * @name expectCommaSeparator + * @description Quietly expect a comma when in tolerant mode, otherwise delegates + * to expect(value) + * @since 2.0 + */ + function expectCommaSeparator() { + var token; + + if (extra.errors) { + token = lookahead; + if (token.type === Token.Punctuator && token.value === ',') { + lex(); + } else if (token.type === Token.Punctuator && token.value === ';') { + lex(); + tolerateUnexpectedToken(token); + } else { + tolerateUnexpectedToken(token, Messages.UnexpectedToken); + } + } else { + expect(','); + } + } + + // Expect the next token to match the specified keyword. + // If not, an exception will be thrown. + + function expectKeyword(keyword) { + var token = lex(); + if (token.type !== Token.Keyword || token.value !== keyword) { + throwUnexpectedToken(token); + } + } + + // Return true if the next token matches the specified punctuator. + + function match(value) { + return lookahead.type === Token.Punctuator && lookahead.value === value; + } + + // Return true if the next token matches the specified keyword + + function matchKeyword(keyword) { + return lookahead.type === Token.Keyword && lookahead.value === keyword; + } + + // Return true if the next token matches the specified contextual keyword + // (where an identifier is sometimes a keyword depending on the context) + + function matchContextualKeyword(keyword) { + return lookahead.type === Token.Identifier && lookahead.value === keyword; + } + + // Return true if the next token is an assignment operator + + function matchAssign() { + var op; + + if (lookahead.type !== Token.Punctuator) { + return false; + } + op = lookahead.value; + return op === '=' || + op === '*=' || + op === '/=' || + op === '%=' || + op === '+=' || + op === '-=' || + op === '<<=' || + op === '>>=' || + op === '>>>=' || + op === '&=' || + op === '^=' || + op === '|='; + } + + function consumeSemicolon() { + // Catch the very common case first: immediately a semicolon (U+003B). + if (source.charCodeAt(startIndex) === 0x3B || match(';')) { + lex(); + return; + } + + if (hasLineTerminator) { + return; + } + + // FIXME(ikarienator): this is seemingly an issue in the previous location info convention. + lastIndex = startIndex; + lastLineNumber = startLineNumber; + lastLineStart = startLineStart; + + if (lookahead.type !== Token.EOF && !match('}')) { + throwUnexpectedToken(lookahead); + } + } + + // Cover grammar support. + // + // When an assignment expression position starts with an left parenthesis, the determination of the type + // of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead) + // or the first comma. This situation also defers the determination of all the expressions nested in the pair. + // + // There are three productions that can be parsed in a parentheses pair that needs to be determined + // after the outermost pair is closed. They are: + // + // 1. AssignmentExpression + // 2. BindingElements + // 3. AssignmentTargets + // + // In order to avoid exponential backtracking, we use two flags to denote if the production can be + // binding element or assignment target. + // + // The three productions have the relationship: + // + // BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression + // + // with a single exception that CoverInitializedName when used directly in an Expression, generates + // an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the + // first usage of CoverInitializedName and report it when we reached the end of the parentheses pair. + // + // isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not + // effect the current flags. This means the production the parser parses is only used as an expression. Therefore + // the CoverInitializedName check is conducted. + // + // inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates + // the flags outside of the parser. This means the production the parser parses is used as a part of a potential + // pattern. The CoverInitializedName check is deferred. + function isolateCoverGrammar(parser) { + var oldIsBindingElement = isBindingElement, + oldIsAssignmentTarget = isAssignmentTarget, + oldFirstCoverInitializedNameError = firstCoverInitializedNameError, + result; + isBindingElement = true; + isAssignmentTarget = true; + firstCoverInitializedNameError = null; + result = parser(); + if (firstCoverInitializedNameError !== null) { + throwUnexpectedToken(firstCoverInitializedNameError); + } + isBindingElement = oldIsBindingElement; + isAssignmentTarget = oldIsAssignmentTarget; + firstCoverInitializedNameError = oldFirstCoverInitializedNameError; + return result; + } + + function inheritCoverGrammar(parser) { + var oldIsBindingElement = isBindingElement, + oldIsAssignmentTarget = isAssignmentTarget, + oldFirstCoverInitializedNameError = firstCoverInitializedNameError, + result; + isBindingElement = true; + isAssignmentTarget = true; + firstCoverInitializedNameError = null; + result = parser(); + isBindingElement = isBindingElement && oldIsBindingElement; + isAssignmentTarget = isAssignmentTarget && oldIsAssignmentTarget; + firstCoverInitializedNameError = oldFirstCoverInitializedNameError || firstCoverInitializedNameError; + return result; + } + + // ECMA-262 13.3.3 Destructuring Binding Patterns + + function parseArrayPattern(params, kind) { + var node = new Node(), elements = [], rest, restNode; + expect('['); + + while (!match(']')) { + if (match(',')) { + lex(); + elements.push(null); + } else { + if (match('...')) { + restNode = new Node(); + lex(); + params.push(lookahead); + rest = parseVariableIdentifier(kind); + elements.push(restNode.finishRestElement(rest)); + break; + } else { + elements.push(parsePatternWithDefault(params, kind)); + } + if (!match(']')) { + expect(','); + } + } + + } + + expect(']'); + + return node.finishArrayPattern(elements); + } + + function parsePropertyPattern(params, kind) { + var node = new Node(), key, keyToken, computed = match('['), init; + if (lookahead.type === Token.Identifier) { + keyToken = lookahead; + key = parseVariableIdentifier(); + if (match('=')) { + params.push(keyToken); + lex(); + init = parseAssignmentExpression(); + + return node.finishProperty( + 'init', key, false, + new WrappingNode(keyToken).finishAssignmentPattern(key, init), false, true); + } else if (!match(':')) { + params.push(keyToken); + return node.finishProperty('init', key, false, key, false, true); + } + } else { + key = parseObjectPropertyKey(); + } + expect(':'); + init = parsePatternWithDefault(params, kind); + return node.finishProperty('init', key, computed, init, false, false); + } + + function parseObjectPattern(params, kind) { + var node = new Node(), properties = []; + + expect('{'); + + while (!match('}')) { + properties.push(parsePropertyPattern(params, kind)); + if (!match('}')) { + expect(','); + } + } + + lex(); + + return node.finishObjectPattern(properties); + } + + function parsePattern(params, kind) { + if (match('[')) { + return parseArrayPattern(params, kind); + } else if (match('{')) { + return parseObjectPattern(params, kind); + } else if (matchKeyword('let')) { + if (kind === 'const' || kind === 'let') { + tolerateUnexpectedToken(lookahead, Messages.UnexpectedToken); + } + } + + params.push(lookahead); + return parseVariableIdentifier(kind); + } + + function parsePatternWithDefault(params, kind) { + var startToken = lookahead, pattern, previousAllowYield, right; + pattern = parsePattern(params, kind); + if (match('=')) { + lex(); + previousAllowYield = state.allowYield; + state.allowYield = true; + right = isolateCoverGrammar(parseAssignmentExpression); + state.allowYield = previousAllowYield; + pattern = new WrappingNode(startToken).finishAssignmentPattern(pattern, right); + } + return pattern; + } + + // ECMA-262 12.2.5 Array Initializer + + function parseArrayInitializer() { + var elements = [], node = new Node(), restSpread; + + expect('['); + + while (!match(']')) { + if (match(',')) { + lex(); + elements.push(null); + } else if (match('...')) { + restSpread = new Node(); + lex(); + restSpread.finishSpreadElement(inheritCoverGrammar(parseAssignmentExpression)); + + if (!match(']')) { + isAssignmentTarget = isBindingElement = false; + expect(','); + } + elements.push(restSpread); + } else { + elements.push(inheritCoverGrammar(parseAssignmentExpression)); + + if (!match(']')) { + expect(','); + } + } + } + + lex(); + + return node.finishArrayExpression(elements); + } + + // ECMA-262 12.2.6 Object Initializer + + function parsePropertyFunction(node, paramInfo, isGenerator) { + var previousStrict, body; + + isAssignmentTarget = isBindingElement = false; + + previousStrict = strict; + body = isolateCoverGrammar(parseFunctionSourceElements); + + if (strict && paramInfo.firstRestricted) { + tolerateUnexpectedToken(paramInfo.firstRestricted, paramInfo.message); + } + if (strict && paramInfo.stricted) { + tolerateUnexpectedToken(paramInfo.stricted, paramInfo.message); + } + + strict = previousStrict; + return node.finishFunctionExpression(null, paramInfo.params, paramInfo.defaults, body, isGenerator); + } + + function parsePropertyMethodFunction() { + var params, method, node = new Node(), + previousAllowYield = state.allowYield; + + state.allowYield = false; + params = parseParams(); + state.allowYield = previousAllowYield; + + state.allowYield = false; + method = parsePropertyFunction(node, params, false); + state.allowYield = previousAllowYield; + + return method; + } + + function parseObjectPropertyKey() { + var token, node = new Node(), expr; + + token = lex(); + + // Note: This function is called only from parseObjectProperty(), where + // EOF and Punctuator tokens are already filtered out. + + switch (token.type) { + case Token.StringLiteral: + case Token.NumericLiteral: + if (strict && token.octal) { + tolerateUnexpectedToken(token, Messages.StrictOctalLiteral); + } + return node.finishLiteral(token); + case Token.Identifier: + case Token.BooleanLiteral: + case Token.NullLiteral: + case Token.Keyword: + return node.finishIdentifier(token.value); + case Token.Punctuator: + if (token.value === '[') { + expr = isolateCoverGrammar(parseAssignmentExpression); + expect(']'); + return expr; + } + break; + } + throwUnexpectedToken(token); + } + + function lookaheadPropertyName() { + switch (lookahead.type) { + case Token.Identifier: + case Token.StringLiteral: + case Token.BooleanLiteral: + case Token.NullLiteral: + case Token.NumericLiteral: + case Token.Keyword: + return true; + case Token.Punctuator: + return lookahead.value === '['; + } + return false; + } + + // This function is to try to parse a MethodDefinition as defined in 14.3. But in the case of object literals, + // it might be called at a position where there is in fact a short hand identifier pattern or a data property. + // This can only be determined after we consumed up to the left parentheses. + // + // In order to avoid back tracking, it returns `null` if the position is not a MethodDefinition and the caller + // is responsible to visit other options. + function tryParseMethodDefinition(token, key, computed, node) { + var value, options, methodNode, params, + previousAllowYield = state.allowYield; + + if (token.type === Token.Identifier) { + // check for `get` and `set`; + + if (token.value === 'get' && lookaheadPropertyName()) { + computed = match('['); + key = parseObjectPropertyKey(); + methodNode = new Node(); + expect('('); + expect(')'); + + state.allowYield = false; + value = parsePropertyFunction(methodNode, { + params: [], + defaults: [], + stricted: null, + firstRestricted: null, + message: null + }, false); + state.allowYield = previousAllowYield; + + return node.finishProperty('get', key, computed, value, false, false); + } else if (token.value === 'set' && lookaheadPropertyName()) { + computed = match('['); + key = parseObjectPropertyKey(); + methodNode = new Node(); + expect('('); + + options = { + params: [], + defaultCount: 0, + defaults: [], + firstRestricted: null, + paramSet: {} + }; + if (match(')')) { + tolerateUnexpectedToken(lookahead); + } else { + state.allowYield = false; + parseParam(options); + state.allowYield = previousAllowYield; + if (options.defaultCount === 0) { + options.defaults = []; + } + } + expect(')'); + + state.allowYield = false; + value = parsePropertyFunction(methodNode, options, false); + state.allowYield = previousAllowYield; + + return node.finishProperty('set', key, computed, value, false, false); + } + } else if (token.type === Token.Punctuator && token.value === '*' && lookaheadPropertyName()) { + computed = match('['); + key = parseObjectPropertyKey(); + methodNode = new Node(); + + state.allowYield = true; + params = parseParams(); + state.allowYield = previousAllowYield; + + state.allowYield = false; + value = parsePropertyFunction(methodNode, params, true); + state.allowYield = previousAllowYield; + + return node.finishProperty('init', key, computed, value, true, false); + } + + if (key && match('(')) { + value = parsePropertyMethodFunction(); + return node.finishProperty('init', key, computed, value, true, false); + } + + // Not a MethodDefinition. + return null; + } + + function parseObjectProperty(hasProto) { + var token = lookahead, node = new Node(), computed, key, maybeMethod, proto, value; + + computed = match('['); + if (match('*')) { + lex(); + } else { + key = parseObjectPropertyKey(); + } + maybeMethod = tryParseMethodDefinition(token, key, computed, node); + if (maybeMethod) { + return maybeMethod; + } + + if (!key) { + throwUnexpectedToken(lookahead); + } + + // Check for duplicated __proto__ + if (!computed) { + proto = (key.type === Syntax.Identifier && key.name === '__proto__') || + (key.type === Syntax.Literal && key.value === '__proto__'); + if (hasProto.value && proto) { + tolerateError(Messages.DuplicateProtoProperty); + } + hasProto.value |= proto; + } + + if (match(':')) { + lex(); + value = inheritCoverGrammar(parseAssignmentExpression); + return node.finishProperty('init', key, computed, value, false, false); + } + + if (token.type === Token.Identifier) { + if (match('=')) { + firstCoverInitializedNameError = lookahead; + lex(); + value = isolateCoverGrammar(parseAssignmentExpression); + return node.finishProperty('init', key, computed, + new WrappingNode(token).finishAssignmentPattern(key, value), false, true); + } + return node.finishProperty('init', key, computed, key, false, true); + } + + throwUnexpectedToken(lookahead); + } + + function parseObjectInitializer() { + var properties = [], hasProto = {value: false}, node = new Node(); + + expect('{'); + + while (!match('}')) { + properties.push(parseObjectProperty(hasProto)); + + if (!match('}')) { + expectCommaSeparator(); + } + } + + expect('}'); + + return node.finishObjectExpression(properties); + } + + function reinterpretExpressionAsPattern(expr) { + var i; + switch (expr.type) { + case Syntax.Identifier: + case Syntax.MemberExpression: + case Syntax.RestElement: + case Syntax.AssignmentPattern: + break; + case Syntax.SpreadElement: + expr.type = Syntax.RestElement; + reinterpretExpressionAsPattern(expr.argument); + break; + case Syntax.ArrayExpression: + expr.type = Syntax.ArrayPattern; + for (i = 0; i < expr.elements.length; i++) { + if (expr.elements[i] !== null) { + reinterpretExpressionAsPattern(expr.elements[i]); + } + } + break; + case Syntax.ObjectExpression: + expr.type = Syntax.ObjectPattern; + for (i = 0; i < expr.properties.length; i++) { + reinterpretExpressionAsPattern(expr.properties[i].value); + } + break; + case Syntax.AssignmentExpression: + expr.type = Syntax.AssignmentPattern; + reinterpretExpressionAsPattern(expr.left); + break; + default: + // Allow other node type for tolerant parsing. + break; + } + } + + // ECMA-262 12.2.9 Template Literals + + function parseTemplateElement(option) { + var node, token; + + if (lookahead.type !== Token.Template || (option.head && !lookahead.head)) { + throwUnexpectedToken(); + } + + node = new Node(); + token = lex(); + + return node.finishTemplateElement({ raw: token.value.raw, cooked: token.value.cooked }, token.tail); + } + + function parseTemplateLiteral() { + var quasi, quasis, expressions, node = new Node(); + + quasi = parseTemplateElement({ head: true }); + quasis = [quasi]; + expressions = []; + + while (!quasi.tail) { + expressions.push(parseExpression()); + quasi = parseTemplateElement({ head: false }); + quasis.push(quasi); + } + + return node.finishTemplateLiteral(quasis, expressions); + } + + // ECMA-262 12.2.10 The Grouping Operator + + function parseGroupExpression() { + var expr, expressions, startToken, i, params = []; + + expect('('); + + if (match(')')) { + lex(); + if (!match('=>')) { + expect('=>'); + } + return { + type: PlaceHolders.ArrowParameterPlaceHolder, + params: [], + rawParams: [] + }; + } + + startToken = lookahead; + if (match('...')) { + expr = parseRestElement(params); + expect(')'); + if (!match('=>')) { + expect('=>'); + } + return { + type: PlaceHolders.ArrowParameterPlaceHolder, + params: [expr] + }; + } + + isBindingElement = true; + expr = inheritCoverGrammar(parseAssignmentExpression); + + if (match(',')) { + isAssignmentTarget = false; + expressions = [expr]; + + while (startIndex < length) { + if (!match(',')) { + break; + } + lex(); + + if (match('...')) { + if (!isBindingElement) { + throwUnexpectedToken(lookahead); + } + expressions.push(parseRestElement(params)); + expect(')'); + if (!match('=>')) { + expect('=>'); + } + isBindingElement = false; + for (i = 0; i < expressions.length; i++) { + reinterpretExpressionAsPattern(expressions[i]); + } + return { + type: PlaceHolders.ArrowParameterPlaceHolder, + params: expressions + }; + } + + expressions.push(inheritCoverGrammar(parseAssignmentExpression)); + } + + expr = new WrappingNode(startToken).finishSequenceExpression(expressions); + } + + + expect(')'); + + if (match('=>')) { + if (expr.type === Syntax.Identifier && expr.name === 'yield') { + return { + type: PlaceHolders.ArrowParameterPlaceHolder, + params: [expr] + }; + } + + if (!isBindingElement) { + throwUnexpectedToken(lookahead); + } + + if (expr.type === Syntax.SequenceExpression) { + for (i = 0; i < expr.expressions.length; i++) { + reinterpretExpressionAsPattern(expr.expressions[i]); + } + } else { + reinterpretExpressionAsPattern(expr); + } + + expr = { + type: PlaceHolders.ArrowParameterPlaceHolder, + params: expr.type === Syntax.SequenceExpression ? expr.expressions : [expr] + }; + } + isBindingElement = false; + return expr; + } + + + // ECMA-262 12.2 Primary Expressions + + function parsePrimaryExpression() { + var type, token, expr, node; + + if (match('(')) { + isBindingElement = false; + return inheritCoverGrammar(parseGroupExpression); + } + + if (match('[')) { + return inheritCoverGrammar(parseArrayInitializer); + } + + if (match('{')) { + return inheritCoverGrammar(parseObjectInitializer); + } + + type = lookahead.type; + node = new Node(); + + if (type === Token.Identifier) { + if (state.sourceType === 'module' && lookahead.value === 'await') { + tolerateUnexpectedToken(lookahead); + } + expr = node.finishIdentifier(lex().value); + } else if (type === Token.StringLiteral || type === Token.NumericLiteral) { + isAssignmentTarget = isBindingElement = false; + if (strict && lookahead.octal) { + tolerateUnexpectedToken(lookahead, Messages.StrictOctalLiteral); + } + expr = node.finishLiteral(lex()); + } else if (type === Token.Keyword) { + if (!strict && state.allowYield && matchKeyword('yield')) { + return parseNonComputedProperty(); + } + if (!strict && matchKeyword('let')) { + return node.finishIdentifier(lex().value); + } + isAssignmentTarget = isBindingElement = false; + if (matchKeyword('function')) { + return parseFunctionExpression(); + } + if (matchKeyword('this')) { + lex(); + return node.finishThisExpression(); + } + if (matchKeyword('class')) { + return parseClassExpression(); + } + throwUnexpectedToken(lex()); + } else if (type === Token.BooleanLiteral) { + isAssignmentTarget = isBindingElement = false; + token = lex(); + token.value = (token.value === 'true'); + expr = node.finishLiteral(token); + } else if (type === Token.NullLiteral) { + isAssignmentTarget = isBindingElement = false; + token = lex(); + token.value = null; + expr = node.finishLiteral(token); + } else if (match('/') || match('/=')) { + isAssignmentTarget = isBindingElement = false; + index = startIndex; + + if (typeof extra.tokens !== 'undefined') { + token = collectRegex(); + } else { + token = scanRegExp(); + } + lex(); + expr = node.finishLiteral(token); + } else if (type === Token.Template) { + expr = parseTemplateLiteral(); + } else { + throwUnexpectedToken(lex()); + } + + return expr; + } + + // ECMA-262 12.3 Left-Hand-Side Expressions + + function parseArguments() { + var args = [], expr; + + expect('('); + + if (!match(')')) { + while (startIndex < length) { + if (match('...')) { + expr = new Node(); + lex(); + expr.finishSpreadElement(isolateCoverGrammar(parseAssignmentExpression)); + } else { + expr = isolateCoverGrammar(parseAssignmentExpression); + } + args.push(expr); + if (match(')')) { + break; + } + expectCommaSeparator(); + } + } + + expect(')'); + + return args; + } + + function parseNonComputedProperty() { + var token, node = new Node(); + + token = lex(); + + if (!isIdentifierName(token)) { + throwUnexpectedToken(token); + } + + return node.finishIdentifier(token.value); + } + + function parseNonComputedMember() { + expect('.'); + + return parseNonComputedProperty(); + } + + function parseComputedMember() { + var expr; + + expect('['); + + expr = isolateCoverGrammar(parseExpression); + + expect(']'); + + return expr; + } + + // ECMA-262 12.3.3 The new Operator + + function parseNewExpression() { + var callee, args, node = new Node(); + + expectKeyword('new'); + + if (match('.')) { + lex(); + if (lookahead.type === Token.Identifier && lookahead.value === 'target') { + if (state.inFunctionBody) { + lex(); + return node.finishMetaProperty('new', 'target'); + } + } + throwUnexpectedToken(lookahead); + } + + callee = isolateCoverGrammar(parseLeftHandSideExpression); + args = match('(') ? parseArguments() : []; + + isAssignmentTarget = isBindingElement = false; + + return node.finishNewExpression(callee, args); + } + + // ECMA-262 12.3.4 Function Calls + + function parseLeftHandSideExpressionAllowCall() { + var quasi, expr, args, property, startToken, previousAllowIn = state.allowIn; + + startToken = lookahead; + state.allowIn = true; + + if (matchKeyword('super') && state.inFunctionBody) { + expr = new Node(); + lex(); + expr = expr.finishSuper(); + if (!match('(') && !match('.') && !match('[')) { + throwUnexpectedToken(lookahead); + } + } else { + expr = inheritCoverGrammar(matchKeyword('new') ? parseNewExpression : parsePrimaryExpression); + } + + for (;;) { + if (match('.')) { + isBindingElement = false; + isAssignmentTarget = true; + property = parseNonComputedMember(); + expr = new WrappingNode(startToken).finishMemberExpression('.', expr, property); + } else if (match('(')) { + isBindingElement = false; + isAssignmentTarget = false; + args = parseArguments(); + expr = new WrappingNode(startToken).finishCallExpression(expr, args); + } else if (match('[')) { + isBindingElement = false; + isAssignmentTarget = true; + property = parseComputedMember(); + expr = new WrappingNode(startToken).finishMemberExpression('[', expr, property); + } else if (lookahead.type === Token.Template && lookahead.head) { + quasi = parseTemplateLiteral(); + expr = new WrappingNode(startToken).finishTaggedTemplateExpression(expr, quasi); + } else { + break; + } + } + state.allowIn = previousAllowIn; + + return expr; + } + + // ECMA-262 12.3 Left-Hand-Side Expressions + + function parseLeftHandSideExpression() { + var quasi, expr, property, startToken; + assert(state.allowIn, 'callee of new expression always allow in keyword.'); + + startToken = lookahead; + + if (matchKeyword('super') && state.inFunctionBody) { + expr = new Node(); + lex(); + expr = expr.finishSuper(); + if (!match('[') && !match('.')) { + throwUnexpectedToken(lookahead); + } + } else { + expr = inheritCoverGrammar(matchKeyword('new') ? parseNewExpression : parsePrimaryExpression); + } + + for (;;) { + if (match('[')) { + isBindingElement = false; + isAssignmentTarget = true; + property = parseComputedMember(); + expr = new WrappingNode(startToken).finishMemberExpression('[', expr, property); + } else if (match('.')) { + isBindingElement = false; + isAssignmentTarget = true; + property = parseNonComputedMember(); + expr = new WrappingNode(startToken).finishMemberExpression('.', expr, property); + } else if (lookahead.type === Token.Template && lookahead.head) { + quasi = parseTemplateLiteral(); + expr = new WrappingNode(startToken).finishTaggedTemplateExpression(expr, quasi); + } else { + break; + } + } + return expr; + } + + // ECMA-262 12.4 Postfix Expressions + + function parsePostfixExpression() { + var expr, token, startToken = lookahead; + + expr = inheritCoverGrammar(parseLeftHandSideExpressionAllowCall); + + if (!hasLineTerminator && lookahead.type === Token.Punctuator) { + if (match('++') || match('--')) { + // ECMA-262 11.3.1, 11.3.2 + if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { + tolerateError(Messages.StrictLHSPostfix); + } + + if (!isAssignmentTarget) { + tolerateError(Messages.InvalidLHSInAssignment); + } + + isAssignmentTarget = isBindingElement = false; + + token = lex(); + expr = new WrappingNode(startToken).finishPostfixExpression(token.value, expr); + } + } + + return expr; + } + + // ECMA-262 12.5 Unary Operators + + function parseUnaryExpression() { + var token, expr, startToken; + + if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) { + expr = parsePostfixExpression(); + } else if (match('++') || match('--')) { + startToken = lookahead; + token = lex(); + expr = inheritCoverGrammar(parseUnaryExpression); + // ECMA-262 11.4.4, 11.4.5 + if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { + tolerateError(Messages.StrictLHSPrefix); + } + + if (!isAssignmentTarget) { + tolerateError(Messages.InvalidLHSInAssignment); + } + expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr); + isAssignmentTarget = isBindingElement = false; + } else if (match('+') || match('-') || match('~') || match('!')) { + startToken = lookahead; + token = lex(); + expr = inheritCoverGrammar(parseUnaryExpression); + expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr); + isAssignmentTarget = isBindingElement = false; + } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { + startToken = lookahead; + token = lex(); + expr = inheritCoverGrammar(parseUnaryExpression); + expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr); + if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) { + tolerateError(Messages.StrictDelete); + } + isAssignmentTarget = isBindingElement = false; + } else { + expr = parsePostfixExpression(); + } + + return expr; + } + + function binaryPrecedence(token, allowIn) { + var prec = 0; + + if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { + return 0; + } + + switch (token.value) { + case '||': + prec = 1; + break; + + case '&&': + prec = 2; + break; + + case '|': + prec = 3; + break; + + case '^': + prec = 4; + break; + + case '&': + prec = 5; + break; + + case '==': + case '!=': + case '===': + case '!==': + prec = 6; + break; + + case '<': + case '>': + case '<=': + case '>=': + case 'instanceof': + prec = 7; + break; + + case 'in': + prec = allowIn ? 7 : 0; + break; + + case '<<': + case '>>': + case '>>>': + prec = 8; + break; + + case '+': + case '-': + prec = 9; + break; + + case '*': + case '/': + case '%': + prec = 11; + break; + + default: + break; + } + + return prec; + } + + // ECMA-262 12.6 Multiplicative Operators + // ECMA-262 12.7 Additive Operators + // ECMA-262 12.8 Bitwise Shift Operators + // ECMA-262 12.9 Relational Operators + // ECMA-262 12.10 Equality Operators + // ECMA-262 12.11 Binary Bitwise Operators + // ECMA-262 12.12 Binary Logical Operators + + function parseBinaryExpression() { + var marker, markers, expr, token, prec, stack, right, operator, left, i; + + marker = lookahead; + left = inheritCoverGrammar(parseUnaryExpression); + + token = lookahead; + prec = binaryPrecedence(token, state.allowIn); + if (prec === 0) { + return left; + } + isAssignmentTarget = isBindingElement = false; + token.prec = prec; + lex(); + + markers = [marker, lookahead]; + right = isolateCoverGrammar(parseUnaryExpression); + + stack = [left, token, right]; + + while ((prec = binaryPrecedence(lookahead, state.allowIn)) > 0) { + + // Reduce: make a binary expression from the three topmost entries. + while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { + right = stack.pop(); + operator = stack.pop().value; + left = stack.pop(); + markers.pop(); + expr = new WrappingNode(markers[markers.length - 1]).finishBinaryExpression(operator, left, right); + stack.push(expr); + } + + // Shift. + token = lex(); + token.prec = prec; + stack.push(token); + markers.push(lookahead); + expr = isolateCoverGrammar(parseUnaryExpression); + stack.push(expr); + } + + // Final reduce to clean-up the stack. + i = stack.length - 1; + expr = stack[i]; + markers.pop(); + while (i > 1) { + expr = new WrappingNode(markers.pop()).finishBinaryExpression(stack[i - 1].value, stack[i - 2], expr); + i -= 2; + } + + return expr; + } + + + // ECMA-262 12.13 Conditional Operator + + function parseConditionalExpression() { + var expr, previousAllowIn, consequent, alternate, startToken; + + startToken = lookahead; + + expr = inheritCoverGrammar(parseBinaryExpression); + if (match('?')) { + lex(); + previousAllowIn = state.allowIn; + state.allowIn = true; + consequent = isolateCoverGrammar(parseAssignmentExpression); + state.allowIn = previousAllowIn; + expect(':'); + alternate = isolateCoverGrammar(parseAssignmentExpression); + + expr = new WrappingNode(startToken).finishConditionalExpression(expr, consequent, alternate); + isAssignmentTarget = isBindingElement = false; + } + + return expr; + } + + // ECMA-262 14.2 Arrow Function Definitions + + function parseConciseBody() { + if (match('{')) { + return parseFunctionSourceElements(); + } + return isolateCoverGrammar(parseAssignmentExpression); + } + + function checkPatternParam(options, param) { + var i; + switch (param.type) { + case Syntax.Identifier: + validateParam(options, param, param.name); + break; + case Syntax.RestElement: + checkPatternParam(options, param.argument); + break; + case Syntax.AssignmentPattern: + checkPatternParam(options, param.left); + break; + case Syntax.ArrayPattern: + for (i = 0; i < param.elements.length; i++) { + if (param.elements[i] !== null) { + checkPatternParam(options, param.elements[i]); + } + } + break; + case Syntax.YieldExpression: + break; + default: + assert(param.type === Syntax.ObjectPattern, 'Invalid type'); + for (i = 0; i < param.properties.length; i++) { + checkPatternParam(options, param.properties[i].value); + } + break; + } + } + function reinterpretAsCoverFormalsList(expr) { + var i, len, param, params, defaults, defaultCount, options, token; + + defaults = []; + defaultCount = 0; + params = [expr]; + + switch (expr.type) { + case Syntax.Identifier: + break; + case PlaceHolders.ArrowParameterPlaceHolder: + params = expr.params; + break; + default: + return null; + } + + options = { + paramSet: {} + }; + + for (i = 0, len = params.length; i < len; i += 1) { + param = params[i]; + switch (param.type) { + case Syntax.AssignmentPattern: + params[i] = param.left; + if (param.right.type === Syntax.YieldExpression) { + if (param.right.argument) { + throwUnexpectedToken(lookahead); + } + param.right.type = Syntax.Identifier; + param.right.name = 'yield'; + delete param.right.argument; + delete param.right.delegate; + } + defaults.push(param.right); + ++defaultCount; + checkPatternParam(options, param.left); + break; + default: + checkPatternParam(options, param); + params[i] = param; + defaults.push(null); + break; + } + } + + if (strict || !state.allowYield) { + for (i = 0, len = params.length; i < len; i += 1) { + param = params[i]; + if (param.type === Syntax.YieldExpression) { + throwUnexpectedToken(lookahead); + } + } + } + + if (options.message === Messages.StrictParamDupe) { + token = strict ? options.stricted : options.firstRestricted; + throwUnexpectedToken(token, options.message); + } + + if (defaultCount === 0) { + defaults = []; + } + + return { + params: params, + defaults: defaults, + stricted: options.stricted, + firstRestricted: options.firstRestricted, + message: options.message + }; + } + + function parseArrowFunctionExpression(options, node) { + var previousStrict, previousAllowYield, body; + + if (hasLineTerminator) { + tolerateUnexpectedToken(lookahead); + } + expect('=>'); + + previousStrict = strict; + previousAllowYield = state.allowYield; + state.allowYield = true; + + body = parseConciseBody(); + + if (strict && options.firstRestricted) { + throwUnexpectedToken(options.firstRestricted, options.message); + } + if (strict && options.stricted) { + tolerateUnexpectedToken(options.stricted, options.message); + } + + strict = previousStrict; + state.allowYield = previousAllowYield; + + return node.finishArrowFunctionExpression(options.params, options.defaults, body, body.type !== Syntax.BlockStatement); + } + + // ECMA-262 14.4 Yield expression + + function parseYieldExpression() { + var argument, expr, delegate, previousAllowYield; + + argument = null; + expr = new Node(); + delegate = false; + + expectKeyword('yield'); + + if (!hasLineTerminator) { + previousAllowYield = state.allowYield; + state.allowYield = false; + delegate = match('*'); + if (delegate) { + lex(); + argument = parseAssignmentExpression(); + } else { + if (!match(';') && !match('}') && !match(')') && lookahead.type !== Token.EOF) { + argument = parseAssignmentExpression(); + } + } + state.allowYield = previousAllowYield; + } + + return expr.finishYieldExpression(argument, delegate); + } + + // ECMA-262 12.14 Assignment Operators + + function parseAssignmentExpression() { + var token, expr, right, list, startToken; + + startToken = lookahead; + token = lookahead; + + if (!state.allowYield && matchKeyword('yield')) { + return parseYieldExpression(); + } + + expr = parseConditionalExpression(); + + if (expr.type === PlaceHolders.ArrowParameterPlaceHolder || match('=>')) { + isAssignmentTarget = isBindingElement = false; + list = reinterpretAsCoverFormalsList(expr); + + if (list) { + firstCoverInitializedNameError = null; + return parseArrowFunctionExpression(list, new WrappingNode(startToken)); + } + + return expr; + } + + if (matchAssign()) { + if (!isAssignmentTarget) { + tolerateError(Messages.InvalidLHSInAssignment); + } + + // ECMA-262 12.1.1 + if (strict && expr.type === Syntax.Identifier) { + if (isRestrictedWord(expr.name)) { + tolerateUnexpectedToken(token, Messages.StrictLHSAssignment); + } + if (isStrictModeReservedWord(expr.name)) { + tolerateUnexpectedToken(token, Messages.StrictReservedWord); + } + } + + if (!match('=')) { + isAssignmentTarget = isBindingElement = false; + } else { + reinterpretExpressionAsPattern(expr); + } + + token = lex(); + right = isolateCoverGrammar(parseAssignmentExpression); + expr = new WrappingNode(startToken).finishAssignmentExpression(token.value, expr, right); + firstCoverInitializedNameError = null; + } + + return expr; + } + + // ECMA-262 12.15 Comma Operator + + function parseExpression() { + var expr, startToken = lookahead, expressions; + + expr = isolateCoverGrammar(parseAssignmentExpression); + + if (match(',')) { + expressions = [expr]; + + while (startIndex < length) { + if (!match(',')) { + break; + } + lex(); + expressions.push(isolateCoverGrammar(parseAssignmentExpression)); + } + + expr = new WrappingNode(startToken).finishSequenceExpression(expressions); + } + + return expr; + } + + // ECMA-262 13.2 Block + + function parseStatementListItem() { + if (lookahead.type === Token.Keyword) { + switch (lookahead.value) { + case 'export': + if (state.sourceType !== 'module') { + tolerateUnexpectedToken(lookahead, Messages.IllegalExportDeclaration); + } + return parseExportDeclaration(); + case 'import': + if (state.sourceType !== 'module') { + tolerateUnexpectedToken(lookahead, Messages.IllegalImportDeclaration); + } + return parseImportDeclaration(); + case 'const': + return parseLexicalDeclaration({inFor: false}); + case 'function': + return parseFunctionDeclaration(new Node()); + case 'class': + return parseClassDeclaration(); + } + } + + if (matchKeyword('let') && isLexicalDeclaration()) { + return parseLexicalDeclaration({inFor: false}); + } + + return parseStatement(); + } + + function parseStatementList() { + var list = []; + while (startIndex < length) { + if (match('}')) { + break; + } + list.push(parseStatementListItem()); + } + + return list; + } + + function parseBlock() { + var block, node = new Node(); + + expect('{'); + + block = parseStatementList(); + + expect('}'); + + return node.finishBlockStatement(block); + } + + // ECMA-262 13.3.2 Variable Statement + + function parseVariableIdentifier(kind) { + var token, node = new Node(); + + token = lex(); + + if (token.type === Token.Keyword && token.value === 'yield') { + if (strict) { + tolerateUnexpectedToken(token, Messages.StrictReservedWord); + } if (!state.allowYield) { + throwUnexpectedToken(token); + } + } else if (token.type !== Token.Identifier) { + if (strict && token.type === Token.Keyword && isStrictModeReservedWord(token.value)) { + tolerateUnexpectedToken(token, Messages.StrictReservedWord); + } else { + if (strict || token.value !== 'let' || kind !== 'var') { + throwUnexpectedToken(token); + } + } + } else if (state.sourceType === 'module' && token.type === Token.Identifier && token.value === 'await') { + tolerateUnexpectedToken(token); + } + + return node.finishIdentifier(token.value); + } + + function parseVariableDeclaration(options) { + var init = null, id, node = new Node(), params = []; + + id = parsePattern(params, 'var'); + + // ECMA-262 12.2.1 + if (strict && isRestrictedWord(id.name)) { + tolerateError(Messages.StrictVarName); + } + + if (match('=')) { + lex(); + init = isolateCoverGrammar(parseAssignmentExpression); + } else if (id.type !== Syntax.Identifier && !options.inFor) { + expect('='); + } + + return node.finishVariableDeclarator(id, init); + } + + function parseVariableDeclarationList(options) { + var opt, list; + + opt = { inFor: options.inFor }; + list = [parseVariableDeclaration(opt)]; + + while (match(',')) { + lex(); + list.push(parseVariableDeclaration(opt)); + } + + return list; + } + + function parseVariableStatement(node) { + var declarations; + + expectKeyword('var'); + + declarations = parseVariableDeclarationList({ inFor: false }); + + consumeSemicolon(); + + return node.finishVariableDeclaration(declarations); + } + + // ECMA-262 13.3.1 Let and Const Declarations + + function parseLexicalBinding(kind, options) { + var init = null, id, node = new Node(), params = []; + + id = parsePattern(params, kind); + + // ECMA-262 12.2.1 + if (strict && id.type === Syntax.Identifier && isRestrictedWord(id.name)) { + tolerateError(Messages.StrictVarName); + } + + if (kind === 'const') { + if (!matchKeyword('in') && !matchContextualKeyword('of')) { + expect('='); + init = isolateCoverGrammar(parseAssignmentExpression); + } + } else if ((!options.inFor && id.type !== Syntax.Identifier) || match('=')) { + expect('='); + init = isolateCoverGrammar(parseAssignmentExpression); + } + + return node.finishVariableDeclarator(id, init); + } + + function parseBindingList(kind, options) { + var list = [parseLexicalBinding(kind, options)]; + + while (match(',')) { + lex(); + list.push(parseLexicalBinding(kind, options)); + } + + return list; + } + + + function tokenizerState() { + return { + index: index, + lineNumber: lineNumber, + lineStart: lineStart, + hasLineTerminator: hasLineTerminator, + lastIndex: lastIndex, + lastLineNumber: lastLineNumber, + lastLineStart: lastLineStart, + startIndex: startIndex, + startLineNumber: startLineNumber, + startLineStart: startLineStart, + lookahead: lookahead, + tokenCount: extra.tokens ? extra.tokens.length : 0 + }; + } + + function resetTokenizerState(ts) { + index = ts.index; + lineNumber = ts.lineNumber; + lineStart = ts.lineStart; + hasLineTerminator = ts.hasLineTerminator; + lastIndex = ts.lastIndex; + lastLineNumber = ts.lastLineNumber; + lastLineStart = ts.lastLineStart; + startIndex = ts.startIndex; + startLineNumber = ts.startLineNumber; + startLineStart = ts.startLineStart; + lookahead = ts.lookahead; + if (extra.tokens) { + extra.tokens.splice(ts.tokenCount, extra.tokens.length); + } + } + + function isLexicalDeclaration() { + var lexical, ts; + + ts = tokenizerState(); + + lex(); + lexical = (lookahead.type === Token.Identifier) || match('[') || match('{') || + matchKeyword('let') || matchKeyword('yield'); + + resetTokenizerState(ts); + + return lexical; + } + + function parseLexicalDeclaration(options) { + var kind, declarations, node = new Node(); + + kind = lex().value; + assert(kind === 'let' || kind === 'const', 'Lexical declaration must be either let or const'); + + declarations = parseBindingList(kind, options); + + consumeSemicolon(); + + return node.finishLexicalDeclaration(declarations, kind); + } + + function parseRestElement(params) { + var param, node = new Node(); + + lex(); + + if (match('{')) { + throwError(Messages.ObjectPatternAsRestParameter); + } + + params.push(lookahead); + + param = parseVariableIdentifier(); + + if (match('=')) { + throwError(Messages.DefaultRestParameter); + } + + if (!match(')')) { + throwError(Messages.ParameterAfterRestParameter); + } + + return node.finishRestElement(param); + } + + // ECMA-262 13.4 Empty Statement + + function parseEmptyStatement(node) { + expect(';'); + return node.finishEmptyStatement(); + } + + // ECMA-262 12.4 Expression Statement + + function parseExpressionStatement(node) { + var expr = parseExpression(); + consumeSemicolon(); + return node.finishExpressionStatement(expr); + } + + // ECMA-262 13.6 If statement + + function parseIfStatement(node) { + var test, consequent, alternate; + + expectKeyword('if'); + + expect('('); + + test = parseExpression(); + + expect(')'); + + consequent = parseStatement(); + + if (matchKeyword('else')) { + lex(); + alternate = parseStatement(); + } else { + alternate = null; + } + + return node.finishIfStatement(test, consequent, alternate); + } + + // ECMA-262 13.7 Iteration Statements + + function parseDoWhileStatement(node) { + var body, test, oldInIteration; + + expectKeyword('do'); + + oldInIteration = state.inIteration; + state.inIteration = true; + + body = parseStatement(); + + state.inIteration = oldInIteration; + + expectKeyword('while'); + + expect('('); + + test = parseExpression(); + + expect(')'); + + if (match(';')) { + lex(); + } + + return node.finishDoWhileStatement(body, test); + } + + function parseWhileStatement(node) { + var test, body, oldInIteration; + + expectKeyword('while'); + + expect('('); + + test = parseExpression(); + + expect(')'); + + oldInIteration = state.inIteration; + state.inIteration = true; + + body = parseStatement(); + + state.inIteration = oldInIteration; + + return node.finishWhileStatement(test, body); + } + + function parseForStatement(node) { + var init, forIn, initSeq, initStartToken, test, update, left, right, kind, declarations, + body, oldInIteration, previousAllowIn = state.allowIn; + + init = test = update = null; + forIn = true; + + expectKeyword('for'); + + expect('('); + + if (match(';')) { + lex(); + } else { + if (matchKeyword('var')) { + init = new Node(); + lex(); + + state.allowIn = false; + declarations = parseVariableDeclarationList({ inFor: true }); + state.allowIn = previousAllowIn; + + if (declarations.length === 1 && matchKeyword('in')) { + init = init.finishVariableDeclaration(declarations); + lex(); + left = init; + right = parseExpression(); + init = null; + } else if (declarations.length === 1 && declarations[0].init === null && matchContextualKeyword('of')) { + init = init.finishVariableDeclaration(declarations); + lex(); + left = init; + right = parseAssignmentExpression(); + init = null; + forIn = false; + } else { + init = init.finishVariableDeclaration(declarations); + expect(';'); + } + } else if (matchKeyword('const') || matchKeyword('let')) { + init = new Node(); + kind = lex().value; + + if (!strict && lookahead.value === 'in') { + init = init.finishIdentifier(kind); + lex(); + left = init; + right = parseExpression(); + init = null; + } else { + state.allowIn = false; + declarations = parseBindingList(kind, {inFor: true}); + state.allowIn = previousAllowIn; + + if (declarations.length === 1 && declarations[0].init === null && matchKeyword('in')) { + init = init.finishLexicalDeclaration(declarations, kind); + lex(); + left = init; + right = parseExpression(); + init = null; + } else if (declarations.length === 1 && declarations[0].init === null && matchContextualKeyword('of')) { + init = init.finishLexicalDeclaration(declarations, kind); + lex(); + left = init; + right = parseAssignmentExpression(); + init = null; + forIn = false; + } else { + consumeSemicolon(); + init = init.finishLexicalDeclaration(declarations, kind); + } + } + } else { + initStartToken = lookahead; + state.allowIn = false; + init = inheritCoverGrammar(parseAssignmentExpression); + state.allowIn = previousAllowIn; + + if (matchKeyword('in')) { + if (!isAssignmentTarget) { + tolerateError(Messages.InvalidLHSInForIn); + } + + lex(); + reinterpretExpressionAsPattern(init); + left = init; + right = parseExpression(); + init = null; + } else if (matchContextualKeyword('of')) { + if (!isAssignmentTarget) { + tolerateError(Messages.InvalidLHSInForLoop); + } + + lex(); + reinterpretExpressionAsPattern(init); + left = init; + right = parseAssignmentExpression(); + init = null; + forIn = false; + } else { + if (match(',')) { + initSeq = [init]; + while (match(',')) { + lex(); + initSeq.push(isolateCoverGrammar(parseAssignmentExpression)); + } + init = new WrappingNode(initStartToken).finishSequenceExpression(initSeq); + } + expect(';'); + } + } + } + + if (typeof left === 'undefined') { + + if (!match(';')) { + test = parseExpression(); + } + expect(';'); + + if (!match(')')) { + update = parseExpression(); + } + } + + expect(')'); + + oldInIteration = state.inIteration; + state.inIteration = true; + + body = isolateCoverGrammar(parseStatement); + + state.inIteration = oldInIteration; + + return (typeof left === 'undefined') ? + node.finishForStatement(init, test, update, body) : + forIn ? node.finishForInStatement(left, right, body) : + node.finishForOfStatement(left, right, body); + } + + // ECMA-262 13.8 The continue statement + + function parseContinueStatement(node) { + var label = null, key; + + expectKeyword('continue'); + + // Optimize the most common form: 'continue;'. + if (source.charCodeAt(startIndex) === 0x3B) { + lex(); + + if (!state.inIteration) { + throwError(Messages.IllegalContinue); + } + + return node.finishContinueStatement(null); + } + + if (hasLineTerminator) { + if (!state.inIteration) { + throwError(Messages.IllegalContinue); + } + + return node.finishContinueStatement(null); + } + + if (lookahead.type === Token.Identifier) { + label = parseVariableIdentifier(); + + key = '$' + label.name; + if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { + throwError(Messages.UnknownLabel, label.name); + } + } + + consumeSemicolon(); + + if (label === null && !state.inIteration) { + throwError(Messages.IllegalContinue); + } + + return node.finishContinueStatement(label); + } + + // ECMA-262 13.9 The break statement + + function parseBreakStatement(node) { + var label = null, key; + + expectKeyword('break'); + + // Catch the very common case first: immediately a semicolon (U+003B). + if (source.charCodeAt(lastIndex) === 0x3B) { + lex(); + + if (!(state.inIteration || state.inSwitch)) { + throwError(Messages.IllegalBreak); + } + + return node.finishBreakStatement(null); + } + + if (hasLineTerminator) { + if (!(state.inIteration || state.inSwitch)) { + throwError(Messages.IllegalBreak); + } + } else if (lookahead.type === Token.Identifier) { + label = parseVariableIdentifier(); + + key = '$' + label.name; + if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { + throwError(Messages.UnknownLabel, label.name); + } + } + + consumeSemicolon(); + + if (label === null && !(state.inIteration || state.inSwitch)) { + throwError(Messages.IllegalBreak); + } + + return node.finishBreakStatement(label); + } + + // ECMA-262 13.10 The return statement + + function parseReturnStatement(node) { + var argument = null; + + expectKeyword('return'); + + if (!state.inFunctionBody) { + tolerateError(Messages.IllegalReturn); + } + + // 'return' followed by a space and an identifier is very common. + if (source.charCodeAt(lastIndex) === 0x20) { + if (isIdentifierStart(source.charCodeAt(lastIndex + 1))) { + argument = parseExpression(); + consumeSemicolon(); + return node.finishReturnStatement(argument); + } + } + + if (hasLineTerminator) { + // HACK + return node.finishReturnStatement(null); + } + + if (!match(';')) { + if (!match('}') && lookahead.type !== Token.EOF) { + argument = parseExpression(); + } + } + + consumeSemicolon(); + + return node.finishReturnStatement(argument); + } + + // ECMA-262 13.11 The with statement + + function parseWithStatement(node) { + var object, body; + + if (strict) { + tolerateError(Messages.StrictModeWith); + } + + expectKeyword('with'); + + expect('('); + + object = parseExpression(); + + expect(')'); + + body = parseStatement(); + + return node.finishWithStatement(object, body); + } + + // ECMA-262 13.12 The switch statement + + function parseSwitchCase() { + var test, consequent = [], statement, node = new Node(); + + if (matchKeyword('default')) { + lex(); + test = null; + } else { + expectKeyword('case'); + test = parseExpression(); + } + expect(':'); + + while (startIndex < length) { + if (match('}') || matchKeyword('default') || matchKeyword('case')) { + break; + } + statement = parseStatementListItem(); + consequent.push(statement); + } + + return node.finishSwitchCase(test, consequent); + } + + function parseSwitchStatement(node) { + var discriminant, cases, clause, oldInSwitch, defaultFound; + + expectKeyword('switch'); + + expect('('); + + discriminant = parseExpression(); + + expect(')'); + + expect('{'); + + cases = []; + + if (match('}')) { + lex(); + return node.finishSwitchStatement(discriminant, cases); + } + + oldInSwitch = state.inSwitch; + state.inSwitch = true; + defaultFound = false; + + while (startIndex < length) { + if (match('}')) { + break; + } + clause = parseSwitchCase(); + if (clause.test === null) { + if (defaultFound) { + throwError(Messages.MultipleDefaultsInSwitch); + } + defaultFound = true; + } + cases.push(clause); + } + + state.inSwitch = oldInSwitch; + + expect('}'); + + return node.finishSwitchStatement(discriminant, cases); + } + + // ECMA-262 13.14 The throw statement + + function parseThrowStatement(node) { + var argument; + + expectKeyword('throw'); + + if (hasLineTerminator) { + throwError(Messages.NewlineAfterThrow); + } + + argument = parseExpression(); + + consumeSemicolon(); + + return node.finishThrowStatement(argument); + } + + // ECMA-262 13.15 The try statement + + function parseCatchClause() { + var param, params = [], paramMap = {}, key, i, body, node = new Node(); + + expectKeyword('catch'); + + expect('('); + if (match(')')) { + throwUnexpectedToken(lookahead); + } + + param = parsePattern(params); + for (i = 0; i < params.length; i++) { + key = '$' + params[i].value; + if (Object.prototype.hasOwnProperty.call(paramMap, key)) { + tolerateError(Messages.DuplicateBinding, params[i].value); + } + paramMap[key] = true; + } + + // ECMA-262 12.14.1 + if (strict && isRestrictedWord(param.name)) { + tolerateError(Messages.StrictCatchVariable); + } + + expect(')'); + body = parseBlock(); + return node.finishCatchClause(param, body); + } + + function parseTryStatement(node) { + var block, handler = null, finalizer = null; + + expectKeyword('try'); + + block = parseBlock(); + + if (matchKeyword('catch')) { + handler = parseCatchClause(); + } + + if (matchKeyword('finally')) { + lex(); + finalizer = parseBlock(); + } + + if (!handler && !finalizer) { + throwError(Messages.NoCatchOrFinally); + } + + return node.finishTryStatement(block, handler, finalizer); + } + + // ECMA-262 13.16 The debugger statement + + function parseDebuggerStatement(node) { + expectKeyword('debugger'); + + consumeSemicolon(); + + return node.finishDebuggerStatement(); + } + + // 13 Statements + + function parseStatement() { + var type = lookahead.type, + expr, + labeledBody, + key, + node; + + if (type === Token.EOF) { + throwUnexpectedToken(lookahead); + } + + if (type === Token.Punctuator && lookahead.value === '{') { + return parseBlock(); + } + isAssignmentTarget = isBindingElement = true; + node = new Node(); + + if (type === Token.Punctuator) { + switch (lookahead.value) { + case ';': + return parseEmptyStatement(node); + case '(': + return parseExpressionStatement(node); + default: + break; + } + } else if (type === Token.Keyword) { + switch (lookahead.value) { + case 'break': + return parseBreakStatement(node); + case 'continue': + return parseContinueStatement(node); + case 'debugger': + return parseDebuggerStatement(node); + case 'do': + return parseDoWhileStatement(node); + case 'for': + return parseForStatement(node); + case 'function': + return parseFunctionDeclaration(node); + case 'if': + return parseIfStatement(node); + case 'return': + return parseReturnStatement(node); + case 'switch': + return parseSwitchStatement(node); + case 'throw': + return parseThrowStatement(node); + case 'try': + return parseTryStatement(node); + case 'var': + return parseVariableStatement(node); + case 'while': + return parseWhileStatement(node); + case 'with': + return parseWithStatement(node); + default: + break; + } + } + + expr = parseExpression(); + + // ECMA-262 12.12 Labelled Statements + if ((expr.type === Syntax.Identifier) && match(':')) { + lex(); + + key = '$' + expr.name; + if (Object.prototype.hasOwnProperty.call(state.labelSet, key)) { + throwError(Messages.Redeclaration, 'Label', expr.name); + } + + state.labelSet[key] = true; + labeledBody = parseStatement(); + delete state.labelSet[key]; + return node.finishLabeledStatement(expr, labeledBody); + } + + consumeSemicolon(); + + return node.finishExpressionStatement(expr); + } + + // ECMA-262 14.1 Function Definition + + function parseFunctionSourceElements() { + var statement, body = [], token, directive, firstRestricted, + oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, + node = new Node(); + + expect('{'); + + while (startIndex < length) { + if (lookahead.type !== Token.StringLiteral) { + break; + } + token = lookahead; + + statement = parseStatementListItem(); + body.push(statement); + if (statement.expression.type !== Syntax.Literal) { + // this is not directive + break; + } + directive = source.slice(token.start + 1, token.end - 1); + if (directive === 'use strict') { + strict = true; + if (firstRestricted) { + tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral); + } + } else { + if (!firstRestricted && token.octal) { + firstRestricted = token; + } + } + } + + oldLabelSet = state.labelSet; + oldInIteration = state.inIteration; + oldInSwitch = state.inSwitch; + oldInFunctionBody = state.inFunctionBody; + + state.labelSet = {}; + state.inIteration = false; + state.inSwitch = false; + state.inFunctionBody = true; + + while (startIndex < length) { + if (match('}')) { + break; + } + body.push(parseStatementListItem()); + } + + expect('}'); + + state.labelSet = oldLabelSet; + state.inIteration = oldInIteration; + state.inSwitch = oldInSwitch; + state.inFunctionBody = oldInFunctionBody; + + return node.finishBlockStatement(body); + } + + function validateParam(options, param, name) { + var key = '$' + name; + if (strict) { + if (isRestrictedWord(name)) { + options.stricted = param; + options.message = Messages.StrictParamName; + } + if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { + options.stricted = param; + options.message = Messages.StrictParamDupe; + } + } else if (!options.firstRestricted) { + if (isRestrictedWord(name)) { + options.firstRestricted = param; + options.message = Messages.StrictParamName; + } else if (isStrictModeReservedWord(name)) { + options.firstRestricted = param; + options.message = Messages.StrictReservedWord; + } else if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { + options.stricted = param; + options.message = Messages.StrictParamDupe; + } + } + options.paramSet[key] = true; + } + + function parseParam(options) { + var token, param, params = [], i, def; + + token = lookahead; + if (token.value === '...') { + param = parseRestElement(params); + validateParam(options, param.argument, param.argument.name); + options.params.push(param); + options.defaults.push(null); + return false; + } + + param = parsePatternWithDefault(params); + for (i = 0; i < params.length; i++) { + validateParam(options, params[i], params[i].value); + } + + if (param.type === Syntax.AssignmentPattern) { + def = param.right; + param = param.left; + ++options.defaultCount; + } + + options.params.push(param); + options.defaults.push(def); + + return !match(')'); + } + + function parseParams(firstRestricted) { + var options; + + options = { + params: [], + defaultCount: 0, + defaults: [], + firstRestricted: firstRestricted + }; + + expect('('); + + if (!match(')')) { + options.paramSet = {}; + while (startIndex < length) { + if (!parseParam(options)) { + break; + } + expect(','); + } + } + + expect(')'); + + if (options.defaultCount === 0) { + options.defaults = []; + } + + return { + params: options.params, + defaults: options.defaults, + stricted: options.stricted, + firstRestricted: options.firstRestricted, + message: options.message + }; + } + + function parseFunctionDeclaration(node, identifierIsOptional) { + var id = null, params = [], defaults = [], body, token, stricted, tmp, firstRestricted, message, previousStrict, + isGenerator, previousAllowYield; + + previousAllowYield = state.allowYield; + + expectKeyword('function'); + + isGenerator = match('*'); + if (isGenerator) { + lex(); + } + + if (!identifierIsOptional || !match('(')) { + token = lookahead; + id = parseVariableIdentifier(); + if (strict) { + if (isRestrictedWord(token.value)) { + tolerateUnexpectedToken(token, Messages.StrictFunctionName); + } + } else { + if (isRestrictedWord(token.value)) { + firstRestricted = token; + message = Messages.StrictFunctionName; + } else if (isStrictModeReservedWord(token.value)) { + firstRestricted = token; + message = Messages.StrictReservedWord; + } + } + } + + state.allowYield = !isGenerator; + tmp = parseParams(firstRestricted); + params = tmp.params; + defaults = tmp.defaults; + stricted = tmp.stricted; + firstRestricted = tmp.firstRestricted; + if (tmp.message) { + message = tmp.message; + } + + + previousStrict = strict; + body = parseFunctionSourceElements(); + if (strict && firstRestricted) { + throwUnexpectedToken(firstRestricted, message); + } + if (strict && stricted) { + tolerateUnexpectedToken(stricted, message); + } + + strict = previousStrict; + state.allowYield = previousAllowYield; + + return node.finishFunctionDeclaration(id, params, defaults, body, isGenerator); + } + + function parseFunctionExpression() { + var token, id = null, stricted, firstRestricted, message, tmp, + params = [], defaults = [], body, previousStrict, node = new Node(), + isGenerator, previousAllowYield; + + previousAllowYield = state.allowYield; + + expectKeyword('function'); + + isGenerator = match('*'); + if (isGenerator) { + lex(); + } + + state.allowYield = !isGenerator; + if (!match('(')) { + token = lookahead; + id = (!strict && !isGenerator && matchKeyword('yield')) ? parseNonComputedProperty() : parseVariableIdentifier(); + if (strict) { + if (isRestrictedWord(token.value)) { + tolerateUnexpectedToken(token, Messages.StrictFunctionName); + } + } else { + if (isRestrictedWord(token.value)) { + firstRestricted = token; + message = Messages.StrictFunctionName; + } else if (isStrictModeReservedWord(token.value)) { + firstRestricted = token; + message = Messages.StrictReservedWord; + } + } + } + + tmp = parseParams(firstRestricted); + params = tmp.params; + defaults = tmp.defaults; + stricted = tmp.stricted; + firstRestricted = tmp.firstRestricted; + if (tmp.message) { + message = tmp.message; + } + + previousStrict = strict; + body = parseFunctionSourceElements(); + if (strict && firstRestricted) { + throwUnexpectedToken(firstRestricted, message); + } + if (strict && stricted) { + tolerateUnexpectedToken(stricted, message); + } + strict = previousStrict; + state.allowYield = previousAllowYield; + + return node.finishFunctionExpression(id, params, defaults, body, isGenerator); + } + + // ECMA-262 14.5 Class Definitions + + function parseClassBody() { + var classBody, token, isStatic, hasConstructor = false, body, method, computed, key; + + classBody = new Node(); + + expect('{'); + body = []; + while (!match('}')) { + if (match(';')) { + lex(); + } else { + method = new Node(); + token = lookahead; + isStatic = false; + computed = match('['); + if (match('*')) { + lex(); + } else { + key = parseObjectPropertyKey(); + if (key.name === 'static' && (lookaheadPropertyName() || match('*'))) { + token = lookahead; + isStatic = true; + computed = match('['); + if (match('*')) { + lex(); + } else { + key = parseObjectPropertyKey(); + } + } + } + method = tryParseMethodDefinition(token, key, computed, method); + if (method) { + method['static'] = isStatic; // jscs:ignore requireDotNotation + if (method.kind === 'init') { + method.kind = 'method'; + } + if (!isStatic) { + if (!method.computed && (method.key.name || method.key.value.toString()) === 'constructor') { + if (method.kind !== 'method' || !method.method || method.value.generator) { + throwUnexpectedToken(token, Messages.ConstructorSpecialMethod); + } + if (hasConstructor) { + throwUnexpectedToken(token, Messages.DuplicateConstructor); + } else { + hasConstructor = true; + } + method.kind = 'constructor'; + } + } else { + if (!method.computed && (method.key.name || method.key.value.toString()) === 'prototype') { + throwUnexpectedToken(token, Messages.StaticPrototype); + } + } + method.type = Syntax.MethodDefinition; + delete method.method; + delete method.shorthand; + body.push(method); + } else { + throwUnexpectedToken(lookahead); + } + } + } + lex(); + return classBody.finishClassBody(body); + } + + function parseClassDeclaration(identifierIsOptional) { + var id = null, superClass = null, classNode = new Node(), classBody, previousStrict = strict; + strict = true; + + expectKeyword('class'); + + if (!identifierIsOptional || lookahead.type === Token.Identifier) { + id = parseVariableIdentifier(); + } + + if (matchKeyword('extends')) { + lex(); + superClass = isolateCoverGrammar(parseLeftHandSideExpressionAllowCall); + } + classBody = parseClassBody(); + strict = previousStrict; + + return classNode.finishClassDeclaration(id, superClass, classBody); + } + + function parseClassExpression() { + var id = null, superClass = null, classNode = new Node(), classBody, previousStrict = strict; + strict = true; + + expectKeyword('class'); + + if (lookahead.type === Token.Identifier) { + id = parseVariableIdentifier(); + } + + if (matchKeyword('extends')) { + lex(); + superClass = isolateCoverGrammar(parseLeftHandSideExpressionAllowCall); + } + classBody = parseClassBody(); + strict = previousStrict; + + return classNode.finishClassExpression(id, superClass, classBody); + } + + // ECMA-262 15.2 Modules + + function parseModuleSpecifier() { + var node = new Node(); + + if (lookahead.type !== Token.StringLiteral) { + throwError(Messages.InvalidModuleSpecifier); + } + return node.finishLiteral(lex()); + } + + // ECMA-262 15.2.3 Exports + + function parseExportSpecifier() { + var exported, local, node = new Node(), def; + if (matchKeyword('default')) { + // export {default} from 'something'; + def = new Node(); + lex(); + local = def.finishIdentifier('default'); + } else { + local = parseVariableIdentifier(); + } + if (matchContextualKeyword('as')) { + lex(); + exported = parseNonComputedProperty(); + } + return node.finishExportSpecifier(local, exported); + } + + function parseExportNamedDeclaration(node) { + var declaration = null, + isExportFromIdentifier, + src = null, specifiers = []; + + // non-default export + if (lookahead.type === Token.Keyword) { + // covers: + // export var f = 1; + switch (lookahead.value) { + case 'let': + case 'const': + declaration = parseLexicalDeclaration({inFor: false}); + return node.finishExportNamedDeclaration(declaration, specifiers, null); + case 'var': + case 'class': + case 'function': + declaration = parseStatementListItem(); + return node.finishExportNamedDeclaration(declaration, specifiers, null); + } + } + + expect('{'); + while (!match('}')) { + isExportFromIdentifier = isExportFromIdentifier || matchKeyword('default'); + specifiers.push(parseExportSpecifier()); + if (!match('}')) { + expect(','); + if (match('}')) { + break; + } + } + } + expect('}'); + + if (matchContextualKeyword('from')) { + // covering: + // export {default} from 'foo'; + // export {foo} from 'foo'; + lex(); + src = parseModuleSpecifier(); + consumeSemicolon(); + } else if (isExportFromIdentifier) { + // covering: + // export {default}; // missing fromClause + throwError(lookahead.value ? + Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); + } else { + // cover + // export {foo}; + consumeSemicolon(); + } + return node.finishExportNamedDeclaration(declaration, specifiers, src); + } + + function parseExportDefaultDeclaration(node) { + var declaration = null, + expression = null; + + // covers: + // export default ... + expectKeyword('default'); + + if (matchKeyword('function')) { + // covers: + // export default function foo () {} + // export default function () {} + declaration = parseFunctionDeclaration(new Node(), true); + return node.finishExportDefaultDeclaration(declaration); + } + if (matchKeyword('class')) { + declaration = parseClassDeclaration(true); + return node.finishExportDefaultDeclaration(declaration); + } + + if (matchContextualKeyword('from')) { + throwError(Messages.UnexpectedToken, lookahead.value); + } + + // covers: + // export default {}; + // export default []; + // export default (1 + 2); + if (match('{')) { + expression = parseObjectInitializer(); + } else if (match('[')) { + expression = parseArrayInitializer(); + } else { + expression = parseAssignmentExpression(); + } + consumeSemicolon(); + return node.finishExportDefaultDeclaration(expression); + } + + function parseExportAllDeclaration(node) { + var src; + + // covers: + // export * from 'foo'; + expect('*'); + if (!matchContextualKeyword('from')) { + throwError(lookahead.value ? + Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); + } + lex(); + src = parseModuleSpecifier(); + consumeSemicolon(); + + return node.finishExportAllDeclaration(src); + } + + function parseExportDeclaration() { + var node = new Node(); + if (state.inFunctionBody) { + throwError(Messages.IllegalExportDeclaration); + } + + expectKeyword('export'); + + if (matchKeyword('default')) { + return parseExportDefaultDeclaration(node); + } + if (match('*')) { + return parseExportAllDeclaration(node); + } + return parseExportNamedDeclaration(node); + } + + // ECMA-262 15.2.2 Imports + + function parseImportSpecifier() { + // import {} ...; + var local, imported, node = new Node(); + + imported = parseNonComputedProperty(); + if (matchContextualKeyword('as')) { + lex(); + local = parseVariableIdentifier(); + } + + return node.finishImportSpecifier(local, imported); + } + + function parseNamedImports() { + var specifiers = []; + // {foo, bar as bas} + expect('{'); + while (!match('}')) { + specifiers.push(parseImportSpecifier()); + if (!match('}')) { + expect(','); + if (match('}')) { + break; + } + } + } + expect('}'); + return specifiers; + } + + function parseImportDefaultSpecifier() { + // import ...; + var local, node = new Node(); + + local = parseNonComputedProperty(); + + return node.finishImportDefaultSpecifier(local); + } + + function parseImportNamespaceSpecifier() { + // import <* as foo> ...; + var local, node = new Node(); + + expect('*'); + if (!matchContextualKeyword('as')) { + throwError(Messages.NoAsAfterImportNamespace); + } + lex(); + local = parseNonComputedProperty(); + + return node.finishImportNamespaceSpecifier(local); + } + + function parseImportDeclaration() { + var specifiers = [], src, node = new Node(); + + if (state.inFunctionBody) { + throwError(Messages.IllegalImportDeclaration); + } + + expectKeyword('import'); + + if (lookahead.type === Token.StringLiteral) { + // import 'foo'; + src = parseModuleSpecifier(); + } else { + + if (match('{')) { + // import {bar} + specifiers = specifiers.concat(parseNamedImports()); + } else if (match('*')) { + // import * as foo + specifiers.push(parseImportNamespaceSpecifier()); + } else if (isIdentifierName(lookahead) && !matchKeyword('default')) { + // import foo + specifiers.push(parseImportDefaultSpecifier()); + if (match(',')) { + lex(); + if (match('*')) { + // import foo, * as foo + specifiers.push(parseImportNamespaceSpecifier()); + } else if (match('{')) { + // import foo, {bar} + specifiers = specifiers.concat(parseNamedImports()); + } else { + throwUnexpectedToken(lookahead); + } + } + } else { + throwUnexpectedToken(lex()); + } + + if (!matchContextualKeyword('from')) { + throwError(lookahead.value ? + Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value); + } + lex(); + src = parseModuleSpecifier(); + } + + consumeSemicolon(); + return node.finishImportDeclaration(specifiers, src); + } + + // ECMA-262 15.1 Scripts + + function parseScriptBody() { + var statement, body = [], token, directive, firstRestricted; + + while (startIndex < length) { + token = lookahead; + if (token.type !== Token.StringLiteral) { + break; + } + + statement = parseStatementListItem(); + body.push(statement); + if (statement.expression.type !== Syntax.Literal) { + // this is not directive + break; + } + directive = source.slice(token.start + 1, token.end - 1); + if (directive === 'use strict') { + strict = true; + if (firstRestricted) { + tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral); + } + } else { + if (!firstRestricted && token.octal) { + firstRestricted = token; + } + } + } + + while (startIndex < length) { + statement = parseStatementListItem(); + /* istanbul ignore if */ + if (typeof statement === 'undefined') { + break; + } + body.push(statement); + } + return body; + } + + function parseProgram() { + var body, node; + + peek(); + node = new Node(); + + body = parseScriptBody(); + return node.finishProgram(body, state.sourceType); + } + + function filterTokenLocation() { + var i, entry, token, tokens = []; + + for (i = 0; i < extra.tokens.length; ++i) { + entry = extra.tokens[i]; + token = { + type: entry.type, + value: entry.value + }; + if (entry.regex) { + token.regex = { + pattern: entry.regex.pattern, + flags: entry.regex.flags + }; + } + if (extra.range) { + token.range = entry.range; + } + if (extra.loc) { + token.loc = entry.loc; + } + tokens.push(token); + } + + extra.tokens = tokens; + } + + function tokenize(code, options, delegate) { + var toString, + tokens; + + toString = String; + if (typeof code !== 'string' && !(code instanceof String)) { + code = toString(code); + } + + source = code; + index = 0; + lineNumber = (source.length > 0) ? 1 : 0; + lineStart = 0; + startIndex = index; + startLineNumber = lineNumber; + startLineStart = lineStart; + length = source.length; + lookahead = null; + state = { + allowIn: true, + allowYield: true, + labelSet: {}, + inFunctionBody: false, + inIteration: false, + inSwitch: false, + lastCommentStart: -1, + curlyStack: [] + }; + + extra = {}; + + // Options matching. + options = options || {}; + + // Of course we collect tokens here. + options.tokens = true; + extra.tokens = []; + extra.tokenValues = []; + extra.tokenize = true; + extra.delegate = delegate; + + // The following two fields are necessary to compute the Regex tokens. + extra.openParenToken = -1; + extra.openCurlyToken = -1; + + extra.range = (typeof options.range === 'boolean') && options.range; + extra.loc = (typeof options.loc === 'boolean') && options.loc; + + if (typeof options.comment === 'boolean' && options.comment) { + extra.comments = []; + } + if (typeof options.tolerant === 'boolean' && options.tolerant) { + extra.errors = []; + } + + try { + peek(); + if (lookahead.type === Token.EOF) { + return extra.tokens; + } + + lex(); + while (lookahead.type !== Token.EOF) { + try { + lex(); + } catch (lexError) { + if (extra.errors) { + recordError(lexError); + // We have to break on the first error + // to avoid infinite loops. + break; + } else { + throw lexError; + } + } + } + + tokens = extra.tokens; + if (typeof extra.errors !== 'undefined') { + tokens.errors = extra.errors; + } + } catch (e) { + throw e; + } finally { + extra = {}; + } + return tokens; + } + + function parse(code, options) { + var program, toString; + + toString = String; + if (typeof code !== 'string' && !(code instanceof String)) { + code = toString(code); + } + + source = code; + index = 0; + lineNumber = (source.length > 0) ? 1 : 0; + lineStart = 0; + startIndex = index; + startLineNumber = lineNumber; + startLineStart = lineStart; + length = source.length; + lookahead = null; + state = { + allowIn: true, + allowYield: true, + labelSet: {}, + inFunctionBody: false, + inIteration: false, + inSwitch: false, + lastCommentStart: -1, + curlyStack: [], + sourceType: 'script' + }; + strict = false; + + extra = {}; + if (typeof options !== 'undefined') { + extra.range = (typeof options.range === 'boolean') && options.range; + extra.loc = (typeof options.loc === 'boolean') && options.loc; + extra.attachComment = (typeof options.attachComment === 'boolean') && options.attachComment; + + if (extra.loc && options.source !== null && options.source !== undefined) { + extra.source = toString(options.source); + } + + if (typeof options.tokens === 'boolean' && options.tokens) { + extra.tokens = []; + } + if (typeof options.comment === 'boolean' && options.comment) { + extra.comments = []; + } + if (typeof options.tolerant === 'boolean' && options.tolerant) { + extra.errors = []; + } + if (extra.attachComment) { + extra.range = true; + extra.comments = []; + extra.bottomRightStack = []; + extra.trailingComments = []; + extra.leadingComments = []; + } + if (options.sourceType === 'module') { + // very restrictive condition for now + state.sourceType = options.sourceType; + strict = true; + } + } + + try { + program = parseProgram(); + if (typeof extra.comments !== 'undefined') { + program.comments = extra.comments; + } + if (typeof extra.tokens !== 'undefined') { + filterTokenLocation(); + program.tokens = extra.tokens; + } + if (typeof extra.errors !== 'undefined') { + program.errors = extra.errors; + } + } catch (e) { + throw e; + } finally { + extra = {}; + } + + return program; + } + + // Sync with *.json manifests. + exports.version = '2.7.3'; + + exports.tokenize = tokenize; + + exports.parse = parse; + + // Deep copy. + /* istanbul ignore next */ + exports.Syntax = (function () { + var name, types = {}; + + if (typeof Object.create === 'function') { + types = Object.create(null); + } + + for (name in Syntax) { + if (Syntax.hasOwnProperty(name)) { + types[name] = Syntax[name]; + } + } + + if (typeof Object.freeze === 'function') { + Object.freeze(types); + } + + return types; + }()); + +})); +/* vim: set sw=4 ts=4 et tw=80 : */ +/*global define, Reflect */ + +/* + * xpcshell has a smaller stack on linux and windows (1MB vs 9MB on mac), + * and the recursive nature of esprima can cause it to overflow pretty + * quickly. So favor it built in Reflect parser: + * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API + */ +define('esprimaAdapter', ['./esprima', 'env'], function (esprima, env) { + if (env.get() === 'xpconnect' && typeof Reflect !== 'undefined') { + return Reflect; + } else { + return esprima; + } +}); +(function webpackUniversalModuleDefinition(root, factory) { +var exports, module; + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define('source-map', [], factory); + else if(typeof exports === 'object') + exports["sourceMap"] = factory(); + else + root["sourceMap"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + /* + * Copyright 2009-2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE.txt or: + * http://opensource.org/licenses/BSD-3-Clause + */ + exports.SourceMapGenerator = __webpack_require__(1).SourceMapGenerator; + exports.SourceMapConsumer = __webpack_require__(7).SourceMapConsumer; + exports.SourceNode = __webpack_require__(10).SourceNode; + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + /* -*- Mode: js; js-indent-level: 2; -*- */ + /* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + + var base64VLQ = __webpack_require__(2); + var util = __webpack_require__(4); + var ArraySet = __webpack_require__(5).ArraySet; + var MappingList = __webpack_require__(6).MappingList; + + /** + * An instance of the SourceMapGenerator represents a source map which is + * being built incrementally. You may pass an object with the following + * properties: + * + * - file: The filename of the generated source. + * - sourceRoot: A root for all relative URLs in this source map. + */ + function SourceMapGenerator(aArgs) { + if (!aArgs) { + aArgs = {}; + } + this._file = util.getArg(aArgs, 'file', null); + this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); + this._skipValidation = util.getArg(aArgs, 'skipValidation', false); + this._sources = new ArraySet(); + this._names = new ArraySet(); + this._mappings = new MappingList(); + this._sourcesContents = null; + } + + SourceMapGenerator.prototype._version = 3; + + /** + * Creates a new SourceMapGenerator based on a SourceMapConsumer + * + * @param aSourceMapConsumer The SourceMap. + */ + SourceMapGenerator.fromSourceMap = + function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { + var sourceRoot = aSourceMapConsumer.sourceRoot; + var generator = new SourceMapGenerator({ + file: aSourceMapConsumer.file, + sourceRoot: sourceRoot + }); + aSourceMapConsumer.eachMapping(function (mapping) { + var newMapping = { + generated: { + line: mapping.generatedLine, + column: mapping.generatedColumn + } + }; + + if (mapping.source != null) { + newMapping.source = mapping.source; + if (sourceRoot != null) { + newMapping.source = util.relative(sourceRoot, newMapping.source); + } + + newMapping.original = { + line: mapping.originalLine, + column: mapping.originalColumn + }; + + if (mapping.name != null) { + newMapping.name = mapping.name; + } + } + + generator.addMapping(newMapping); + }); + aSourceMapConsumer.sources.forEach(function (sourceFile) { + var content = aSourceMapConsumer.sourceContentFor(sourceFile); + if (content != null) { + generator.setSourceContent(sourceFile, content); + } + }); + return generator; + }; + + /** + * Add a single mapping from original source line and column to the generated + * source's line and column for this source map being created. The mapping + * object should have the following properties: + * + * - generated: An object with the generated line and column positions. + * - original: An object with the original line and column positions. + * - source: The original source file (relative to the sourceRoot). + * - name: An optional original token name for this mapping. + */ + SourceMapGenerator.prototype.addMapping = + function SourceMapGenerator_addMapping(aArgs) { + var generated = util.getArg(aArgs, 'generated'); + var original = util.getArg(aArgs, 'original', null); + var source = util.getArg(aArgs, 'source', null); + var name = util.getArg(aArgs, 'name', null); + + if (!this._skipValidation) { + this._validateMapping(generated, original, source, name); + } + + if (source != null) { + source = String(source); + if (!this._sources.has(source)) { + this._sources.add(source); + } + } + + if (name != null) { + name = String(name); + if (!this._names.has(name)) { + this._names.add(name); + } + } + + this._mappings.add({ + generatedLine: generated.line, + generatedColumn: generated.column, + originalLine: original != null && original.line, + originalColumn: original != null && original.column, + source: source, + name: name + }); + }; + + /** + * Set the source content for a source file. + */ + SourceMapGenerator.prototype.setSourceContent = + function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { + var source = aSourceFile; + if (this._sourceRoot != null) { + source = util.relative(this._sourceRoot, source); + } + + if (aSourceContent != null) { + // Add the source content to the _sourcesContents map. + // Create a new _sourcesContents map if the property is null. + if (!this._sourcesContents) { + this._sourcesContents = Object.create(null); + } + this._sourcesContents[util.toSetString(source)] = aSourceContent; + } else if (this._sourcesContents) { + // Remove the source file from the _sourcesContents map. + // If the _sourcesContents map is empty, set the property to null. + delete this._sourcesContents[util.toSetString(source)]; + if (Object.keys(this._sourcesContents).length === 0) { + this._sourcesContents = null; + } + } + }; + + /** + * Applies the mappings of a sub-source-map for a specific source file to the + * source map being generated. Each mapping to the supplied source file is + * rewritten using the supplied source map. Note: The resolution for the + * resulting mappings is the minimium of this map and the supplied map. + * + * @param aSourceMapConsumer The source map to be applied. + * @param aSourceFile Optional. The filename of the source file. + * If omitted, SourceMapConsumer's file property will be used. + * @param aSourceMapPath Optional. The dirname of the path to the source map + * to be applied. If relative, it is relative to the SourceMapConsumer. + * This parameter is needed when the two source maps aren't in the same + * directory, and the source map to be applied contains relative source + * paths. If so, those relative source paths need to be rewritten + * relative to the SourceMapGenerator. + */ + SourceMapGenerator.prototype.applySourceMap = + function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) { + var sourceFile = aSourceFile; + // If aSourceFile is omitted, we will use the file property of the SourceMap + if (aSourceFile == null) { + if (aSourceMapConsumer.file == null) { + throw new Error( + 'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' + + 'or the source map\'s "file" property. Both were omitted.' + ); + } + sourceFile = aSourceMapConsumer.file; + } + var sourceRoot = this._sourceRoot; + // Make "sourceFile" relative if an absolute Url is passed. + if (sourceRoot != null) { + sourceFile = util.relative(sourceRoot, sourceFile); + } + // Applying the SourceMap can add and remove items from the sources and + // the names array. + var newSources = new ArraySet(); + var newNames = new ArraySet(); + + // Find mappings for the "sourceFile" + this._mappings.unsortedForEach(function (mapping) { + if (mapping.source === sourceFile && mapping.originalLine != null) { + // Check if it can be mapped by the source map, then update the mapping. + var original = aSourceMapConsumer.originalPositionFor({ + line: mapping.originalLine, + column: mapping.originalColumn + }); + if (original.source != null) { + // Copy mapping + mapping.source = original.source; + if (aSourceMapPath != null) { + mapping.source = util.join(aSourceMapPath, mapping.source) + } + if (sourceRoot != null) { + mapping.source = util.relative(sourceRoot, mapping.source); + } + mapping.originalLine = original.line; + mapping.originalColumn = original.column; + if (original.name != null) { + mapping.name = original.name; + } + } + } + + var source = mapping.source; + if (source != null && !newSources.has(source)) { + newSources.add(source); + } + + var name = mapping.name; + if (name != null && !newNames.has(name)) { + newNames.add(name); + } + + }, this); + this._sources = newSources; + this._names = newNames; + + // Copy sourcesContents of applied map. + aSourceMapConsumer.sources.forEach(function (sourceFile) { + var content = aSourceMapConsumer.sourceContentFor(sourceFile); + if (content != null) { + if (aSourceMapPath != null) { + sourceFile = util.join(aSourceMapPath, sourceFile); + } + if (sourceRoot != null) { + sourceFile = util.relative(sourceRoot, sourceFile); + } + this.setSourceContent(sourceFile, content); + } + }, this); + }; + + /** + * A mapping can have one of the three levels of data: + * + * 1. Just the generated position. + * 2. The Generated position, original position, and original source. + * 3. Generated and original position, original source, as well as a name + * token. + * + * To maintain consistency, we validate that any new mapping being added falls + * in to one of these categories. + */ + SourceMapGenerator.prototype._validateMapping = + function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, + aName) { + if (aGenerated && 'line' in aGenerated && 'column' in aGenerated + && aGenerated.line > 0 && aGenerated.column >= 0 + && !aOriginal && !aSource && !aName) { + // Case 1. + return; + } + else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated + && aOriginal && 'line' in aOriginal && 'column' in aOriginal + && aGenerated.line > 0 && aGenerated.column >= 0 + && aOriginal.line > 0 && aOriginal.column >= 0 + && aSource) { + // Cases 2 and 3. + return; + } + else { + throw new Error('Invalid mapping: ' + JSON.stringify({ + generated: aGenerated, + source: aSource, + original: aOriginal, + name: aName + })); + } + }; + + /** + * Serialize the accumulated mappings in to the stream of base 64 VLQs + * specified by the source map format. + */ + SourceMapGenerator.prototype._serializeMappings = + function SourceMapGenerator_serializeMappings() { + var previousGeneratedColumn = 0; + var previousGeneratedLine = 1; + var previousOriginalColumn = 0; + var previousOriginalLine = 0; + var previousName = 0; + var previousSource = 0; + var result = ''; + var next; + var mapping; + var nameIdx; + var sourceIdx; + + var mappings = this._mappings.toArray(); + for (var i = 0, len = mappings.length; i < len; i++) { + mapping = mappings[i]; + next = '' + + if (mapping.generatedLine !== previousGeneratedLine) { + previousGeneratedColumn = 0; + while (mapping.generatedLine !== previousGeneratedLine) { + next += ';'; + previousGeneratedLine++; + } + } + else { + if (i > 0) { + if (!util.compareByGeneratedPositionsInflated(mapping, mappings[i - 1])) { + continue; + } + next += ','; + } + } + + next += base64VLQ.encode(mapping.generatedColumn + - previousGeneratedColumn); + previousGeneratedColumn = mapping.generatedColumn; + + if (mapping.source != null) { + sourceIdx = this._sources.indexOf(mapping.source); + next += base64VLQ.encode(sourceIdx - previousSource); + previousSource = sourceIdx; + + // lines are stored 0-based in SourceMap spec version 3 + next += base64VLQ.encode(mapping.originalLine - 1 + - previousOriginalLine); + previousOriginalLine = mapping.originalLine - 1; + + next += base64VLQ.encode(mapping.originalColumn + - previousOriginalColumn); + previousOriginalColumn = mapping.originalColumn; + + if (mapping.name != null) { + nameIdx = this._names.indexOf(mapping.name); + next += base64VLQ.encode(nameIdx - previousName); + previousName = nameIdx; + } + } + + result += next; + } + + return result; + }; + + SourceMapGenerator.prototype._generateSourcesContent = + function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { + return aSources.map(function (source) { + if (!this._sourcesContents) { + return null; + } + if (aSourceRoot != null) { + source = util.relative(aSourceRoot, source); + } + var key = util.toSetString(source); + return Object.prototype.hasOwnProperty.call(this._sourcesContents, key) + ? this._sourcesContents[key] + : null; + }, this); + }; + + /** + * Externalize the source map. + */ + SourceMapGenerator.prototype.toJSON = + function SourceMapGenerator_toJSON() { + var map = { + version: this._version, + sources: this._sources.toArray(), + names: this._names.toArray(), + mappings: this._serializeMappings() + }; + if (this._file != null) { + map.file = this._file; + } + if (this._sourceRoot != null) { + map.sourceRoot = this._sourceRoot; + } + if (this._sourcesContents) { + map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); + } + + return map; + }; + + /** + * Render the source map being generated to a string. + */ + SourceMapGenerator.prototype.toString = + function SourceMapGenerator_toString() { + return JSON.stringify(this.toJSON()); + }; + + exports.SourceMapGenerator = SourceMapGenerator; + + +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { + + /* -*- Mode: js; js-indent-level: 2; -*- */ + /* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + * + * Based on the Base 64 VLQ implementation in Closure Compiler: + * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java + * + * Copyright 2011 The Closure Compiler Authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + var base64 = __webpack_require__(3); + + // A single base 64 digit can contain 6 bits of data. For the base 64 variable + // length quantities we use in the source map spec, the first bit is the sign, + // the next four bits are the actual value, and the 6th bit is the + // continuation bit. The continuation bit tells us whether there are more + // digits in this value following this digit. + // + // Continuation + // | Sign + // | | + // V V + // 101011 + + var VLQ_BASE_SHIFT = 5; + + // binary: 100000 + var VLQ_BASE = 1 << VLQ_BASE_SHIFT; + + // binary: 011111 + var VLQ_BASE_MASK = VLQ_BASE - 1; + + // binary: 100000 + var VLQ_CONTINUATION_BIT = VLQ_BASE; + + /** + * Converts from a two-complement value to a value where the sign bit is + * placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + */ + function toVLQSigned(aValue) { + return aValue < 0 + ? ((-aValue) << 1) + 1 + : (aValue << 1) + 0; + } + + /** + * Converts to a two-complement value from a value where the sign bit is + * placed in the least significant bit. For example, as decimals: + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + */ + function fromVLQSigned(aValue) { + var isNegative = (aValue & 1) === 1; + var shifted = aValue >> 1; + return isNegative + ? -shifted + : shifted; + } + + /** + * Returns the base 64 VLQ encoded value. + */ + exports.encode = function base64VLQ_encode(aValue) { + var encoded = ""; + var digit; + + var vlq = toVLQSigned(aValue); + + do { + digit = vlq & VLQ_BASE_MASK; + vlq >>>= VLQ_BASE_SHIFT; + if (vlq > 0) { + // There are still more digits in this value, so we must make sure the + // continuation bit is marked. + digit |= VLQ_CONTINUATION_BIT; + } + encoded += base64.encode(digit); + } while (vlq > 0); + + return encoded; + }; + + /** + * Decodes the next base 64 VLQ value from the given string and returns the + * value and the rest of the string via the out parameter. + */ + exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { + var strLen = aStr.length; + var result = 0; + var shift = 0; + var continuation, digit; + + do { + if (aIndex >= strLen) { + throw new Error("Expected more digits in base 64 VLQ value."); + } + + digit = base64.decode(aStr.charCodeAt(aIndex++)); + if (digit === -1) { + throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1)); + } + + continuation = !!(digit & VLQ_CONTINUATION_BIT); + digit &= VLQ_BASE_MASK; + result = result + (digit << shift); + shift += VLQ_BASE_SHIFT; + } while (continuation); + + aOutParam.value = fromVLQSigned(result); + aOutParam.rest = aIndex; + }; + + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + + /* -*- Mode: js; js-indent-level: 2; -*- */ + /* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + + var intToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); + + /** + * Encode an integer in the range of 0 to 63 to a single base 64 digit. + */ + exports.encode = function (number) { + if (0 <= number && number < intToCharMap.length) { + return intToCharMap[number]; + } + throw new TypeError("Must be between 0 and 63: " + number); + }; + + /** + * Decode a single base 64 character code digit to an integer. Returns -1 on + * failure. + */ + exports.decode = function (charCode) { + var bigA = 65; // 'A' + var bigZ = 90; // 'Z' + + var littleA = 97; // 'a' + var littleZ = 122; // 'z' + + var zero = 48; // '0' + var nine = 57; // '9' + + var plus = 43; // '+' + var slash = 47; // '/' + + var littleOffset = 26; + var numberOffset = 52; + + // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ + if (bigA <= charCode && charCode <= bigZ) { + return (charCode - bigA); + } + + // 26 - 51: abcdefghijklmnopqrstuvwxyz + if (littleA <= charCode && charCode <= littleZ) { + return (charCode - littleA + littleOffset); + } + + // 52 - 61: 0123456789 + if (zero <= charCode && charCode <= nine) { + return (charCode - zero + numberOffset); + } + + // 62: + + if (charCode == plus) { + return 62; + } + + // 63: / + if (charCode == slash) { + return 63; + } + + // Invalid base64 digit. + return -1; + }; + + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + /* -*- Mode: js; js-indent-level: 2; -*- */ + /* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + + /** + * This is a helper function for getting values from parameter/options + * objects. + * + * @param args The object we are extracting values from + * @param name The name of the property we are getting. + * @param defaultValue An optional value to return if the property is missing + * from the object. If this is not specified and the property is missing, an + * error will be thrown. + */ + function getArg(aArgs, aName, aDefaultValue) { + if (aName in aArgs) { + return aArgs[aName]; + } else if (arguments.length === 3) { + return aDefaultValue; + } else { + throw new Error('"' + aName + '" is a required argument.'); + } + } + exports.getArg = getArg; + + var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/; + var dataUrlRegexp = /^data:.+\,.+$/; + + function urlParse(aUrl) { + var match = aUrl.match(urlRegexp); + if (!match) { + return null; + } + return { + scheme: match[1], + auth: match[2], + host: match[3], + port: match[4], + path: match[5] + }; + } + exports.urlParse = urlParse; + + function urlGenerate(aParsedUrl) { + var url = ''; + if (aParsedUrl.scheme) { + url += aParsedUrl.scheme + ':'; + } + url += '//'; + if (aParsedUrl.auth) { + url += aParsedUrl.auth + '@'; + } + if (aParsedUrl.host) { + url += aParsedUrl.host; + } + if (aParsedUrl.port) { + url += ":" + aParsedUrl.port + } + if (aParsedUrl.path) { + url += aParsedUrl.path; + } + return url; + } + exports.urlGenerate = urlGenerate; + + /** + * Normalizes a path, or the path portion of a URL: + * + * - Replaces consequtive slashes with one slash. + * - Removes unnecessary '.' parts. + * - Removes unnecessary '/..' parts. + * + * Based on code in the Node.js 'path' core module. + * + * @param aPath The path or url to normalize. + */ + function normalize(aPath) { + var path = aPath; + var url = urlParse(aPath); + if (url) { + if (!url.path) { + return aPath; + } + path = url.path; + } + var isAbsolute = exports.isAbsolute(path); + + var parts = path.split(/\/+/); + for (var part, up = 0, i = parts.length - 1; i >= 0; i--) { + part = parts[i]; + if (part === '.') { + parts.splice(i, 1); + } else if (part === '..') { + up++; + } else if (up > 0) { + if (part === '') { + // The first part is blank if the path is absolute. Trying to go + // above the root is a no-op. Therefore we can remove all '..' parts + // directly after the root. + parts.splice(i + 1, up); + up = 0; + } else { + parts.splice(i, 2); + up--; + } + } + } + path = parts.join('/'); + + if (path === '') { + path = isAbsolute ? '/' : '.'; + } + + if (url) { + url.path = path; + return urlGenerate(url); + } + return path; + } + exports.normalize = normalize; + + /** + * Joins two paths/URLs. + * + * @param aRoot The root path or URL. + * @param aPath The path or URL to be joined with the root. + * + * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a + * scheme-relative URL: Then the scheme of aRoot, if any, is prepended + * first. + * - Otherwise aPath is a path. If aRoot is a URL, then its path portion + * is updated with the result and aRoot is returned. Otherwise the result + * is returned. + * - If aPath is absolute, the result is aPath. + * - Otherwise the two paths are joined with a slash. + * - Joining for example 'http://' and 'www.example.com' is also supported. + */ + function join(aRoot, aPath) { + if (aRoot === "") { + aRoot = "."; + } + if (aPath === "") { + aPath = "."; + } + var aPathUrl = urlParse(aPath); + var aRootUrl = urlParse(aRoot); + if (aRootUrl) { + aRoot = aRootUrl.path || '/'; + } + + // `join(foo, '//www.example.org')` + if (aPathUrl && !aPathUrl.scheme) { + if (aRootUrl) { + aPathUrl.scheme = aRootUrl.scheme; + } + return urlGenerate(aPathUrl); + } + + if (aPathUrl || aPath.match(dataUrlRegexp)) { + return aPath; + } + + // `join('http://', 'www.example.com')` + if (aRootUrl && !aRootUrl.host && !aRootUrl.path) { + aRootUrl.host = aPath; + return urlGenerate(aRootUrl); + } + + var joined = aPath.charAt(0) === '/' + ? aPath + : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath); + + if (aRootUrl) { + aRootUrl.path = joined; + return urlGenerate(aRootUrl); + } + return joined; + } + exports.join = join; + + exports.isAbsolute = function (aPath) { + return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp); + }; + + /** + * Make a path relative to a URL or another path. + * + * @param aRoot The root path or URL. + * @param aPath The path or URL to be made relative to aRoot. + */ + function relative(aRoot, aPath) { + if (aRoot === "") { + aRoot = "."; + } + + aRoot = aRoot.replace(/\/$/, ''); + + // It is possible for the path to be above the root. In this case, simply + // checking whether the root is a prefix of the path won't work. Instead, we + // need to remove components from the root one by one, until either we find + // a prefix that fits, or we run out of components to remove. + var level = 0; + while (aPath.indexOf(aRoot + '/') !== 0) { + var index = aRoot.lastIndexOf("/"); + if (index < 0) { + return aPath; + } + + // If the only part of the root that is left is the scheme (i.e. http://, + // file:///, etc.), one or more slashes (/), or simply nothing at all, we + // have exhausted all components, so the path is not relative to the root. + aRoot = aRoot.slice(0, index); + if (aRoot.match(/^([^\/]+:\/)?\/*$/)) { + return aPath; + } + + ++level; + } + + // Make sure we add a "../" for each component we removed from the root. + return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1); + } + exports.relative = relative; + + var supportsNullProto = (function () { + var obj = Object.create(null); + return !('__proto__' in obj); + }()); + + function identity (s) { + return s; + } + + /** + * Because behavior goes wacky when you set `__proto__` on objects, we + * have to prefix all the strings in our set with an arbitrary character. + * + * See https://github.com/mozilla/source-map/pull/31 and + * https://github.com/mozilla/source-map/issues/30 + * + * @param String aStr + */ + function toSetString(aStr) { + if (isProtoString(aStr)) { + return '$' + aStr; + } + + return aStr; + } + exports.toSetString = supportsNullProto ? identity : toSetString; + + function fromSetString(aStr) { + if (isProtoString(aStr)) { + return aStr.slice(1); + } + + return aStr; + } + exports.fromSetString = supportsNullProto ? identity : fromSetString; + + function isProtoString(s) { + if (!s) { + return false; + } + + var length = s.length; + + if (length < 9 /* "__proto__".length */) { + return false; + } + + if (s.charCodeAt(length - 1) !== 95 /* '_' */ || + s.charCodeAt(length - 2) !== 95 /* '_' */ || + s.charCodeAt(length - 3) !== 111 /* 'o' */ || + s.charCodeAt(length - 4) !== 116 /* 't' */ || + s.charCodeAt(length - 5) !== 111 /* 'o' */ || + s.charCodeAt(length - 6) !== 114 /* 'r' */ || + s.charCodeAt(length - 7) !== 112 /* 'p' */ || + s.charCodeAt(length - 8) !== 95 /* '_' */ || + s.charCodeAt(length - 9) !== 95 /* '_' */) { + return false; + } + + for (var i = length - 10; i >= 0; i--) { + if (s.charCodeAt(i) !== 36 /* '$' */) { + return false; + } + } + + return true; + } + + /** + * Comparator between two mappings where the original positions are compared. + * + * Optionally pass in `true` as `onlyCompareGenerated` to consider two + * mappings with the same original source/line/column, but different generated + * line and column the same. Useful when searching for a mapping with a + * stubbed out mapping. + */ + function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { + var cmp = mappingA.source - mappingB.source; + if (cmp !== 0) { + return cmp; + } + + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp !== 0) { + return cmp; + } + + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp !== 0 || onlyCompareOriginal) { + return cmp; + } + + cmp = mappingA.generatedColumn - mappingB.generatedColumn; + if (cmp !== 0) { + return cmp; + } + + cmp = mappingA.generatedLine - mappingB.generatedLine; + if (cmp !== 0) { + return cmp; + } + + return mappingA.name - mappingB.name; + } + exports.compareByOriginalPositions = compareByOriginalPositions; + + /** + * Comparator between two mappings with deflated source and name indices where + * the generated positions are compared. + * + * Optionally pass in `true` as `onlyCompareGenerated` to consider two + * mappings with the same generated line and column, but different + * source/name/original line and column the same. Useful when searching for a + * mapping with a stubbed out mapping. + */ + function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) { + var cmp = mappingA.generatedLine - mappingB.generatedLine; + if (cmp !== 0) { + return cmp; + } + + cmp = mappingA.generatedColumn - mappingB.generatedColumn; + if (cmp !== 0 || onlyCompareGenerated) { + return cmp; + } + + cmp = mappingA.source - mappingB.source; + if (cmp !== 0) { + return cmp; + } + + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp !== 0) { + return cmp; + } + + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp !== 0) { + return cmp; + } + + return mappingA.name - mappingB.name; + } + exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated; + + function strcmp(aStr1, aStr2) { + if (aStr1 === aStr2) { + return 0; + } + + if (aStr1 > aStr2) { + return 1; + } + + return -1; + } + + /** + * Comparator between two mappings with inflated source and name strings where + * the generated positions are compared. + */ + function compareByGeneratedPositionsInflated(mappingA, mappingB) { + var cmp = mappingA.generatedLine - mappingB.generatedLine; + if (cmp !== 0) { + return cmp; + } + + cmp = mappingA.generatedColumn - mappingB.generatedColumn; + if (cmp !== 0) { + return cmp; + } + + cmp = strcmp(mappingA.source, mappingB.source); + if (cmp !== 0) { + return cmp; + } + + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp !== 0) { + return cmp; + } + + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp !== 0) { + return cmp; + } + + return strcmp(mappingA.name, mappingB.name); + } + exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated; + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + /* -*- Mode: js; js-indent-level: 2; -*- */ + /* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + + var util = __webpack_require__(4); + var has = Object.prototype.hasOwnProperty; + + /** + * A data structure which is a combination of an array and a set. Adding a new + * member is O(1), testing for membership is O(1), and finding the index of an + * element is O(1). Removing elements from the set is not supported. Only + * strings are supported for membership. + */ + function ArraySet() { + this._array = []; + this._set = Object.create(null); + } + + /** + * Static method for creating ArraySet instances from an existing array. + */ + ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { + var set = new ArraySet(); + for (var i = 0, len = aArray.length; i < len; i++) { + set.add(aArray[i], aAllowDuplicates); + } + return set; + }; + + /** + * Return how many unique items are in this ArraySet. If duplicates have been + * added, than those do not count towards the size. + * + * @returns Number + */ + ArraySet.prototype.size = function ArraySet_size() { + return Object.getOwnPropertyNames(this._set).length; + }; + + /** + * Add the given string to this set. + * + * @param String aStr + */ + ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { + var sStr = util.toSetString(aStr); + var isDuplicate = has.call(this._set, sStr); + var idx = this._array.length; + if (!isDuplicate || aAllowDuplicates) { + this._array.push(aStr); + } + if (!isDuplicate) { + this._set[sStr] = idx; + } + }; + + /** + * Is the given string a member of this set? + * + * @param String aStr + */ + ArraySet.prototype.has = function ArraySet_has(aStr) { + var sStr = util.toSetString(aStr); + return has.call(this._set, sStr); + }; + + /** + * What is the index of the given string in the array? + * + * @param String aStr + */ + ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { + var sStr = util.toSetString(aStr); + if (has.call(this._set, sStr)) { + return this._set[sStr]; + } + throw new Error('"' + aStr + '" is not in the set.'); + }; + + /** + * What is the element at the given index? + * + * @param Number aIdx + */ + ArraySet.prototype.at = function ArraySet_at(aIdx) { + if (aIdx >= 0 && aIdx < this._array.length) { + return this._array[aIdx]; + } + throw new Error('No element indexed by ' + aIdx); + }; + + /** + * Returns the array representation of this set (which has the proper indices + * indicated by indexOf). Note that this is a copy of the internal array used + * for storing the members so that no one can mess with internal state. + */ + ArraySet.prototype.toArray = function ArraySet_toArray() { + return this._array.slice(); + }; + + exports.ArraySet = ArraySet; + + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + /* -*- Mode: js; js-indent-level: 2; -*- */ + /* + * Copyright 2014 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + + var util = __webpack_require__(4); + + /** + * Determine whether mappingB is after mappingA with respect to generated + * position. + */ + function generatedPositionAfter(mappingA, mappingB) { + // Optimized for most common case + var lineA = mappingA.generatedLine; + var lineB = mappingB.generatedLine; + var columnA = mappingA.generatedColumn; + var columnB = mappingB.generatedColumn; + return lineB > lineA || lineB == lineA && columnB >= columnA || + util.compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0; + } + + /** + * A data structure to provide a sorted view of accumulated mappings in a + * performance conscious manner. It trades a neglibable overhead in general + * case for a large speedup in case of mappings being added in order. + */ + function MappingList() { + this._array = []; + this._sorted = true; + // Serves as infimum + this._last = {generatedLine: -1, generatedColumn: 0}; + } + + /** + * Iterate through internal items. This method takes the same arguments that + * `Array.prototype.forEach` takes. + * + * NOTE: The order of the mappings is NOT guaranteed. + */ + MappingList.prototype.unsortedForEach = + function MappingList_forEach(aCallback, aThisArg) { + this._array.forEach(aCallback, aThisArg); + }; + + /** + * Add the given source mapping. + * + * @param Object aMapping + */ + MappingList.prototype.add = function MappingList_add(aMapping) { + if (generatedPositionAfter(this._last, aMapping)) { + this._last = aMapping; + this._array.push(aMapping); + } else { + this._sorted = false; + this._array.push(aMapping); + } + }; + + /** + * Returns the flat, sorted array of mappings. The mappings are sorted by + * generated position. + * + * WARNING: This method returns internal data without copying, for + * performance. The return value must NOT be mutated, and should be treated as + * an immutable borrow. If you want to take ownership, you must make your own + * copy. + */ + MappingList.prototype.toArray = function MappingList_toArray() { + if (!this._sorted) { + this._array.sort(util.compareByGeneratedPositionsInflated); + this._sorted = true; + } + return this._array; + }; + + exports.MappingList = MappingList; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + /* -*- Mode: js; js-indent-level: 2; -*- */ + /* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + + var util = __webpack_require__(4); + var binarySearch = __webpack_require__(8); + var ArraySet = __webpack_require__(5).ArraySet; + var base64VLQ = __webpack_require__(2); + var quickSort = __webpack_require__(9).quickSort; + + function SourceMapConsumer(aSourceMap) { + var sourceMap = aSourceMap; + if (typeof aSourceMap === 'string') { + sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); + } + + return sourceMap.sections != null + ? new IndexedSourceMapConsumer(sourceMap) + : new BasicSourceMapConsumer(sourceMap); + } + + SourceMapConsumer.fromSourceMap = function(aSourceMap) { + return BasicSourceMapConsumer.fromSourceMap(aSourceMap); + } + + /** + * The version of the source mapping spec that we are consuming. + */ + SourceMapConsumer.prototype._version = 3; + + // `__generatedMappings` and `__originalMappings` are arrays that hold the + // parsed mapping coordinates from the source map's "mappings" attribute. They + // are lazily instantiated, accessed via the `_generatedMappings` and + // `_originalMappings` getters respectively, and we only parse the mappings + // and create these arrays once queried for a source location. We jump through + // these hoops because there can be many thousands of mappings, and parsing + // them is expensive, so we only want to do it if we must. + // + // Each object in the arrays is of the form: + // + // { + // generatedLine: The line number in the generated code, + // generatedColumn: The column number in the generated code, + // source: The path to the original source file that generated this + // chunk of code, + // originalLine: The line number in the original source that + // corresponds to this chunk of generated code, + // originalColumn: The column number in the original source that + // corresponds to this chunk of generated code, + // name: The name of the original symbol which generated this chunk of + // code. + // } + // + // All properties except for `generatedLine` and `generatedColumn` can be + // `null`. + // + // `_generatedMappings` is ordered by the generated positions. + // + // `_originalMappings` is ordered by the original positions. + + SourceMapConsumer.prototype.__generatedMappings = null; + Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { + get: function () { + if (!this.__generatedMappings) { + this._parseMappings(this._mappings, this.sourceRoot); + } + + return this.__generatedMappings; + } + }); + + SourceMapConsumer.prototype.__originalMappings = null; + Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { + get: function () { + if (!this.__originalMappings) { + this._parseMappings(this._mappings, this.sourceRoot); + } + + return this.__originalMappings; + } + }); + + SourceMapConsumer.prototype._charIsMappingSeparator = + function SourceMapConsumer_charIsMappingSeparator(aStr, index) { + var c = aStr.charAt(index); + return c === ";" || c === ","; + }; + + /** + * Parse the mappings in a string in to a data structure which we can easily + * query (the ordered arrays in the `this.__generatedMappings` and + * `this.__originalMappings` properties). + */ + SourceMapConsumer.prototype._parseMappings = + function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { + throw new Error("Subclasses must implement _parseMappings"); + }; + + SourceMapConsumer.GENERATED_ORDER = 1; + SourceMapConsumer.ORIGINAL_ORDER = 2; + + SourceMapConsumer.GREATEST_LOWER_BOUND = 1; + SourceMapConsumer.LEAST_UPPER_BOUND = 2; + + /** + * Iterate over each mapping between an original source/line/column and a + * generated line/column in this source map. + * + * @param Function aCallback + * The function that is called with each mapping. + * @param Object aContext + * Optional. If specified, this object will be the value of `this` every + * time that `aCallback` is called. + * @param aOrder + * Either `SourceMapConsumer.GENERATED_ORDER` or + * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to + * iterate over the mappings sorted by the generated file's line/column + * order or the original's source/line/column order, respectively. Defaults to + * `SourceMapConsumer.GENERATED_ORDER`. + */ + SourceMapConsumer.prototype.eachMapping = + function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { + var context = aContext || null; + var order = aOrder || SourceMapConsumer.GENERATED_ORDER; + + var mappings; + switch (order) { + case SourceMapConsumer.GENERATED_ORDER: + mappings = this._generatedMappings; + break; + case SourceMapConsumer.ORIGINAL_ORDER: + mappings = this._originalMappings; + break; + default: + throw new Error("Unknown order of iteration."); + } + + var sourceRoot = this.sourceRoot; + mappings.map(function (mapping) { + var source = mapping.source === null ? null : this._sources.at(mapping.source); + if (source != null && sourceRoot != null) { + source = util.join(sourceRoot, source); + } + return { + source: source, + generatedLine: mapping.generatedLine, + generatedColumn: mapping.generatedColumn, + originalLine: mapping.originalLine, + originalColumn: mapping.originalColumn, + name: mapping.name === null ? null : this._names.at(mapping.name) + }; + }, this).forEach(aCallback, context); + }; + + /** + * Returns all generated line and column information for the original source, + * line, and column provided. If no column is provided, returns all mappings + * corresponding to a either the line we are searching for or the next + * closest line that has any mappings. Otherwise, returns all mappings + * corresponding to the given line and either the column we are searching for + * or the next closest column that has any offsets. + * + * The only argument is an object with the following properties: + * + * - source: The filename of the original source. + * - line: The line number in the original source. + * - column: Optional. the column number in the original source. + * + * and an array of objects is returned, each with the following properties: + * + * - line: The line number in the generated source, or null. + * - column: The column number in the generated source, or null. + */ + SourceMapConsumer.prototype.allGeneratedPositionsFor = + function SourceMapConsumer_allGeneratedPositionsFor(aArgs) { + var line = util.getArg(aArgs, 'line'); + + // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping + // returns the index of the closest mapping less than the needle. By + // setting needle.originalColumn to 0, we thus find the last mapping for + // the given line, provided such a mapping exists. + var needle = { + source: util.getArg(aArgs, 'source'), + originalLine: line, + originalColumn: util.getArg(aArgs, 'column', 0) + }; + + if (this.sourceRoot != null) { + needle.source = util.relative(this.sourceRoot, needle.source); + } + if (!this._sources.has(needle.source)) { + return []; + } + needle.source = this._sources.indexOf(needle.source); + + var mappings = []; + + var index = this._findMapping(needle, + this._originalMappings, + "originalLine", + "originalColumn", + util.compareByOriginalPositions, + binarySearch.LEAST_UPPER_BOUND); + if (index >= 0) { + var mapping = this._originalMappings[index]; + + if (aArgs.column === undefined) { + var originalLine = mapping.originalLine; + + // Iterate until either we run out of mappings, or we run into + // a mapping for a different line than the one we found. Since + // mappings are sorted, this is guaranteed to find all mappings for + // the line we found. + while (mapping && mapping.originalLine === originalLine) { + mappings.push({ + line: util.getArg(mapping, 'generatedLine', null), + column: util.getArg(mapping, 'generatedColumn', null), + lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) + }); + + mapping = this._originalMappings[++index]; + } + } else { + var originalColumn = mapping.originalColumn; + + // Iterate until either we run out of mappings, or we run into + // a mapping for a different line than the one we were searching for. + // Since mappings are sorted, this is guaranteed to find all mappings for + // the line we are searching for. + while (mapping && + mapping.originalLine === line && + mapping.originalColumn == originalColumn) { + mappings.push({ + line: util.getArg(mapping, 'generatedLine', null), + column: util.getArg(mapping, 'generatedColumn', null), + lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) + }); + + mapping = this._originalMappings[++index]; + } + } + } + + return mappings; + }; + + exports.SourceMapConsumer = SourceMapConsumer; + + /** + * A BasicSourceMapConsumer instance represents a parsed source map which we can + * query for information about the original file positions by giving it a file + * position in the generated source. + * + * The only parameter is the raw source map (either as a JSON string, or + * already parsed to an object). According to the spec, source maps have the + * following attributes: + * + * - version: Which version of the source map spec this map is following. + * - sources: An array of URLs to the original source files. + * - names: An array of identifiers which can be referrenced by individual mappings. + * - sourceRoot: Optional. The URL root from which all sources are relative. + * - sourcesContent: Optional. An array of contents of the original source files. + * - mappings: A string of base64 VLQs which contain the actual mappings. + * - file: Optional. The generated file this source map is associated with. + * + * Here is an example source map, taken from the source map spec[0]: + * + * { + * version : 3, + * file: "out.js", + * sourceRoot : "", + * sources: ["foo.js", "bar.js"], + * names: ["src", "maps", "are", "fun"], + * mappings: "AA,AB;;ABCDE;" + * } + * + * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# + */ + function BasicSourceMapConsumer(aSourceMap) { + var sourceMap = aSourceMap; + if (typeof aSourceMap === 'string') { + sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); + } + + var version = util.getArg(sourceMap, 'version'); + var sources = util.getArg(sourceMap, 'sources'); + // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which + // requires the array) to play nice here. + var names = util.getArg(sourceMap, 'names', []); + var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); + var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); + var mappings = util.getArg(sourceMap, 'mappings'); + var file = util.getArg(sourceMap, 'file', null); + + // Once again, Sass deviates from the spec and supplies the version as a + // string rather than a number, so we use loose equality checking here. + if (version != this._version) { + throw new Error('Unsupported version: ' + version); + } + + sources = sources + .map(String) + // Some source maps produce relative source paths like "./foo.js" instead of + // "foo.js". Normalize these first so that future comparisons will succeed. + // See bugzil.la/1090768. + .map(util.normalize) + // Always ensure that absolute sources are internally stored relative to + // the source root, if the source root is absolute. Not doing this would + // be particularly problematic when the source root is a prefix of the + // source (valid, but why??). See github issue #199 and bugzil.la/1188982. + .map(function (source) { + return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source) + ? util.relative(sourceRoot, source) + : source; + }); + + // Pass `true` below to allow duplicate names and sources. While source maps + // are intended to be compressed and deduplicated, the TypeScript compiler + // sometimes generates source maps with duplicates in them. See Github issue + // #72 and bugzil.la/889492. + this._names = ArraySet.fromArray(names.map(String), true); + this._sources = ArraySet.fromArray(sources, true); + + this.sourceRoot = sourceRoot; + this.sourcesContent = sourcesContent; + this._mappings = mappings; + this.file = file; + } + + BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); + BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer; + + /** + * Create a BasicSourceMapConsumer from a SourceMapGenerator. + * + * @param SourceMapGenerator aSourceMap + * The source map that will be consumed. + * @returns BasicSourceMapConsumer + */ + BasicSourceMapConsumer.fromSourceMap = + function SourceMapConsumer_fromSourceMap(aSourceMap) { + var smc = Object.create(BasicSourceMapConsumer.prototype); + + var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); + var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); + smc.sourceRoot = aSourceMap._sourceRoot; + smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), + smc.sourceRoot); + smc.file = aSourceMap._file; + + // Because we are modifying the entries (by converting string sources and + // names to indices into the sources and names ArraySets), we have to make + // a copy of the entry or else bad things happen. Shared mutable state + // strikes again! See github issue #191. + + var generatedMappings = aSourceMap._mappings.toArray().slice(); + var destGeneratedMappings = smc.__generatedMappings = []; + var destOriginalMappings = smc.__originalMappings = []; + + for (var i = 0, length = generatedMappings.length; i < length; i++) { + var srcMapping = generatedMappings[i]; + var destMapping = new Mapping; + destMapping.generatedLine = srcMapping.generatedLine; + destMapping.generatedColumn = srcMapping.generatedColumn; + + if (srcMapping.source) { + destMapping.source = sources.indexOf(srcMapping.source); + destMapping.originalLine = srcMapping.originalLine; + destMapping.originalColumn = srcMapping.originalColumn; + + if (srcMapping.name) { + destMapping.name = names.indexOf(srcMapping.name); + } + + destOriginalMappings.push(destMapping); + } + + destGeneratedMappings.push(destMapping); + } + + quickSort(smc.__originalMappings, util.compareByOriginalPositions); + + return smc; + }; + + /** + * The version of the source mapping spec that we are consuming. + */ + BasicSourceMapConsumer.prototype._version = 3; + + /** + * The list of original sources. + */ + Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', { + get: function () { + return this._sources.toArray().map(function (s) { + return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s; + }, this); + } + }); + + /** + * Provide the JIT with a nice shape / hidden class. + */ + function Mapping() { + this.generatedLine = 0; + this.generatedColumn = 0; + this.source = null; + this.originalLine = null; + this.originalColumn = null; + this.name = null; + } + + /** + * Parse the mappings in a string in to a data structure which we can easily + * query (the ordered arrays in the `this.__generatedMappings` and + * `this.__originalMappings` properties). + */ + BasicSourceMapConsumer.prototype._parseMappings = + function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { + var generatedLine = 1; + var previousGeneratedColumn = 0; + var previousOriginalLine = 0; + var previousOriginalColumn = 0; + var previousSource = 0; + var previousName = 0; + var length = aStr.length; + var index = 0; + var cachedSegments = {}; + var temp = {}; + var originalMappings = []; + var generatedMappings = []; + var mapping, str, segment, end, value; + + while (index < length) { + if (aStr.charAt(index) === ';') { + generatedLine++; + index++; + previousGeneratedColumn = 0; + } + else if (aStr.charAt(index) === ',') { + index++; + } + else { + mapping = new Mapping(); + mapping.generatedLine = generatedLine; + + // Because each offset is encoded relative to the previous one, + // many segments often have the same encoding. We can exploit this + // fact by caching the parsed variable length fields of each segment, + // allowing us to avoid a second parse if we encounter the same + // segment again. + for (end = index; end < length; end++) { + if (this._charIsMappingSeparator(aStr, end)) { + break; + } + } + str = aStr.slice(index, end); + + segment = cachedSegments[str]; + if (segment) { + index += str.length; + } else { + segment = []; + while (index < end) { + base64VLQ.decode(aStr, index, temp); + value = temp.value; + index = temp.rest; + segment.push(value); + } + + if (segment.length === 2) { + throw new Error('Found a source, but no line and column'); + } + + if (segment.length === 3) { + throw new Error('Found a source and line, but no column'); + } + + cachedSegments[str] = segment; + } + + // Generated column. + mapping.generatedColumn = previousGeneratedColumn + segment[0]; + previousGeneratedColumn = mapping.generatedColumn; + + if (segment.length > 1) { + // Original source. + mapping.source = previousSource + segment[1]; + previousSource += segment[1]; + + // Original line. + mapping.originalLine = previousOriginalLine + segment[2]; + previousOriginalLine = mapping.originalLine; + // Lines are stored 0-based + mapping.originalLine += 1; + + // Original column. + mapping.originalColumn = previousOriginalColumn + segment[3]; + previousOriginalColumn = mapping.originalColumn; + + if (segment.length > 4) { + // Original name. + mapping.name = previousName + segment[4]; + previousName += segment[4]; + } + } + + generatedMappings.push(mapping); + if (typeof mapping.originalLine === 'number') { + originalMappings.push(mapping); + } + } + } + + quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated); + this.__generatedMappings = generatedMappings; + + quickSort(originalMappings, util.compareByOriginalPositions); + this.__originalMappings = originalMappings; + }; + + /** + * Find the mapping that best matches the hypothetical "needle" mapping that + * we are searching for in the given "haystack" of mappings. + */ + BasicSourceMapConsumer.prototype._findMapping = + function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, + aColumnName, aComparator, aBias) { + // To return the position we are searching for, we must first find the + // mapping for the given position and then return the opposite position it + // points to. Because the mappings are sorted, we can use binary search to + // find the best mapping. + + if (aNeedle[aLineName] <= 0) { + throw new TypeError('Line must be greater than or equal to 1, got ' + + aNeedle[aLineName]); + } + if (aNeedle[aColumnName] < 0) { + throw new TypeError('Column must be greater than or equal to 0, got ' + + aNeedle[aColumnName]); + } + + return binarySearch.search(aNeedle, aMappings, aComparator, aBias); + }; + + /** + * Compute the last column for each generated mapping. The last column is + * inclusive. + */ + BasicSourceMapConsumer.prototype.computeColumnSpans = + function SourceMapConsumer_computeColumnSpans() { + for (var index = 0; index < this._generatedMappings.length; ++index) { + var mapping = this._generatedMappings[index]; + + // Mappings do not contain a field for the last generated columnt. We + // can come up with an optimistic estimate, however, by assuming that + // mappings are contiguous (i.e. given two consecutive mappings, the + // first mapping ends where the second one starts). + if (index + 1 < this._generatedMappings.length) { + var nextMapping = this._generatedMappings[index + 1]; + + if (mapping.generatedLine === nextMapping.generatedLine) { + mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1; + continue; + } + } + + // The last mapping for each line spans the entire line. + mapping.lastGeneratedColumn = Infinity; + } + }; + + /** + * Returns the original source, line, and column information for the generated + * source's line and column positions provided. The only argument is an object + * with the following properties: + * + * - line: The line number in the generated source. + * - column: The column number in the generated source. + * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or + * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the + * closest element that is smaller than or greater than the one we are + * searching for, respectively, if the exact element cannot be found. + * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'. + * + * and an object is returned with the following properties: + * + * - source: The original source file, or null. + * - line: The line number in the original source, or null. + * - column: The column number in the original source, or null. + * - name: The original identifier, or null. + */ + BasicSourceMapConsumer.prototype.originalPositionFor = + function SourceMapConsumer_originalPositionFor(aArgs) { + var needle = { + generatedLine: util.getArg(aArgs, 'line'), + generatedColumn: util.getArg(aArgs, 'column') + }; + + var index = this._findMapping( + needle, + this._generatedMappings, + "generatedLine", + "generatedColumn", + util.compareByGeneratedPositionsDeflated, + util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) + ); + + if (index >= 0) { + var mapping = this._generatedMappings[index]; + + if (mapping.generatedLine === needle.generatedLine) { + var source = util.getArg(mapping, 'source', null); + if (source !== null) { + source = this._sources.at(source); + if (this.sourceRoot != null) { + source = util.join(this.sourceRoot, source); + } + } + var name = util.getArg(mapping, 'name', null); + if (name !== null) { + name = this._names.at(name); + } + return { + source: source, + line: util.getArg(mapping, 'originalLine', null), + column: util.getArg(mapping, 'originalColumn', null), + name: name + }; + } + } + + return { + source: null, + line: null, + column: null, + name: null + }; + }; + + /** + * Return true if we have the source content for every source in the source + * map, false otherwise. + */ + BasicSourceMapConsumer.prototype.hasContentsOfAllSources = + function BasicSourceMapConsumer_hasContentsOfAllSources() { + if (!this.sourcesContent) { + return false; + } + return this.sourcesContent.length >= this._sources.size() && + !this.sourcesContent.some(function (sc) { return sc == null; }); + }; + + /** + * Returns the original source content. The only argument is the url of the + * original source file. Returns null if no original source content is + * available. + */ + BasicSourceMapConsumer.prototype.sourceContentFor = + function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) { + if (!this.sourcesContent) { + return null; + } + + if (this.sourceRoot != null) { + aSource = util.relative(this.sourceRoot, aSource); + } + + if (this._sources.has(aSource)) { + return this.sourcesContent[this._sources.indexOf(aSource)]; + } + + var url; + if (this.sourceRoot != null + && (url = util.urlParse(this.sourceRoot))) { + // XXX: file:// URIs and absolute paths lead to unexpected behavior for + // many users. We can help them out when they expect file:// URIs to + // behave like it would if they were running a local HTTP server. See + // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. + var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); + if (url.scheme == "file" + && this._sources.has(fileUriAbsPath)) { + return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] + } + + if ((!url.path || url.path == "/") + && this._sources.has("/" + aSource)) { + return this.sourcesContent[this._sources.indexOf("/" + aSource)]; + } + } + + // This function is used recursively from + // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we + // don't want to throw if we can't find the source - we just want to + // return null, so we provide a flag to exit gracefully. + if (nullOnMissing) { + return null; + } + else { + throw new Error('"' + aSource + '" is not in the SourceMap.'); + } + }; + + /** + * Returns the generated line and column information for the original source, + * line, and column positions provided. The only argument is an object with + * the following properties: + * + * - source: The filename of the original source. + * - line: The line number in the original source. + * - column: The column number in the original source. + * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or + * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the + * closest element that is smaller than or greater than the one we are + * searching for, respectively, if the exact element cannot be found. + * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'. + * + * and an object is returned with the following properties: + * + * - line: The line number in the generated source, or null. + * - column: The column number in the generated source, or null. + */ + BasicSourceMapConsumer.prototype.generatedPositionFor = + function SourceMapConsumer_generatedPositionFor(aArgs) { + var source = util.getArg(aArgs, 'source'); + if (this.sourceRoot != null) { + source = util.relative(this.sourceRoot, source); + } + if (!this._sources.has(source)) { + return { + line: null, + column: null, + lastColumn: null + }; + } + source = this._sources.indexOf(source); + + var needle = { + source: source, + originalLine: util.getArg(aArgs, 'line'), + originalColumn: util.getArg(aArgs, 'column') + }; + + var index = this._findMapping( + needle, + this._originalMappings, + "originalLine", + "originalColumn", + util.compareByOriginalPositions, + util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) + ); + + if (index >= 0) { + var mapping = this._originalMappings[index]; + + if (mapping.source === needle.source) { + return { + line: util.getArg(mapping, 'generatedLine', null), + column: util.getArg(mapping, 'generatedColumn', null), + lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) + }; + } + } + + return { + line: null, + column: null, + lastColumn: null + }; + }; + + exports.BasicSourceMapConsumer = BasicSourceMapConsumer; + + /** + * An IndexedSourceMapConsumer instance represents a parsed source map which + * we can query for information. It differs from BasicSourceMapConsumer in + * that it takes "indexed" source maps (i.e. ones with a "sections" field) as + * input. + * + * The only parameter is a raw source map (either as a JSON string, or already + * parsed to an object). According to the spec for indexed source maps, they + * have the following attributes: + * + * - version: Which version of the source map spec this map is following. + * - file: Optional. The generated file this source map is associated with. + * - sections: A list of section definitions. + * + * Each value under the "sections" field has two fields: + * - offset: The offset into the original specified at which this section + * begins to apply, defined as an object with a "line" and "column" + * field. + * - map: A source map definition. This source map could also be indexed, + * but doesn't have to be. + * + * Instead of the "map" field, it's also possible to have a "url" field + * specifying a URL to retrieve a source map from, but that's currently + * unsupported. + * + * Here's an example source map, taken from the source map spec[0], but + * modified to omit a section which uses the "url" field. + * + * { + * version : 3, + * file: "app.js", + * sections: [{ + * offset: {line:100, column:10}, + * map: { + * version : 3, + * file: "section.js", + * sources: ["foo.js", "bar.js"], + * names: ["src", "maps", "are", "fun"], + * mappings: "AAAA,E;;ABCDE;" + * } + * }], + * } + * + * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt + */ + function IndexedSourceMapConsumer(aSourceMap) { + var sourceMap = aSourceMap; + if (typeof aSourceMap === 'string') { + sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); + } + + var version = util.getArg(sourceMap, 'version'); + var sections = util.getArg(sourceMap, 'sections'); + + if (version != this._version) { + throw new Error('Unsupported version: ' + version); + } + + this._sources = new ArraySet(); + this._names = new ArraySet(); + + var lastOffset = { + line: -1, + column: 0 + }; + this._sections = sections.map(function (s) { + if (s.url) { + // The url field will require support for asynchronicity. + // See https://github.com/mozilla/source-map/issues/16 + throw new Error('Support for url field in sections not implemented.'); + } + var offset = util.getArg(s, 'offset'); + var offsetLine = util.getArg(offset, 'line'); + var offsetColumn = util.getArg(offset, 'column'); + + if (offsetLine < lastOffset.line || + (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) { + throw new Error('Section offsets must be ordered and non-overlapping.'); + } + lastOffset = offset; + + return { + generatedOffset: { + // The offset fields are 0-based, but we use 1-based indices when + // encoding/decoding from VLQ. + generatedLine: offsetLine + 1, + generatedColumn: offsetColumn + 1 + }, + consumer: new SourceMapConsumer(util.getArg(s, 'map')) + } + }); + } + + IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); + IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer; + + /** + * The version of the source mapping spec that we are consuming. + */ + IndexedSourceMapConsumer.prototype._version = 3; + + /** + * The list of original sources. + */ + Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', { + get: function () { + var sources = []; + for (var i = 0; i < this._sections.length; i++) { + for (var j = 0; j < this._sections[i].consumer.sources.length; j++) { + sources.push(this._sections[i].consumer.sources[j]); + } + } + return sources; + } + }); + + /** + * Returns the original source, line, and column information for the generated + * source's line and column positions provided. The only argument is an object + * with the following properties: + * + * - line: The line number in the generated source. + * - column: The column number in the generated source. + * + * and an object is returned with the following properties: + * + * - source: The original source file, or null. + * - line: The line number in the original source, or null. + * - column: The column number in the original source, or null. + * - name: The original identifier, or null. + */ + IndexedSourceMapConsumer.prototype.originalPositionFor = + function IndexedSourceMapConsumer_originalPositionFor(aArgs) { + var needle = { + generatedLine: util.getArg(aArgs, 'line'), + generatedColumn: util.getArg(aArgs, 'column') + }; + + // Find the section containing the generated position we're trying to map + // to an original position. + var sectionIndex = binarySearch.search(needle, this._sections, + function(needle, section) { + var cmp = needle.generatedLine - section.generatedOffset.generatedLine; + if (cmp) { + return cmp; + } + + return (needle.generatedColumn - + section.generatedOffset.generatedColumn); + }); + var section = this._sections[sectionIndex]; + + if (!section) { + return { + source: null, + line: null, + column: null, + name: null + }; + } + + return section.consumer.originalPositionFor({ + line: needle.generatedLine - + (section.generatedOffset.generatedLine - 1), + column: needle.generatedColumn - + (section.generatedOffset.generatedLine === needle.generatedLine + ? section.generatedOffset.generatedColumn - 1 + : 0), + bias: aArgs.bias + }); + }; + + /** + * Return true if we have the source content for every source in the source + * map, false otherwise. + */ + IndexedSourceMapConsumer.prototype.hasContentsOfAllSources = + function IndexedSourceMapConsumer_hasContentsOfAllSources() { + return this._sections.every(function (s) { + return s.consumer.hasContentsOfAllSources(); + }); + }; + + /** + * Returns the original source content. The only argument is the url of the + * original source file. Returns null if no original source content is + * available. + */ + IndexedSourceMapConsumer.prototype.sourceContentFor = + function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) { + for (var i = 0; i < this._sections.length; i++) { + var section = this._sections[i]; + + var content = section.consumer.sourceContentFor(aSource, true); + if (content) { + return content; + } + } + if (nullOnMissing) { + return null; + } + else { + throw new Error('"' + aSource + '" is not in the SourceMap.'); + } + }; + + /** + * Returns the generated line and column information for the original source, + * line, and column positions provided. The only argument is an object with + * the following properties: + * + * - source: The filename of the original source. + * - line: The line number in the original source. + * - column: The column number in the original source. + * + * and an object is returned with the following properties: + * + * - line: The line number in the generated source, or null. + * - column: The column number in the generated source, or null. + */ + IndexedSourceMapConsumer.prototype.generatedPositionFor = + function IndexedSourceMapConsumer_generatedPositionFor(aArgs) { + for (var i = 0; i < this._sections.length; i++) { + var section = this._sections[i]; + + // Only consider this section if the requested source is in the list of + // sources of the consumer. + if (section.consumer.sources.indexOf(util.getArg(aArgs, 'source')) === -1) { + continue; + } + var generatedPosition = section.consumer.generatedPositionFor(aArgs); + if (generatedPosition) { + var ret = { + line: generatedPosition.line + + (section.generatedOffset.generatedLine - 1), + column: generatedPosition.column + + (section.generatedOffset.generatedLine === generatedPosition.line + ? section.generatedOffset.generatedColumn - 1 + : 0) + }; + return ret; + } + } + + return { + line: null, + column: null + }; + }; + + /** + * Parse the mappings in a string in to a data structure which we can easily + * query (the ordered arrays in the `this.__generatedMappings` and + * `this.__originalMappings` properties). + */ + IndexedSourceMapConsumer.prototype._parseMappings = + function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) { + this.__generatedMappings = []; + this.__originalMappings = []; + for (var i = 0; i < this._sections.length; i++) { + var section = this._sections[i]; + var sectionMappings = section.consumer._generatedMappings; + for (var j = 0; j < sectionMappings.length; j++) { + var mapping = sectionMappings[j]; + + var source = section.consumer._sources.at(mapping.source); + if (section.consumer.sourceRoot !== null) { + source = util.join(section.consumer.sourceRoot, source); + } + this._sources.add(source); + source = this._sources.indexOf(source); + + var name = section.consumer._names.at(mapping.name); + this._names.add(name); + name = this._names.indexOf(name); + + // The mappings coming from the consumer for the section have + // generated positions relative to the start of the section, so we + // need to offset them to be relative to the start of the concatenated + // generated file. + var adjustedMapping = { + source: source, + generatedLine: mapping.generatedLine + + (section.generatedOffset.generatedLine - 1), + generatedColumn: mapping.generatedColumn + + (section.generatedOffset.generatedLine === mapping.generatedLine + ? section.generatedOffset.generatedColumn - 1 + : 0), + originalLine: mapping.originalLine, + originalColumn: mapping.originalColumn, + name: name + }; + + this.__generatedMappings.push(adjustedMapping); + if (typeof adjustedMapping.originalLine === 'number') { + this.__originalMappings.push(adjustedMapping); + } + } + } + + quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated); + quickSort(this.__originalMappings, util.compareByOriginalPositions); + }; + + exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; + + +/***/ }, +/* 8 */ +/***/ function(module, exports) { + + /* -*- Mode: js; js-indent-level: 2; -*- */ + /* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + + exports.GREATEST_LOWER_BOUND = 1; + exports.LEAST_UPPER_BOUND = 2; + + /** + * Recursive implementation of binary search. + * + * @param aLow Indices here and lower do not contain the needle. + * @param aHigh Indices here and higher do not contain the needle. + * @param aNeedle The element being searched for. + * @param aHaystack The non-empty array being searched. + * @param aCompare Function which takes two elements and returns -1, 0, or 1. + * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or + * 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the + * closest element that is smaller than or greater than the one we are + * searching for, respectively, if the exact element cannot be found. + */ + function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare, aBias) { + // This function terminates when one of the following is true: + // + // 1. We find the exact element we are looking for. + // + // 2. We did not find the exact element, but we can return the index of + // the next-closest element. + // + // 3. We did not find the exact element, and there is no next-closest + // element than the one we are searching for, so we return -1. + var mid = Math.floor((aHigh - aLow) / 2) + aLow; + var cmp = aCompare(aNeedle, aHaystack[mid], true); + if (cmp === 0) { + // Found the element we are looking for. + return mid; + } + else if (cmp > 0) { + // Our needle is greater than aHaystack[mid]. + if (aHigh - mid > 1) { + // The element is in the upper half. + return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare, aBias); + } + + // The exact needle element was not found in this haystack. Determine if + // we are in termination case (3) or (2) and return the appropriate thing. + if (aBias == exports.LEAST_UPPER_BOUND) { + return aHigh < aHaystack.length ? aHigh : -1; + } else { + return mid; + } + } + else { + // Our needle is less than aHaystack[mid]. + if (mid - aLow > 1) { + // The element is in the lower half. + return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare, aBias); + } + + // we are in termination case (3) or (2) and return the appropriate thing. + if (aBias == exports.LEAST_UPPER_BOUND) { + return mid; + } else { + return aLow < 0 ? -1 : aLow; + } + } + } + + /** + * This is an implementation of binary search which will always try and return + * the index of the closest element if there is no exact hit. This is because + * mappings between original and generated line/col pairs are single points, + * and there is an implicit region between each of them, so a miss just means + * that you aren't on the very start of a region. + * + * @param aNeedle The element you are looking for. + * @param aHaystack The array that is being searched. + * @param aCompare A function which takes the needle and an element in the + * array and returns -1, 0, or 1 depending on whether the needle is less + * than, equal to, or greater than the element, respectively. + * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or + * 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the + * closest element that is smaller than or greater than the one we are + * searching for, respectively, if the exact element cannot be found. + * Defaults to 'binarySearch.GREATEST_LOWER_BOUND'. + */ + exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { + if (aHaystack.length === 0) { + return -1; + } + + var index = recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, + aCompare, aBias || exports.GREATEST_LOWER_BOUND); + if (index < 0) { + return -1; + } + + // We have found either the exact element, or the next-closest element than + // the one we are searching for. However, there may be more than one such + // element. Make sure we always return the smallest of these. + while (index - 1 >= 0) { + if (aCompare(aHaystack[index], aHaystack[index - 1], true) !== 0) { + break; + } + --index; + } + + return index; + }; + + +/***/ }, +/* 9 */ +/***/ function(module, exports) { + + /* -*- Mode: js; js-indent-level: 2; -*- */ + /* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + + // It turns out that some (most?) JavaScript engines don't self-host + // `Array.prototype.sort`. This makes sense because C++ will likely remain + // faster than JS when doing raw CPU-intensive sorting. However, when using a + // custom comparator function, calling back and forth between the VM's C++ and + // JIT'd JS is rather slow *and* loses JIT type information, resulting in + // worse generated code for the comparator function than would be optimal. In + // fact, when sorting with a comparator, these costs outweigh the benefits of + // sorting in C++. By using our own JS-implemented Quick Sort (below), we get + // a ~3500ms mean speed-up in `bench/bench.html`. + + /** + * Swap the elements indexed by `x` and `y` in the array `ary`. + * + * @param {Array} ary + * The array. + * @param {Number} x + * The index of the first item. + * @param {Number} y + * The index of the second item. + */ + function swap(ary, x, y) { + var temp = ary[x]; + ary[x] = ary[y]; + ary[y] = temp; + } + + /** + * Returns a random integer within the range `low .. high` inclusive. + * + * @param {Number} low + * The lower bound on the range. + * @param {Number} high + * The upper bound on the range. + */ + function randomIntInRange(low, high) { + return Math.round(low + (Math.random() * (high - low))); + } + + /** + * The Quick Sort algorithm. + * + * @param {Array} ary + * An array to sort. + * @param {function} comparator + * Function to use to compare two items. + * @param {Number} p + * Start index of the array + * @param {Number} r + * End index of the array + */ + function doQuickSort(ary, comparator, p, r) { + // If our lower bound is less than our upper bound, we (1) partition the + // array into two pieces and (2) recurse on each half. If it is not, this is + // the empty array and our base case. + + if (p < r) { + // (1) Partitioning. + // + // The partitioning chooses a pivot between `p` and `r` and moves all + // elements that are less than or equal to the pivot to the before it, and + // all the elements that are greater than it after it. The effect is that + // once partition is done, the pivot is in the exact place it will be when + // the array is put in sorted order, and it will not need to be moved + // again. This runs in O(n) time. + + // Always choose a random pivot so that an input array which is reverse + // sorted does not cause O(n^2) running time. + var pivotIndex = randomIntInRange(p, r); + var i = p - 1; + + swap(ary, pivotIndex, r); + var pivot = ary[r]; + + // Immediately after `j` is incremented in this loop, the following hold + // true: + // + // * Every element in `ary[p .. i]` is less than or equal to the pivot. + // + // * Every element in `ary[i+1 .. j-1]` is greater than the pivot. + for (var j = p; j < r; j++) { + if (comparator(ary[j], pivot) <= 0) { + i += 1; + swap(ary, i, j); + } + } + + swap(ary, i + 1, j); + var q = i + 1; + + // (2) Recurse on each half. + + doQuickSort(ary, comparator, p, q - 1); + doQuickSort(ary, comparator, q + 1, r); + } + } + + /** + * Sort the given array in-place with the given comparator function. + * + * @param {Array} ary + * An array to sort. + * @param {function} comparator + * Function to use to compare two items. + */ + exports.quickSort = function (ary, comparator) { + doQuickSort(ary, comparator, 0, ary.length - 1); + }; + + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + /* -*- Mode: js; js-indent-level: 2; -*- */ + /* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + + var SourceMapGenerator = __webpack_require__(1).SourceMapGenerator; + var util = __webpack_require__(4); + + // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other + // operating systems these days (capturing the result). + var REGEX_NEWLINE = /(\r?\n)/; + + // Newline character code for charCodeAt() comparisons + var NEWLINE_CODE = 10; + + // Private symbol for identifying `SourceNode`s when multiple versions of + // the source-map library are loaded. This MUST NOT CHANGE across + // versions! + var isSourceNode = "$$$isSourceNode$$$"; + + /** + * SourceNodes provide a way to abstract over interpolating/concatenating + * snippets of generated JavaScript source code while maintaining the line and + * column information associated with the original source code. + * + * @param aLine The original line number. + * @param aColumn The original column number. + * @param aSource The original source's filename. + * @param aChunks Optional. An array of strings which are snippets of + * generated JS, or other SourceNodes. + * @param aName The original identifier. + */ + function SourceNode(aLine, aColumn, aSource, aChunks, aName) { + this.children = []; + this.sourceContents = {}; + this.line = aLine == null ? null : aLine; + this.column = aColumn == null ? null : aColumn; + this.source = aSource == null ? null : aSource; + this.name = aName == null ? null : aName; + this[isSourceNode] = true; + if (aChunks != null) this.add(aChunks); + } + + /** + * Creates a SourceNode from generated code and a SourceMapConsumer. + * + * @param aGeneratedCode The generated code + * @param aSourceMapConsumer The SourceMap for the generated code + * @param aRelativePath Optional. The path that relative sources in the + * SourceMapConsumer should be relative to. + */ + SourceNode.fromStringWithSourceMap = + function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) { + // The SourceNode we want to fill with the generated code + // and the SourceMap + var node = new SourceNode(); + + // All even indices of this array are one line of the generated code, + // while all odd indices are the newlines between two adjacent lines + // (since `REGEX_NEWLINE` captures its match). + // Processed fragments are removed from this array, by calling `shiftNextLine`. + var remainingLines = aGeneratedCode.split(REGEX_NEWLINE); + var shiftNextLine = function() { + var lineContents = remainingLines.shift(); + // The last line of a file might not have a newline. + var newLine = remainingLines.shift() || ""; + return lineContents + newLine; + }; + + // We need to remember the position of "remainingLines" + var lastGeneratedLine = 1, lastGeneratedColumn = 0; + + // The generate SourceNodes we need a code range. + // To extract it current and last mapping is used. + // Here we store the last mapping. + var lastMapping = null; + + aSourceMapConsumer.eachMapping(function (mapping) { + if (lastMapping !== null) { + // We add the code from "lastMapping" to "mapping": + // First check if there is a new line in between. + if (lastGeneratedLine < mapping.generatedLine) { + // Associate first line with "lastMapping" + addMappingWithCode(lastMapping, shiftNextLine()); + lastGeneratedLine++; + lastGeneratedColumn = 0; + // The remaining code is added without mapping + } else { + // There is no new line in between. + // Associate the code between "lastGeneratedColumn" and + // "mapping.generatedColumn" with "lastMapping" + var nextLine = remainingLines[0]; + var code = nextLine.substr(0, mapping.generatedColumn - + lastGeneratedColumn); + remainingLines[0] = nextLine.substr(mapping.generatedColumn - + lastGeneratedColumn); + lastGeneratedColumn = mapping.generatedColumn; + addMappingWithCode(lastMapping, code); + // No more remaining code, continue + lastMapping = mapping; + return; + } + } + // We add the generated code until the first mapping + // to the SourceNode without any mapping. + // Each line is added as separate string. + while (lastGeneratedLine < mapping.generatedLine) { + node.add(shiftNextLine()); + lastGeneratedLine++; + } + if (lastGeneratedColumn < mapping.generatedColumn) { + var nextLine = remainingLines[0]; + node.add(nextLine.substr(0, mapping.generatedColumn)); + remainingLines[0] = nextLine.substr(mapping.generatedColumn); + lastGeneratedColumn = mapping.generatedColumn; + } + lastMapping = mapping; + }, this); + // We have processed all mappings. + if (remainingLines.length > 0) { + if (lastMapping) { + // Associate the remaining code in the current line with "lastMapping" + addMappingWithCode(lastMapping, shiftNextLine()); + } + // and add the remaining lines without any mapping + node.add(remainingLines.join("")); + } + + // Copy sourcesContent into SourceNode + aSourceMapConsumer.sources.forEach(function (sourceFile) { + var content = aSourceMapConsumer.sourceContentFor(sourceFile); + if (content != null) { + if (aRelativePath != null) { + sourceFile = util.join(aRelativePath, sourceFile); + } + node.setSourceContent(sourceFile, content); + } + }); + + return node; + + function addMappingWithCode(mapping, code) { + if (mapping === null || mapping.source === undefined) { + node.add(code); + } else { + var source = aRelativePath + ? util.join(aRelativePath, mapping.source) + : mapping.source; + node.add(new SourceNode(mapping.originalLine, + mapping.originalColumn, + source, + code, + mapping.name)); + } + } + }; + + /** + * Add a chunk of generated JS to this source node. + * + * @param aChunk A string snippet of generated JS code, another instance of + * SourceNode, or an array where each member is one of those things. + */ + SourceNode.prototype.add = function SourceNode_add(aChunk) { + if (Array.isArray(aChunk)) { + aChunk.forEach(function (chunk) { + this.add(chunk); + }, this); + } + else if (aChunk[isSourceNode] || typeof aChunk === "string") { + if (aChunk) { + this.children.push(aChunk); + } + } + else { + throw new TypeError( + "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk + ); + } + return this; + }; + + /** + * Add a chunk of generated JS to the beginning of this source node. + * + * @param aChunk A string snippet of generated JS code, another instance of + * SourceNode, or an array where each member is one of those things. + */ + SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { + if (Array.isArray(aChunk)) { + for (var i = aChunk.length-1; i >= 0; i--) { + this.prepend(aChunk[i]); + } + } + else if (aChunk[isSourceNode] || typeof aChunk === "string") { + this.children.unshift(aChunk); + } + else { + throw new TypeError( + "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk + ); + } + return this; + }; + + /** + * Walk over the tree of JS snippets in this node and its children. The + * walking function is called once for each snippet of JS and is passed that + * snippet and the its original associated source's line/column location. + * + * @param aFn The traversal function. + */ + SourceNode.prototype.walk = function SourceNode_walk(aFn) { + var chunk; + for (var i = 0, len = this.children.length; i < len; i++) { + chunk = this.children[i]; + if (chunk[isSourceNode]) { + chunk.walk(aFn); + } + else { + if (chunk !== '') { + aFn(chunk, { source: this.source, + line: this.line, + column: this.column, + name: this.name }); + } + } + } + }; + + /** + * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between + * each of `this.children`. + * + * @param aSep The separator. + */ + SourceNode.prototype.join = function SourceNode_join(aSep) { + var newChildren; + var i; + var len = this.children.length; + if (len > 0) { + newChildren = []; + for (i = 0; i < len-1; i++) { + newChildren.push(this.children[i]); + newChildren.push(aSep); + } + newChildren.push(this.children[i]); + this.children = newChildren; + } + return this; + }; + + /** + * Call String.prototype.replace on the very right-most source snippet. Useful + * for trimming whitespace from the end of a source node, etc. + * + * @param aPattern The pattern to replace. + * @param aReplacement The thing to replace the pattern with. + */ + SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { + var lastChild = this.children[this.children.length - 1]; + if (lastChild[isSourceNode]) { + lastChild.replaceRight(aPattern, aReplacement); + } + else if (typeof lastChild === 'string') { + this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); + } + else { + this.children.push(''.replace(aPattern, aReplacement)); + } + return this; + }; + + /** + * Set the source content for a source file. This will be added to the SourceMapGenerator + * in the sourcesContent field. + * + * @param aSourceFile The filename of the source file + * @param aSourceContent The content of the source file + */ + SourceNode.prototype.setSourceContent = + function SourceNode_setSourceContent(aSourceFile, aSourceContent) { + this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; + }; + + /** + * Walk over the tree of SourceNodes. The walking function is called for each + * source file content and is passed the filename and source content. + * + * @param aFn The traversal function. + */ + SourceNode.prototype.walkSourceContents = + function SourceNode_walkSourceContents(aFn) { + for (var i = 0, len = this.children.length; i < len; i++) { + if (this.children[i][isSourceNode]) { + this.children[i].walkSourceContents(aFn); + } + } + + var sources = Object.keys(this.sourceContents); + for (var i = 0, len = sources.length; i < len; i++) { + aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); + } + }; + + /** + * Return the string representation of this source node. Walks over the tree + * and concatenates all the various snippets together to one string. + */ + SourceNode.prototype.toString = function SourceNode_toString() { + var str = ""; + this.walk(function (chunk) { + str += chunk; + }); + return str; + }; + + /** + * Returns the string representation of this source node along with a source + * map. + */ + SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { + var generated = { + code: "", + line: 1, + column: 0 + }; + var map = new SourceMapGenerator(aArgs); + var sourceMappingActive = false; + var lastOriginalSource = null; + var lastOriginalLine = null; + var lastOriginalColumn = null; + var lastOriginalName = null; + this.walk(function (chunk, original) { + generated.code += chunk; + if (original.source !== null + && original.line !== null + && original.column !== null) { + if(lastOriginalSource !== original.source + || lastOriginalLine !== original.line + || lastOriginalColumn !== original.column + || lastOriginalName !== original.name) { + map.addMapping({ + source: original.source, + original: { + line: original.line, + column: original.column + }, + generated: { + line: generated.line, + column: generated.column + }, + name: original.name + }); + } + lastOriginalSource = original.source; + lastOriginalLine = original.line; + lastOriginalColumn = original.column; + lastOriginalName = original.name; + sourceMappingActive = true; + } else if (sourceMappingActive) { + map.addMapping({ + generated: { + line: generated.line, + column: generated.column + } + }); + lastOriginalSource = null; + sourceMappingActive = false; + } + for (var idx = 0, length = chunk.length; idx < length; idx++) { + if (chunk.charCodeAt(idx) === NEWLINE_CODE) { + generated.line++; + generated.column = 0; + // Mappings end at eol + if (idx + 1 === length) { + lastOriginalSource = null; + sourceMappingActive = false; + } else if (sourceMappingActive) { + map.addMapping({ + source: original.source, + original: { + line: original.line, + column: original.column + }, + generated: { + line: generated.line, + column: generated.column + }, + name: original.name + }); + } + } else { + generated.column++; + } + } + }); + this.walkSourceContents(function (sourceFile, sourceContent) { + map.setSourceContent(sourceFile, sourceContent); + }); + + return { code: generated.code, map: map }; + }; + + exports.SourceNode = SourceNode; + + +/***/ } +/******/ ]) +}); +;//Distributed under the BSD license: +//Copyright 2012 (c) Mihai Bazon +define('uglifyjs', ['exports', 'source-map', 'logger', 'env!env/file'], function (exports, MOZ_SourceMap, logger, rjsFile) { + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +function array_to_hash(a) { + var ret = Object.create(null); + for (var i = 0; i < a.length; ++i) + ret[a[i]] = true; + return ret; +}; + +function slice(a, start) { + return Array.prototype.slice.call(a, start || 0); +}; + +function characters(str) { + return str.split(""); +}; + +function member(name, array) { + return array.indexOf(name) >= 0; +}; + +function find_if(func, array) { + for (var i = 0, n = array.length; i < n; ++i) { + if (func(array[i])) + return array[i]; + } +}; + +function repeat_string(str, i) { + if (i <= 0) return ""; + if (i == 1) return str; + var d = repeat_string(str, i >> 1); + d += d; + if (i & 1) d += str; + return d; +}; + +function DefaultsError(msg, defs) { + Error.call(this, msg); + this.msg = msg; + this.defs = defs; +}; +DefaultsError.prototype = Object.create(Error.prototype); +DefaultsError.prototype.constructor = DefaultsError; + +DefaultsError.croak = function(msg, defs) { + throw new DefaultsError(msg, defs); +}; + +function defaults(args, defs, croak) { + if (args === true) + args = {}; + var ret = args || {}; + if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i)) + DefaultsError.croak("`" + i + "` is not a supported option", defs); + for (var i in defs) if (HOP(defs, i)) { + ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; + } + return ret; +}; + +function merge(obj, ext) { + var count = 0; + for (var i in ext) if (HOP(ext, i)) { + obj[i] = ext[i]; + count++; + } + return count; +}; + +function noop() {}; + +var MAP = (function(){ + function MAP(a, f, backwards) { + var ret = [], top = [], i; + function doit() { + var val = f(a[i], i); + var is_last = val instanceof Last; + if (is_last) val = val.v; + if (val instanceof AtTop) { + val = val.v; + if (val instanceof Splice) { + top.push.apply(top, backwards ? val.v.slice().reverse() : val.v); + } else { + top.push(val); + } + } + else if (val !== skip) { + if (val instanceof Splice) { + ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v); + } else { + ret.push(val); + } + } + return is_last; + }; + if (a instanceof Array) { + if (backwards) { + for (i = a.length; --i >= 0;) if (doit()) break; + ret.reverse(); + top.reverse(); + } else { + for (i = 0; i < a.length; ++i) if (doit()) break; + } + } + else { + for (i in a) if (HOP(a, i)) if (doit()) break; + } + return top.concat(ret); + }; + MAP.at_top = function(val) { return new AtTop(val) }; + MAP.splice = function(val) { return new Splice(val) }; + MAP.last = function(val) { return new Last(val) }; + var skip = MAP.skip = {}; + function AtTop(val) { this.v = val }; + function Splice(val) { this.v = val }; + function Last(val) { this.v = val }; + return MAP; +})(); + +function push_uniq(array, el) { + if (array.indexOf(el) < 0) + array.push(el); +}; + +function string_template(text, props) { + return text.replace(/\{(.+?)\}/g, function(str, p){ + return props[p]; + }); +}; + +function remove(array, el) { + for (var i = array.length; --i >= 0;) { + if (array[i] === el) array.splice(i, 1); + } +}; + +function mergeSort(array, cmp) { + if (array.length < 2) return array.slice(); + function merge(a, b) { + var r = [], ai = 0, bi = 0, i = 0; + while (ai < a.length && bi < b.length) { + cmp(a[ai], b[bi]) <= 0 + ? r[i++] = a[ai++] + : r[i++] = b[bi++]; + } + if (ai < a.length) r.push.apply(r, a.slice(ai)); + if (bi < b.length) r.push.apply(r, b.slice(bi)); + return r; + }; + function _ms(a) { + if (a.length <= 1) + return a; + var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m); + left = _ms(left); + right = _ms(right); + return merge(left, right); + }; + return _ms(array); +}; + +function set_difference(a, b) { + return a.filter(function(el){ + return b.indexOf(el) < 0; + }); +}; + +function set_intersection(a, b) { + return a.filter(function(el){ + return b.indexOf(el) >= 0; + }); +}; + +// this function is taken from Acorn [1], written by Marijn Haverbeke +// [1] https://github.com/marijnh/acorn +function makePredicate(words) { + if (!(words instanceof Array)) words = words.split(" "); + var f = "", cats = []; + out: for (var i = 0; i < words.length; ++i) { + for (var j = 0; j < cats.length; ++j) + if (cats[j][0].length == words[i].length) { + cats[j].push(words[i]); + continue out; + } + cats.push([words[i]]); + } + function quote(word) { + return JSON.stringify(word).replace(/[\u2028\u2029]/g, function(s) { + switch (s) { + case "\u2028": return "\\u2028"; + case "\u2029": return "\\u2029"; + } + return s; + }); + } + function compareTo(arr) { + if (arr.length == 1) return f += "return str === " + quote(arr[0]) + ";"; + f += "switch(str){"; + for (var i = 0; i < arr.length; ++i) f += "case " + quote(arr[i]) + ":"; + f += "return true}return false;"; + } + // When there are more than three length categories, an outer + // switch first dispatches on the lengths, to save on comparisons. + if (cats.length > 3) { + cats.sort(function(a, b) {return b.length - a.length;}); + f += "switch(str.length){"; + for (var i = 0; i < cats.length; ++i) { + var cat = cats[i]; + f += "case " + cat[0].length + ":"; + compareTo(cat); + } + f += "}"; + // Otherwise, simply generate a flat `switch` statement. + } else { + compareTo(words); + } + return new Function("str", f); +}; + +function all(array, predicate) { + for (var i = array.length; --i >= 0;) + if (!predicate(array[i])) + return false; + return true; +}; + +function Dictionary() { + this._values = Object.create(null); + this._size = 0; +}; +Dictionary.prototype = { + set: function(key, val) { + if (!this.has(key)) ++this._size; + this._values["$" + key] = val; + return this; + }, + add: function(key, val) { + if (this.has(key)) { + this.get(key).push(val); + } else { + this.set(key, [ val ]); + } + return this; + }, + get: function(key) { return this._values["$" + key] }, + del: function(key) { + if (this.has(key)) { + --this._size; + delete this._values["$" + key]; + } + return this; + }, + has: function(key) { return ("$" + key) in this._values }, + each: function(f) { + for (var i in this._values) + f(this._values[i], i.substr(1)); + }, + size: function() { + return this._size; + }, + map: function(f) { + var ret = []; + for (var i in this._values) + ret.push(f(this._values[i], i.substr(1))); + return ret; + }, + toObject: function() { return this._values } +}; +Dictionary.fromObject = function(obj) { + var dict = new Dictionary(); + dict._size = merge(dict._values, obj); + return dict; +}; + +function HOP(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +function DEFNODE(type, props, methods, base) { + if (arguments.length < 4) base = AST_Node; + if (!props) props = []; + else props = props.split(/\s+/); + var self_props = props; + if (base && base.PROPS) + props = props.concat(base.PROPS); + var code = "return function AST_" + type + "(props){ if (props) { "; + for (var i = props.length; --i >= 0;) { + code += "this." + props[i] + " = props." + props[i] + ";"; + } + var proto = base && new base; + if (proto && proto.initialize || (methods && methods.initialize)) + code += "this.initialize();"; + code += "}}"; + var ctor = new Function(code)(); + if (proto) { + ctor.prototype = proto; + ctor.BASE = base; + } + if (base) base.SUBCLASSES.push(ctor); + ctor.prototype.CTOR = ctor; + ctor.PROPS = props || null; + ctor.SELF_PROPS = self_props; + ctor.SUBCLASSES = []; + if (type) { + ctor.prototype.TYPE = ctor.TYPE = type; + } + if (methods) for (i in methods) if (HOP(methods, i)) { + if (/^\$/.test(i)) { + ctor[i.substr(1)] = methods[i]; + } else { + ctor.prototype[i] = methods[i]; + } + } + ctor.DEFMETHOD = function(name, method) { + this.prototype[name] = method; + }; + exports["AST_" + type] = ctor; + return ctor; +}; + +var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw", { +}, null); + +var AST_Node = DEFNODE("Node", "start end", { + clone: function() { + return new this.CTOR(this); + }, + $documentation: "Base class of all AST nodes", + $propdoc: { + start: "[AST_Token] The first token of this node", + end: "[AST_Token] The last token of this node" + }, + _walk: function(visitor) { + return visitor._visit(this); + }, + walk: function(visitor) { + return this._walk(visitor); // not sure the indirection will be any help + } +}, null); + +AST_Node.warn_function = null; +AST_Node.warn = function(txt, props) { + if (AST_Node.warn_function) + AST_Node.warn_function(string_template(txt, props)); +}; + +/* -----[ statements ]----- */ + +var AST_Statement = DEFNODE("Statement", null, { + $documentation: "Base class of all statements", +}); + +var AST_Debugger = DEFNODE("Debugger", null, { + $documentation: "Represents a debugger statement", +}, AST_Statement); + +var AST_Directive = DEFNODE("Directive", "value scope quote", { + $documentation: "Represents a directive, like \"use strict\";", + $propdoc: { + value: "[string] The value of this directive as a plain string (it's not an AST_String!)", + scope: "[AST_Scope/S] The scope that this directive affects", + quote: "[string] the original quote character" + }, +}, AST_Statement); + +var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", { + $documentation: "A statement consisting of an expression, i.e. a = 1 + 2", + $propdoc: { + body: "[AST_Node] an expression node (should not be instanceof AST_Statement)" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.body._walk(visitor); + }); + } +}, AST_Statement); + +function walk_body(node, visitor) { + if (node.body instanceof AST_Statement) { + node.body._walk(visitor); + } + else node.body.forEach(function(stat){ + stat._walk(visitor); + }); +}; + +var AST_Block = DEFNODE("Block", "body", { + $documentation: "A body of statements (usually bracketed)", + $propdoc: { + body: "[AST_Statement*] an array of statements" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + walk_body(this, visitor); + }); + } +}, AST_Statement); + +var AST_BlockStatement = DEFNODE("BlockStatement", null, { + $documentation: "A block statement", +}, AST_Block); + +var AST_EmptyStatement = DEFNODE("EmptyStatement", null, { + $documentation: "The empty statement (empty block or simply a semicolon)", + _walk: function(visitor) { + return visitor._visit(this); + } +}, AST_Statement); + +var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", { + $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`", + $propdoc: { + body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.body._walk(visitor); + }); + } +}, AST_Statement); + +var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", { + $documentation: "Statement with a label", + $propdoc: { + label: "[AST_Label] a label definition" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.label._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_StatementWithBody); + +var AST_IterationStatement = DEFNODE("IterationStatement", null, { + $documentation: "Internal class. All loops inherit from it." +}, AST_StatementWithBody); + +var AST_DWLoop = DEFNODE("DWLoop", "condition", { + $documentation: "Base class for do/while statements", + $propdoc: { + condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement" + } +}, AST_IterationStatement); + +var AST_Do = DEFNODE("Do", null, { + $documentation: "A `do` statement", + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.body._walk(visitor); + this.condition._walk(visitor); + }); + } +}, AST_DWLoop); + +var AST_While = DEFNODE("While", null, { + $documentation: "A `while` statement", + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.condition._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_DWLoop); + +var AST_For = DEFNODE("For", "init condition step", { + $documentation: "A `for` statement", + $propdoc: { + init: "[AST_Node?] the `for` initialization code, or null if empty", + condition: "[AST_Node?] the `for` termination clause, or null if empty", + step: "[AST_Node?] the `for` update clause, or null if empty" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + if (this.init) this.init._walk(visitor); + if (this.condition) this.condition._walk(visitor); + if (this.step) this.step._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_IterationStatement); + +var AST_ForIn = DEFNODE("ForIn", "init name object", { + $documentation: "A `for ... in` statement", + $propdoc: { + init: "[AST_Node] the `for/in` initialization code", + name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var", + object: "[AST_Node] the object that we're looping through" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.init._walk(visitor); + this.object._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_IterationStatement); + +var AST_With = DEFNODE("With", "expression", { + $documentation: "A `with` statement", + $propdoc: { + expression: "[AST_Node] the `with` expression" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + this.body._walk(visitor); + }); + } +}, AST_StatementWithBody); + +/* -----[ scope and functions ]----- */ + +var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", { + $documentation: "Base class for all statements introducing a lexical scope", + $propdoc: { + directives: "[string*/S] an array of directives declared in this scope", + variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope", + functions: "[Object/S] like `variables`, but only lists function declarations", + uses_with: "[boolean/S] tells whether this scope uses the `with` statement", + uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`", + parent_scope: "[AST_Scope?/S] link to the parent scope", + enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", + cname: "[integer/S] current index for mangling variables (used internally by the mangler)", + }, +}, AST_Block); + +var AST_Toplevel = DEFNODE("Toplevel", "globals", { + $documentation: "The toplevel scope", + $propdoc: { + globals: "[Object/S] a map of name -> SymbolDef for all undeclared names", + }, + wrap_enclose: function(arg_parameter_pairs) { + var self = this; + var args = []; + var parameters = []; + + arg_parameter_pairs.forEach(function(pair) { + var splitAt = pair.lastIndexOf(":"); + + args.push(pair.substr(0, splitAt)); + parameters.push(pair.substr(splitAt + 1)); + }); + + var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")"; + wrapped_tl = parse(wrapped_tl); + wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ + if (node instanceof AST_Directive && node.value == "$ORIG") { + return MAP.splice(self.body); + } + })); + return wrapped_tl; + }, + wrap_commonjs: function(name, export_all) { + var self = this; + var to_export = []; + if (export_all) { + self.figure_out_scope(); + self.walk(new TreeWalker(function(node){ + if (node instanceof AST_SymbolDeclaration && node.definition().global) { + if (!find_if(function(n){ return n.name == node.name }, to_export)) + to_export.push(node); + } + })); + } + var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "'] = exports; }({}, (function(){return this}())))"; + wrapped_tl = parse(wrapped_tl); + wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ + if (node instanceof AST_Directive) { + switch (node.value) { + case "$ORIG": + return MAP.splice(self.body); + case "$EXPORTS": + var body = []; + to_export.forEach(function(sym){ + body.push(new AST_SimpleStatement({ + body: new AST_Assign({ + left: new AST_Sub({ + expression: new AST_SymbolRef({ name: "exports" }), + property: new AST_String({ value: sym.name }), + }), + operator: "=", + right: new AST_SymbolRef(sym), + }), + })); + }); + return MAP.splice(body); + } + } + })); + return wrapped_tl; + } +}, AST_Scope); + +var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", { + $documentation: "Base class for functions", + $propdoc: { + name: "[AST_SymbolDeclaration?] the name of this function", + argnames: "[AST_SymbolFunarg*] array of function arguments", + uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + if (this.name) this.name._walk(visitor); + this.argnames.forEach(function(arg){ + arg._walk(visitor); + }); + walk_body(this, visitor); + }); + } +}, AST_Scope); + +var AST_Accessor = DEFNODE("Accessor", null, { + $documentation: "A setter/getter function. The `name` property is always null." +}, AST_Lambda); + +var AST_Function = DEFNODE("Function", null, { + $documentation: "A function expression" +}, AST_Lambda); + +var AST_Defun = DEFNODE("Defun", null, { + $documentation: "A function definition" +}, AST_Lambda); + +/* -----[ JUMPS ]----- */ + +var AST_Jump = DEFNODE("Jump", null, { + $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)" +}, AST_Statement); + +var AST_Exit = DEFNODE("Exit", "value", { + $documentation: "Base class for “exits” (`return` and `throw`)", + $propdoc: { + value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return" + }, + _walk: function(visitor) { + return visitor._visit(this, this.value && function(){ + this.value._walk(visitor); + }); + } +}, AST_Jump); + +var AST_Return = DEFNODE("Return", null, { + $documentation: "A `return` statement" +}, AST_Exit); + +var AST_Throw = DEFNODE("Throw", null, { + $documentation: "A `throw` statement" +}, AST_Exit); + +var AST_LoopControl = DEFNODE("LoopControl", "label", { + $documentation: "Base class for loop control statements (`break` and `continue`)", + $propdoc: { + label: "[AST_LabelRef?] the label, or null if none", + }, + _walk: function(visitor) { + return visitor._visit(this, this.label && function(){ + this.label._walk(visitor); + }); + } +}, AST_Jump); + +var AST_Break = DEFNODE("Break", null, { + $documentation: "A `break` statement" +}, AST_LoopControl); + +var AST_Continue = DEFNODE("Continue", null, { + $documentation: "A `continue` statement" +}, AST_LoopControl); + +/* -----[ IF ]----- */ + +var AST_If = DEFNODE("If", "condition alternative", { + $documentation: "A `if` statement", + $propdoc: { + condition: "[AST_Node] the `if` condition", + alternative: "[AST_Statement?] the `else` part, or null if not present" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.condition._walk(visitor); + this.body._walk(visitor); + if (this.alternative) this.alternative._walk(visitor); + }); + } +}, AST_StatementWithBody); + +/* -----[ SWITCH ]----- */ + +var AST_Switch = DEFNODE("Switch", "expression", { + $documentation: "A `switch` statement", + $propdoc: { + expression: "[AST_Node] the `switch` “discriminant”" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + walk_body(this, visitor); + }); + } +}, AST_Block); + +var AST_SwitchBranch = DEFNODE("SwitchBranch", null, { + $documentation: "Base class for `switch` branches", +}, AST_Block); + +var AST_Default = DEFNODE("Default", null, { + $documentation: "A `default` switch branch", +}, AST_SwitchBranch); + +var AST_Case = DEFNODE("Case", "expression", { + $documentation: "A `case` switch branch", + $propdoc: { + expression: "[AST_Node] the `case` expression" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + walk_body(this, visitor); + }); + } +}, AST_SwitchBranch); + +/* -----[ EXCEPTIONS ]----- */ + +var AST_Try = DEFNODE("Try", "bcatch bfinally", { + $documentation: "A `try` statement", + $propdoc: { + bcatch: "[AST_Catch?] the catch block, or null if not present", + bfinally: "[AST_Finally?] the finally block, or null if not present" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + walk_body(this, visitor); + if (this.bcatch) this.bcatch._walk(visitor); + if (this.bfinally) this.bfinally._walk(visitor); + }); + } +}, AST_Block); + +var AST_Catch = DEFNODE("Catch", "argname", { + $documentation: "A `catch` node; only makes sense as part of a `try` statement", + $propdoc: { + argname: "[AST_SymbolCatch] symbol for the exception" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.argname._walk(visitor); + walk_body(this, visitor); + }); + } +}, AST_Block); + +var AST_Finally = DEFNODE("Finally", null, { + $documentation: "A `finally` node; only makes sense as part of a `try` statement" +}, AST_Block); + +/* -----[ VAR/CONST ]----- */ + +var AST_Definitions = DEFNODE("Definitions", "definitions", { + $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)", + $propdoc: { + definitions: "[AST_VarDef*] array of variable definitions" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.definitions.forEach(function(def){ + def._walk(visitor); + }); + }); + } +}, AST_Statement); + +var AST_Var = DEFNODE("Var", null, { + $documentation: "A `var` statement" +}, AST_Definitions); + +var AST_Const = DEFNODE("Const", null, { + $documentation: "A `const` statement" +}, AST_Definitions); + +var AST_VarDef = DEFNODE("VarDef", "name value", { + $documentation: "A variable declaration; only appears in a AST_Definitions node", + $propdoc: { + name: "[AST_SymbolVar|AST_SymbolConst] name of the variable", + value: "[AST_Node?] initializer, or null of there's no initializer" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.name._walk(visitor); + if (this.value) this.value._walk(visitor); + }); + } +}); + +/* -----[ OTHER ]----- */ + +var AST_Call = DEFNODE("Call", "expression args", { + $documentation: "A function call expression", + $propdoc: { + expression: "[AST_Node] expression to invoke as function", + args: "[AST_Node*] array of arguments" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + this.args.forEach(function(arg){ + arg._walk(visitor); + }); + }); + } +}); + +var AST_New = DEFNODE("New", null, { + $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties" +}, AST_Call); + +var AST_Seq = DEFNODE("Seq", "car cdr", { + $documentation: "A sequence expression (two comma-separated expressions)", + $propdoc: { + car: "[AST_Node] first element in sequence", + cdr: "[AST_Node] second element in sequence" + }, + $cons: function(x, y) { + var seq = new AST_Seq(x); + seq.car = x; + seq.cdr = y; + return seq; + }, + $from_array: function(array) { + if (array.length == 0) return null; + if (array.length == 1) return array[0].clone(); + var list = null; + for (var i = array.length; --i >= 0;) { + list = AST_Seq.cons(array[i], list); + } + var p = list; + while (p) { + if (p.cdr && !p.cdr.cdr) { + p.cdr = p.cdr.car; + break; + } + p = p.cdr; + } + return list; + }, + to_array: function() { + var p = this, a = []; + while (p) { + a.push(p.car); + if (p.cdr && !(p.cdr instanceof AST_Seq)) { + a.push(p.cdr); + break; + } + p = p.cdr; + } + return a; + }, + add: function(node) { + var p = this; + while (p) { + if (!(p.cdr instanceof AST_Seq)) { + var cell = AST_Seq.cons(p.cdr, node); + return p.cdr = cell; + } + p = p.cdr; + } + }, + len: function() { + if (this.cdr instanceof AST_Seq) { + return this.cdr.len() + 1; + } else { + return 2; + } + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.car._walk(visitor); + if (this.cdr) this.cdr._walk(visitor); + }); + } +}); + +var AST_PropAccess = DEFNODE("PropAccess", "expression property", { + $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`", + $propdoc: { + expression: "[AST_Node] the “container” expression", + property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node" + } +}); + +var AST_Dot = DEFNODE("Dot", null, { + $documentation: "A dotted property access expression", + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + }); + } +}, AST_PropAccess); + +var AST_Sub = DEFNODE("Sub", null, { + $documentation: "Index-style property access, i.e. `a[\"foo\"]`", + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + this.property._walk(visitor); + }); + } +}, AST_PropAccess); + +var AST_Unary = DEFNODE("Unary", "operator expression", { + $documentation: "Base class for unary expressions", + $propdoc: { + operator: "[string] the operator", + expression: "[AST_Node] expression that this unary operator applies to" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.expression._walk(visitor); + }); + } +}); + +var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, { + $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`" +}, AST_Unary); + +var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, { + $documentation: "Unary postfix expression, i.e. `i++`" +}, AST_Unary); + +var AST_Binary = DEFNODE("Binary", "left operator right", { + $documentation: "Binary expression, i.e. `a + b`", + $propdoc: { + left: "[AST_Node] left-hand side expression", + operator: "[string] the operator", + right: "[AST_Node] right-hand side expression" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.left._walk(visitor); + this.right._walk(visitor); + }); + } +}); + +var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", { + $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`", + $propdoc: { + condition: "[AST_Node]", + consequent: "[AST_Node]", + alternative: "[AST_Node]" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.condition._walk(visitor); + this.consequent._walk(visitor); + this.alternative._walk(visitor); + }); + } +}); + +var AST_Assign = DEFNODE("Assign", null, { + $documentation: "An assignment expression — `a = b + 5`", +}, AST_Binary); + +/* -----[ LITERALS ]----- */ + +var AST_Array = DEFNODE("Array", "elements", { + $documentation: "An array literal", + $propdoc: { + elements: "[AST_Node*] array of elements" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.elements.forEach(function(el){ + el._walk(visitor); + }); + }); + } +}); + +var AST_Object = DEFNODE("Object", "properties", { + $documentation: "An object literal", + $propdoc: { + properties: "[AST_ObjectProperty*] array of properties" + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.properties.forEach(function(prop){ + prop._walk(visitor); + }); + }); + } +}); + +var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { + $documentation: "Base class for literal object properties", + $propdoc: { + key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.", + value: "[AST_Node] property value. For setters and getters this is an AST_Function." + }, + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.value._walk(visitor); + }); + } +}); + +var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", { + $documentation: "A key: value object property", + $propdoc: { + quote: "[string] the original quote character" + } +}, AST_ObjectProperty); + +var AST_ObjectSetter = DEFNODE("ObjectSetter", null, { + $documentation: "An object setter property", +}, AST_ObjectProperty); + +var AST_ObjectGetter = DEFNODE("ObjectGetter", null, { + $documentation: "An object getter property", +}, AST_ObjectProperty); + +var AST_Symbol = DEFNODE("Symbol", "scope name thedef", { + $propdoc: { + name: "[string] name of this symbol", + scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)", + thedef: "[SymbolDef/S] the definition of this symbol" + }, + $documentation: "Base class for all symbols", +}); + +var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, { + $documentation: "The name of a property accessor (setter/getter function)" +}, AST_Symbol); + +var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { + $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)", + $propdoc: { + init: "[AST_Node*/S] array of initializers for this declaration." + } +}, AST_Symbol); + +var AST_SymbolVar = DEFNODE("SymbolVar", null, { + $documentation: "Symbol defining a variable", +}, AST_SymbolDeclaration); + +var AST_SymbolConst = DEFNODE("SymbolConst", null, { + $documentation: "A constant declaration" +}, AST_SymbolDeclaration); + +var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { + $documentation: "Symbol naming a function argument", +}, AST_SymbolVar); + +var AST_SymbolDefun = DEFNODE("SymbolDefun", null, { + $documentation: "Symbol defining a function", +}, AST_SymbolDeclaration); + +var AST_SymbolLambda = DEFNODE("SymbolLambda", null, { + $documentation: "Symbol naming a function expression", +}, AST_SymbolDeclaration); + +var AST_SymbolCatch = DEFNODE("SymbolCatch", null, { + $documentation: "Symbol naming the exception in catch", +}, AST_SymbolDeclaration); + +var AST_Label = DEFNODE("Label", "references", { + $documentation: "Symbol naming a label (declaration)", + $propdoc: { + references: "[AST_LoopControl*] a list of nodes referring to this label" + }, + initialize: function() { + this.references = []; + this.thedef = this; + } +}, AST_Symbol); + +var AST_SymbolRef = DEFNODE("SymbolRef", null, { + $documentation: "Reference to some symbol (not definition/declaration)", +}, AST_Symbol); + +var AST_LabelRef = DEFNODE("LabelRef", null, { + $documentation: "Reference to a label symbol", +}, AST_Symbol); + +var AST_This = DEFNODE("This", null, { + $documentation: "The `this` symbol", +}, AST_Symbol); + +var AST_Constant = DEFNODE("Constant", null, { + $documentation: "Base class for all constants", + getValue: function() { + return this.value; + } +}); + +var AST_String = DEFNODE("String", "value quote", { + $documentation: "A string literal", + $propdoc: { + value: "[string] the contents of this string", + quote: "[string] the original quote character" + } +}, AST_Constant); + +var AST_Number = DEFNODE("Number", "value literal", { + $documentation: "A number literal", + $propdoc: { + value: "[number] the numeric value", + literal: "[string] numeric value as string (optional)" + } +}, AST_Constant); + +var AST_RegExp = DEFNODE("RegExp", "value", { + $documentation: "A regexp literal", + $propdoc: { + value: "[RegExp] the actual regexp" + } +}, AST_Constant); + +var AST_Atom = DEFNODE("Atom", null, { + $documentation: "Base class for atoms", +}, AST_Constant); + +var AST_Null = DEFNODE("Null", null, { + $documentation: "The `null` atom", + value: null +}, AST_Atom); + +var AST_NaN = DEFNODE("NaN", null, { + $documentation: "The impossible value", + value: 0/0 +}, AST_Atom); + +var AST_Undefined = DEFNODE("Undefined", null, { + $documentation: "The `undefined` value", + value: (function(){}()) +}, AST_Atom); + +var AST_Hole = DEFNODE("Hole", null, { + $documentation: "A hole in an array", + value: (function(){}()) +}, AST_Atom); + +var AST_Infinity = DEFNODE("Infinity", null, { + $documentation: "The `Infinity` value", + value: 1/0 +}, AST_Atom); + +var AST_Boolean = DEFNODE("Boolean", null, { + $documentation: "Base class for booleans", +}, AST_Atom); + +var AST_False = DEFNODE("False", null, { + $documentation: "The `false` atom", + value: false +}, AST_Boolean); + +var AST_True = DEFNODE("True", null, { + $documentation: "The `true` atom", + value: true +}, AST_Boolean); + +/* -----[ TreeWalker ]----- */ + +function TreeWalker(callback) { + this.visit = callback; + this.stack = []; + this.directives = Object.create(null); +}; +TreeWalker.prototype = { + _visit: function(node, descend) { + this.push(node); + var ret = this.visit(node, descend ? function(){ + descend.call(node); + } : noop); + if (!ret && descend) { + descend.call(node); + } + this.pop(node); + return ret; + }, + parent: function(n) { + return this.stack[this.stack.length - 2 - (n || 0)]; + }, + push: function (node) { + if (node instanceof AST_Lambda) { + this.directives = Object.create(this.directives); + } else if (node instanceof AST_Directive) { + this.directives[node.value] = this.directives[node.value] ? "up" : true; + } + this.stack.push(node); + }, + pop: function(node) { + this.stack.pop(); + if (node instanceof AST_Lambda) { + this.directives = Object.getPrototypeOf(this.directives); + } + }, + self: function() { + return this.stack[this.stack.length - 1]; + }, + find_parent: function(type) { + var stack = this.stack; + for (var i = stack.length; --i >= 0;) { + var x = stack[i]; + if (x instanceof type) return x; + } + }, + has_directive: function(type) { + var dir = this.directives[type]; + if (dir) return dir; + var node = this.stack[this.stack.length - 1]; + if (node instanceof AST_Scope) { + for (var i = 0; i < node.body.length; ++i) { + var st = node.body[i]; + if (!(st instanceof AST_Directive)) break; + if (st.value == type) return true; + } + } + }, + in_boolean_context: function() { + var stack = this.stack; + var i = stack.length, self = stack[--i]; + while (i > 0) { + var p = stack[--i]; + if ((p instanceof AST_If && p.condition === self) || + (p instanceof AST_Conditional && p.condition === self) || + (p instanceof AST_DWLoop && p.condition === self) || + (p instanceof AST_For && p.condition === self) || + (p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self)) + { + return true; + } + if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||"))) + return false; + self = p; + } + }, + loopcontrol_target: function(label) { + var stack = this.stack; + if (label) for (var i = stack.length; --i >= 0;) { + var x = stack[i]; + if (x instanceof AST_LabeledStatement && x.label.name == label.name) { + return x.body; + } + } else for (var i = stack.length; --i >= 0;) { + var x = stack[i]; + if (x instanceof AST_Switch || x instanceof AST_IterationStatement) + return x; + } + } +}; + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/). + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with'; +var KEYWORDS_ATOM = 'false null true'; +var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield' + + " " + KEYWORDS_ATOM + " " + KEYWORDS; +var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case'; + +KEYWORDS = makePredicate(KEYWORDS); +RESERVED_WORDS = makePredicate(RESERVED_WORDS); +KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION); +KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM); + +var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^")); + +var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; +var RE_OCT_NUMBER = /^0[0-7]+$/; + +var OPERATORS = makePredicate([ + "in", + "instanceof", + "typeof", + "new", + "void", + "delete", + "++", + "--", + "+", + "-", + "!", + "~", + "&", + "|", + "^", + "*", + "/", + "%", + ">>", + "<<", + ">>>", + "<", + ">", + "<=", + ">=", + "==", + "===", + "!=", + "!==", + "?", + "=", + "+=", + "-=", + "/=", + "*=", + "%=", + ">>=", + "<<=", + ">>>=", + "|=", + "^=", + "&=", + "&&", + "||" +]); + +var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\uFEFF")); + +var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029")); + +var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:")); + +var PUNC_CHARS = makePredicate(characters("[]{}(),;:")); + +var REGEXP_MODIFIERS = makePredicate(characters("gmsiy")); + +/* -----[ Tokenizer ]----- */ + +// regexps adapted from http://xregexp.com/plugins/#unicode +var UNICODE = { + letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B2\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"), + digit: new RegExp("[\\u0030-\\u0039\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0BE6-\\u0BEF\\u0C66-\\u0C6F\\u0CE6-\\u0CEF\\u0D66-\\u0D6F\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F29\\u1040-\\u1049\\u1090-\\u1099\\u17E0-\\u17E9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19D9\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\uA620-\\uA629\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19]"), + non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"), + space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"), + connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]") +}; + +function is_letter(code) { + return (code >= 97 && code <= 122) + || (code >= 65 && code <= 90) + || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code))); +}; + +function is_digit(code) { + return code >= 48 && code <= 57; +}; + +function is_alphanumeric_char(code) { + return is_digit(code) || is_letter(code); +}; + +function is_unicode_digit(code) { + return UNICODE.digit.test(String.fromCharCode(code)); +} + +function is_unicode_combining_mark(ch) { + return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch); +}; + +function is_unicode_connector_punctuation(ch) { + return UNICODE.connector_punctuation.test(ch); +}; + +function is_identifier(name) { + return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name); +}; + +function is_identifier_start(code) { + return code == 36 || code == 95 || is_letter(code); +}; + +function is_identifier_char(ch) { + var code = ch.charCodeAt(0); + return is_identifier_start(code) + || is_digit(code) + || code == 8204 // \u200c: zero-width non-joiner + || code == 8205 // \u200d: zero-width joiner (in my ECMA-262 PDF, this is also 200c) + || is_unicode_combining_mark(ch) + || is_unicode_connector_punctuation(ch) + || is_unicode_digit(code) + ; +}; + +function is_identifier_string(str){ + return /^[a-z_$][a-z0-9_$]*$/i.test(str); +}; + +function parse_js_number(num) { + if (RE_HEX_NUMBER.test(num)) { + return parseInt(num.substr(2), 16); + } else if (RE_OCT_NUMBER.test(num)) { + return parseInt(num.substr(1), 8); + } else { + var val = parseFloat(num); + if (val == num) return val; + } +}; + +function JS_Parse_Error(message, filename, line, col, pos) { + this.message = message; + this.filename = filename; + this.line = line; + this.col = col; + this.pos = pos; + this.stack = new Error().stack; +}; + +JS_Parse_Error.prototype.toString = function() { + return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack; +}; + +function js_error(message, filename, line, col, pos) { + throw new JS_Parse_Error(message, filename, line, col, pos); +}; + +function is_token(token, type, val) { + return token.type == type && (val == null || token.value == val); +}; + +var EX_EOF = {}; + +function tokenizer($TEXT, filename, html5_comments, shebang) { + + var S = { + text : $TEXT, + filename : filename, + pos : 0, + tokpos : 0, + line : 1, + tokline : 0, + col : 0, + tokcol : 0, + newline_before : false, + regex_allowed : false, + comments_before : [], + directives : {}, + directive_stack : [] + }; + + function peek() { return S.text.charAt(S.pos); }; + + function next(signal_eof, in_string) { + var ch = S.text.charAt(S.pos++); + if (signal_eof && !ch) + throw EX_EOF; + if (NEWLINE_CHARS(ch)) { + S.newline_before = S.newline_before || !in_string; + ++S.line; + S.col = 0; + if (!in_string && ch == "\r" && peek() == "\n") { + // treat a \r\n sequence as a single \n + ++S.pos; + ch = "\n"; + } + } else { + ++S.col; + } + return ch; + }; + + function forward(i) { + while (i-- > 0) next(); + }; + + function looking_at(str) { + return S.text.substr(S.pos, str.length) == str; + }; + + function find_eol() { + var text = S.text; + for (var i = S.pos, n = S.text.length; i < n; ++i) { + var ch = text[i]; + if (NEWLINE_CHARS(ch)) + return i; + } + return -1; + }; + + function find(what, signal_eof) { + var pos = S.text.indexOf(what, S.pos); + if (signal_eof && pos == -1) throw EX_EOF; + return pos; + }; + + function start_token() { + S.tokline = S.line; + S.tokcol = S.col; + S.tokpos = S.pos; + }; + + var prev_was_dot = false; + function token(type, value, is_comment) { + S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) || + (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) || + (type == "punc" && PUNC_BEFORE_EXPRESSION(value))); + prev_was_dot = (type == "punc" && value == "."); + var ret = { + type : type, + value : value, + line : S.tokline, + col : S.tokcol, + pos : S.tokpos, + endline : S.line, + endcol : S.col, + endpos : S.pos, + nlb : S.newline_before, + file : filename + }; + if (/^(?:num|string|regexp)$/i.test(type)) { + ret.raw = $TEXT.substring(ret.pos, ret.endpos); + } + if (!is_comment) { + ret.comments_before = S.comments_before; + S.comments_before = []; + // make note of any newlines in the comments that came before + for (var i = 0, len = ret.comments_before.length; i < len; i++) { + ret.nlb = ret.nlb || ret.comments_before[i].nlb; + } + } + S.newline_before = false; + return new AST_Token(ret); + }; + + function skip_whitespace() { + while (WHITESPACE_CHARS(peek())) + next(); + }; + + function read_while(pred) { + var ret = "", ch, i = 0; + while ((ch = peek()) && pred(ch, i++)) + ret += next(); + return ret; + }; + + function parse_error(err) { + js_error(err, filename, S.tokline, S.tokcol, S.tokpos); + }; + + function read_num(prefix) { + var has_e = false, after_e = false, has_x = false, has_dot = prefix == "."; + var num = read_while(function(ch, i){ + var code = ch.charCodeAt(0); + switch (code) { + case 120: case 88: // xX + return has_x ? false : (has_x = true); + case 101: case 69: // eE + return has_x ? true : has_e ? false : (has_e = after_e = true); + case 45: // - + return after_e || (i == 0 && !prefix); + case 43: // + + return after_e; + case (after_e = false, 46): // . + return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false; + } + return is_alphanumeric_char(code); + }); + if (prefix) num = prefix + num; + if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) { + parse_error("SyntaxError: Legacy octal literals are not allowed in strict mode"); + } + var valid = parse_js_number(num); + if (!isNaN(valid)) { + return token("num", valid); + } else { + parse_error("SyntaxError: Invalid syntax: " + num); + } + }; + + function read_escaped_char(in_string) { + var ch = next(true, in_string); + switch (ch.charCodeAt(0)) { + case 110 : return "\n"; + case 114 : return "\r"; + case 116 : return "\t"; + case 98 : return "\b"; + case 118 : return "\u000b"; // \v + case 102 : return "\f"; + case 120 : return String.fromCharCode(hex_bytes(2)); // \x + case 117 : return String.fromCharCode(hex_bytes(4)); // \u + case 10 : return ""; // newline + case 13 : // \r + if (peek() == "\n") { // DOS newline + next(true, in_string); + return ""; + } + } + if (ch >= "0" && ch <= "7") + return read_octal_escape_sequence(ch); + return ch; + }; + + function read_octal_escape_sequence(ch) { + // Read + var p = peek(); + if (p >= "0" && p <= "7") { + ch += next(true); + if (ch[0] <= "3" && (p = peek()) >= "0" && p <= "7") + ch += next(true); + } + + // Parse + if (ch === "0") return "\0"; + if (ch.length > 0 && next_token.has_directive("use strict")) + parse_error("SyntaxError: Legacy octal escape sequences are not allowed in strict mode"); + return String.fromCharCode(parseInt(ch, 8)); + } + + function hex_bytes(n) { + var num = 0; + for (; n > 0; --n) { + var digit = parseInt(next(true), 16); + if (isNaN(digit)) + parse_error("SyntaxError: Invalid hex-character pattern in string"); + num = (num << 4) | digit; + } + return num; + }; + + var read_string = with_eof_error("SyntaxError: Unterminated string constant", function(quote_char){ + var quote = next(), ret = ""; + for (;;) { + var ch = next(true, true); + if (ch == "\\") ch = read_escaped_char(true); + else if (NEWLINE_CHARS(ch)) parse_error("SyntaxError: Unterminated string constant"); + else if (ch == quote) break; + ret += ch; + } + var tok = token("string", ret); + tok.quote = quote_char; + return tok; + }); + + function skip_line_comment(type) { + var regex_allowed = S.regex_allowed; + var i = find_eol(), ret; + if (i == -1) { + ret = S.text.substr(S.pos); + S.pos = S.text.length; + } else { + ret = S.text.substring(S.pos, i); + S.pos = i; + } + S.col = S.tokcol + (S.pos - S.tokpos); + S.comments_before.push(token(type, ret, true)); + S.regex_allowed = regex_allowed; + return next_token; + }; + + var skip_multiline_comment = with_eof_error("SyntaxError: Unterminated multiline comment", function(){ + var regex_allowed = S.regex_allowed; + var i = find("*/", true); + var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n'); + // update stream position + forward(text.length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2); + S.comments_before.push(token("comment2", text, true)); + S.regex_allowed = regex_allowed; + return next_token; + }); + + function read_name() { + var backslash = false, name = "", ch, escaped = false, hex; + while ((ch = peek()) != null) { + if (!backslash) { + if (ch == "\\") escaped = backslash = true, next(); + else if (is_identifier_char(ch)) name += next(); + else break; + } + else { + if (ch != "u") parse_error("SyntaxError: Expecting UnicodeEscapeSequence -- uXXXX"); + ch = read_escaped_char(); + if (!is_identifier_char(ch)) parse_error("SyntaxError: Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier"); + name += ch; + backslash = false; + } + } + if (KEYWORDS(name) && escaped) { + hex = name.charCodeAt(0).toString(16).toUpperCase(); + name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1); + } + return name; + }; + + var read_regexp = with_eof_error("SyntaxError: Unterminated regular expression", function(regexp){ + var prev_backslash = false, ch, in_class = false; + while ((ch = next(true))) if (NEWLINE_CHARS(ch)) { + parse_error("SyntaxError: Unexpected line terminator"); + } else if (prev_backslash) { + regexp += "\\" + ch; + prev_backslash = false; + } else if (ch == "[") { + in_class = true; + regexp += ch; + } else if (ch == "]" && in_class) { + in_class = false; + regexp += ch; + } else if (ch == "/" && !in_class) { + break; + } else if (ch == "\\") { + prev_backslash = true; + } else { + regexp += ch; + } + var mods = read_name(); + try { + return token("regexp", new RegExp(regexp, mods)); + } catch(e) { + parse_error("SyntaxError: " + e.message); + } + }); + + function read_operator(prefix) { + function grow(op) { + if (!peek()) return op; + var bigger = op + peek(); + if (OPERATORS(bigger)) { + next(); + return grow(bigger); + } else { + return op; + } + }; + return token("operator", grow(prefix || next())); + }; + + function handle_slash() { + next(); + switch (peek()) { + case "/": + next(); + return skip_line_comment("comment1"); + case "*": + next(); + return skip_multiline_comment(); + } + return S.regex_allowed ? read_regexp("") : read_operator("/"); + }; + + function handle_dot() { + next(); + return is_digit(peek().charCodeAt(0)) + ? read_num(".") + : token("punc", "."); + }; + + function read_word() { + var word = read_name(); + if (prev_was_dot) return token("name", word); + return KEYWORDS_ATOM(word) ? token("atom", word) + : !KEYWORDS(word) ? token("name", word) + : OPERATORS(word) ? token("operator", word) + : token("keyword", word); + }; + + function with_eof_error(eof_error, cont) { + return function(x) { + try { + return cont(x); + } catch(ex) { + if (ex === EX_EOF) parse_error(eof_error); + else throw ex; + } + }; + }; + + function next_token(force_regexp) { + if (force_regexp != null) + return read_regexp(force_regexp); + for (;;) { + skip_whitespace(); + start_token(); + if (html5_comments) { + if (looking_at("") && S.newline_before) { + forward(3); + skip_line_comment("comment4"); + continue; + } + } + var ch = peek(); + if (!ch) return token("eof"); + var code = ch.charCodeAt(0); + switch (code) { + case 34: case 39: return read_string(ch); + case 46: return handle_dot(); + case 47: { + var tok = handle_slash(); + if (tok === next_token) continue; + return tok; + } + } + if (is_digit(code)) return read_num(); + if (PUNC_CHARS(ch)) return token("punc", next()); + if (OPERATOR_CHARS(ch)) return read_operator(); + if (code == 92 || is_identifier_start(code)) return read_word(); + if (shebang) { + if (S.pos == 0 && looking_at("#!")) { + forward(2); + skip_line_comment("comment5"); + continue; + } + } + break; + } + parse_error("SyntaxError: Unexpected character '" + ch + "'"); + }; + + next_token.context = function(nc) { + if (nc) S = nc; + return S; + }; + + next_token.add_directive = function(directive) { + S.directive_stack[S.directive_stack.length - 1].push(directive); + + if (S.directives[directive] === undefined) { + S.directives[directive] = 1; + } else { + S.directives[directive]++; + } + } + + next_token.push_directives_stack = function() { + S.directive_stack.push([]); + } + + next_token.pop_directives_stack = function() { + var directives = S.directive_stack[S.directive_stack.length - 1]; + + for (var i = 0; i < directives.length; i++) { + S.directives[directives[i]]--; + } + + S.directive_stack.pop(); + } + + next_token.has_directive = function(directive) { + return S.directives[directive] !== undefined && + S.directives[directive] > 0; + } + + return next_token; + +}; + +/* -----[ Parser (constants) ]----- */ + +var UNARY_PREFIX = makePredicate([ + "typeof", + "void", + "delete", + "--", + "++", + "!", + "~", + "-", + "+" +]); + +var UNARY_POSTFIX = makePredicate([ "--", "++" ]); + +var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); + +var PRECEDENCE = (function(a, ret){ + for (var i = 0; i < a.length; ++i) { + var b = a[i]; + for (var j = 0; j < b.length; ++j) { + ret[b[j]] = i + 1; + } + } + return ret; +})( + [ + ["||"], + ["&&"], + ["|"], + ["^"], + ["&"], + ["==", "===", "!=", "!=="], + ["<", ">", "<=", ">=", "in", "instanceof"], + [">>", "<<", ">>>"], + ["+", "-"], + ["*", "/", "%"] + ], + {} +); + +var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); + +var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]); + +/* -----[ Parser ]----- */ + +function parse($TEXT, options) { + + options = defaults(options, { + strict : false, + filename : null, + toplevel : null, + expression : false, + html5_comments : true, + bare_returns : false, + shebang : true, + }); + + var S = { + input : (typeof $TEXT == "string" + ? tokenizer($TEXT, options.filename, + options.html5_comments, options.shebang) + : $TEXT), + token : null, + prev : null, + peeked : null, + in_function : 0, + in_directives : true, + in_loop : 0, + labels : [] + }; + + S.token = next(); + + function is(type, value) { + return is_token(S.token, type, value); + }; + + function peek() { return S.peeked || (S.peeked = S.input()); }; + + function next() { + S.prev = S.token; + if (S.peeked) { + S.token = S.peeked; + S.peeked = null; + } else { + S.token = S.input(); + } + S.in_directives = S.in_directives && ( + S.token.type == "string" || is("punc", ";") + ); + return S.token; + }; + + function prev() { + return S.prev; + }; + + function croak(msg, line, col, pos) { + var ctx = S.input.context(); + js_error(msg, + ctx.filename, + line != null ? line : ctx.tokline, + col != null ? col : ctx.tokcol, + pos != null ? pos : ctx.tokpos); + }; + + function token_error(token, msg) { + croak(msg, token.line, token.col); + }; + + function unexpected(token) { + if (token == null) + token = S.token; + token_error(token, "SyntaxError: Unexpected token: " + token.type + " (" + token.value + ")"); + }; + + function expect_token(type, val) { + if (is(type, val)) { + return next(); + } + token_error(S.token, "SyntaxError: Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»"); + }; + + function expect(punc) { return expect_token("punc", punc); }; + + function can_insert_semicolon() { + return !options.strict && ( + S.token.nlb || is("eof") || is("punc", "}") + ); + }; + + function semicolon(optional) { + if (is("punc", ";")) next(); + else if (!optional && !can_insert_semicolon()) unexpected(); + }; + + function parenthesised() { + expect("("); + var exp = expression(true); + expect(")"); + return exp; + }; + + function embed_tokens(parser) { + return function() { + var start = S.token; + var expr = parser(); + var end = prev(); + expr.start = start; + expr.end = end; + return expr; + }; + }; + + function handle_regexp() { + if (is("operator", "/") || is("operator", "/=")) { + S.peeked = null; + S.token = S.input(S.token.value.substr(1)); // force regexp + } + }; + + var statement = embed_tokens(function() { + var tmp; + handle_regexp(); + switch (S.token.type) { + case "string": + var dir = false; + if (S.in_directives === true) { + if ((is_token(peek(), "punc", ";") || peek().nlb) && S.token.raw.indexOf("\\") === -1) { + S.input.add_directive(S.token.value); + } else { + S.in_directives = false; + } + } + var dir = S.in_directives, stat = simple_statement(); + if (dir) { + return new AST_Directive({ + start : stat.body.start, + end : stat.body.end, + quote : stat.body.quote, + value : stat.body.value, + }); + } + return stat; + case "num": + case "regexp": + case "operator": + case "atom": + return simple_statement(); + + case "name": + return is_token(peek(), "punc", ":") + ? labeled_statement() + : simple_statement(); + + case "punc": + switch (S.token.value) { + case "{": + return new AST_BlockStatement({ + start : S.token, + body : block_(), + end : prev() + }); + case "[": + case "(": + return simple_statement(); + case ";": + S.in_directives = false; + next(); + return new AST_EmptyStatement(); + default: + unexpected(); + } + + case "keyword": + switch (tmp = S.token.value, next(), tmp) { + case "break": + return break_cont(AST_Break); + + case "continue": + return break_cont(AST_Continue); + + case "debugger": + semicolon(); + return new AST_Debugger(); + + case "do": + return new AST_Do({ + body : in_loop(statement), + condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp) + }); + + case "while": + return new AST_While({ + condition : parenthesised(), + body : in_loop(statement) + }); + + case "for": + return for_(); + + case "function": + return function_(AST_Defun); + + case "if": + return if_(); + + case "return": + if (S.in_function == 0 && !options.bare_returns) + croak("SyntaxError: 'return' outside of function"); + return new AST_Return({ + value: ( is("punc", ";") + ? (next(), null) + : can_insert_semicolon() + ? null + : (tmp = expression(true), semicolon(), tmp) ) + }); + + case "switch": + return new AST_Switch({ + expression : parenthesised(), + body : in_loop(switch_body_) + }); + + case "throw": + if (S.token.nlb) + croak("SyntaxError: Illegal newline after 'throw'"); + return new AST_Throw({ + value: (tmp = expression(true), semicolon(), tmp) + }); + + case "try": + return try_(); + + case "var": + return tmp = var_(), semicolon(), tmp; + + case "const": + return tmp = const_(), semicolon(), tmp; + + case "with": + if (S.input.has_directive("use strict")) { + croak("SyntaxError: Strict mode may not include a with statement"); + } + return new AST_With({ + expression : parenthesised(), + body : statement() + }); + + default: + unexpected(); + } + } + }); + + function labeled_statement() { + var label = as_symbol(AST_Label); + if (find_if(function(l){ return l.name == label.name }, S.labels)) { + // ECMA-262, 12.12: An ECMAScript program is considered + // syntactically incorrect if it contains a + // LabelledStatement that is enclosed by a + // LabelledStatement with the same Identifier as label. + croak("SyntaxError: Label " + label.name + " defined twice"); + } + expect(":"); + S.labels.push(label); + var stat = statement(); + S.labels.pop(); + if (!(stat instanceof AST_IterationStatement)) { + // check for `continue` that refers to this label. + // those should be reported as syntax errors. + // https://github.com/mishoo/UglifyJS2/issues/287 + label.references.forEach(function(ref){ + if (ref instanceof AST_Continue) { + ref = ref.label.start; + croak("SyntaxError: Continue label `" + label.name + "` refers to non-IterationStatement.", + ref.line, ref.col, ref.pos); + } + }); + } + return new AST_LabeledStatement({ body: stat, label: label }); + }; + + function simple_statement(tmp) { + return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) }); + }; + + function break_cont(type) { + var label = null, ldef; + if (!can_insert_semicolon()) { + label = as_symbol(AST_LabelRef, true); + } + if (label != null) { + ldef = find_if(function(l){ return l.name == label.name }, S.labels); + if (!ldef) + croak("SyntaxError: Undefined label " + label.name); + label.thedef = ldef; + } + else if (S.in_loop == 0) + croak("SyntaxError: " + type.TYPE + " not inside a loop or switch"); + semicolon(); + var stat = new type({ label: label }); + if (ldef) ldef.references.push(stat); + return stat; + }; + + function for_() { + expect("("); + var init = null; + if (!is("punc", ";")) { + init = is("keyword", "var") + ? (next(), var_(true)) + : expression(true, true); + if (is("operator", "in")) { + if (init instanceof AST_Var && init.definitions.length > 1) + croak("SyntaxError: Only one variable declaration allowed in for..in loop"); + next(); + return for_in(init); + } + } + return regular_for(init); + }; + + function regular_for(init) { + expect(";"); + var test = is("punc", ";") ? null : expression(true); + expect(";"); + var step = is("punc", ")") ? null : expression(true); + expect(")"); + return new AST_For({ + init : init, + condition : test, + step : step, + body : in_loop(statement) + }); + }; + + function for_in(init) { + var lhs = init instanceof AST_Var ? init.definitions[0].name : null; + var obj = expression(true); + expect(")"); + return new AST_ForIn({ + init : init, + name : lhs, + object : obj, + body : in_loop(statement) + }); + }; + + var function_ = function(ctor) { + var in_statement = ctor === AST_Defun; + var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null; + if (in_statement && !name) + unexpected(); + expect("("); + return new ctor({ + name: name, + argnames: (function(first, a){ + while (!is("punc", ")")) { + if (first) first = false; else expect(","); + a.push(as_symbol(AST_SymbolFunarg)); + } + next(); + return a; + })(true, []), + body: (function(loop, labels){ + ++S.in_function; + S.in_directives = true; + S.input.push_directives_stack(); + S.in_loop = 0; + S.labels = []; + var a = block_(); + S.input.pop_directives_stack(); + --S.in_function; + S.in_loop = loop; + S.labels = labels; + return a; + })(S.in_loop, S.labels) + }); + }; + + function if_() { + var cond = parenthesised(), body = statement(), belse = null; + if (is("keyword", "else")) { + next(); + belse = statement(); + } + return new AST_If({ + condition : cond, + body : body, + alternative : belse + }); + }; + + function block_() { + expect("{"); + var a = []; + while (!is("punc", "}")) { + if (is("eof")) unexpected(); + a.push(statement()); + } + next(); + return a; + }; + + function switch_body_() { + expect("{"); + var a = [], cur = null, branch = null, tmp; + while (!is("punc", "}")) { + if (is("eof")) unexpected(); + if (is("keyword", "case")) { + if (branch) branch.end = prev(); + cur = []; + branch = new AST_Case({ + start : (tmp = S.token, next(), tmp), + expression : expression(true), + body : cur + }); + a.push(branch); + expect(":"); + } + else if (is("keyword", "default")) { + if (branch) branch.end = prev(); + cur = []; + branch = new AST_Default({ + start : (tmp = S.token, next(), expect(":"), tmp), + body : cur + }); + a.push(branch); + } + else { + if (!cur) unexpected(); + cur.push(statement()); + } + } + if (branch) branch.end = prev(); + next(); + return a; + }; + + function try_() { + var body = block_(), bcatch = null, bfinally = null; + if (is("keyword", "catch")) { + var start = S.token; + next(); + expect("("); + var name = as_symbol(AST_SymbolCatch); + expect(")"); + bcatch = new AST_Catch({ + start : start, + argname : name, + body : block_(), + end : prev() + }); + } + if (is("keyword", "finally")) { + var start = S.token; + next(); + bfinally = new AST_Finally({ + start : start, + body : block_(), + end : prev() + }); + } + if (!bcatch && !bfinally) + croak("SyntaxError: Missing catch/finally blocks"); + return new AST_Try({ + body : body, + bcatch : bcatch, + bfinally : bfinally + }); + }; + + function vardefs(no_in, in_const) { + var a = []; + for (;;) { + a.push(new AST_VarDef({ + start : S.token, + name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar), + value : is("operator", "=") ? (next(), expression(false, no_in)) : null, + end : prev() + })); + if (!is("punc", ",")) + break; + next(); + } + return a; + }; + + var var_ = function(no_in) { + return new AST_Var({ + start : prev(), + definitions : vardefs(no_in, false), + end : prev() + }); + }; + + var const_ = function() { + return new AST_Const({ + start : prev(), + definitions : vardefs(false, true), + end : prev() + }); + }; + + var new_ = function(allow_calls) { + var start = S.token; + expect_token("operator", "new"); + var newexp = expr_atom(false), args; + if (is("punc", "(")) { + next(); + args = expr_list(")"); + } else { + args = []; + } + return subscripts(new AST_New({ + start : start, + expression : newexp, + args : args, + end : prev() + }), allow_calls); + }; + + function as_atom_node() { + var tok = S.token, ret; + switch (tok.type) { + case "name": + case "keyword": + ret = _make_symbol(AST_SymbolRef); + break; + case "num": + ret = new AST_Number({ start: tok, end: tok, value: tok.value }); + break; + case "string": + ret = new AST_String({ + start : tok, + end : tok, + value : tok.value, + quote : tok.quote + }); + break; + case "regexp": + ret = new AST_RegExp({ start: tok, end: tok, value: tok.value }); + break; + case "atom": + switch (tok.value) { + case "false": + ret = new AST_False({ start: tok, end: tok }); + break; + case "true": + ret = new AST_True({ start: tok, end: tok }); + break; + case "null": + ret = new AST_Null({ start: tok, end: tok }); + break; + } + break; + case "operator": + if (!is_identifier_string(tok.value)) { + croak("SyntaxError: Invalid getter/setter name: " + tok.value, + tok.line, tok.col, tok.pos); + } + ret = _make_symbol(AST_SymbolRef); + break; + } + next(); + return ret; + }; + + var expr_atom = function(allow_calls) { + if (is("operator", "new")) { + return new_(allow_calls); + } + var start = S.token; + if (is("punc")) { + switch (start.value) { + case "(": + next(); + var ex = expression(true); + ex.start = start; + ex.end = S.token; + expect(")"); + return subscripts(ex, allow_calls); + case "[": + return subscripts(array_(), allow_calls); + case "{": + return subscripts(object_(), allow_calls); + } + unexpected(); + } + if (is("keyword", "function")) { + next(); + var func = function_(AST_Function); + func.start = start; + func.end = prev(); + return subscripts(func, allow_calls); + } + if (ATOMIC_START_TOKEN[S.token.type]) { + return subscripts(as_atom_node(), allow_calls); + } + unexpected(); + }; + + function expr_list(closing, allow_trailing_comma, allow_empty) { + var first = true, a = []; + while (!is("punc", closing)) { + if (first) first = false; else expect(","); + if (allow_trailing_comma && is("punc", closing)) break; + if (is("punc", ",") && allow_empty) { + a.push(new AST_Hole({ start: S.token, end: S.token })); + } else { + a.push(expression(false)); + } + } + next(); + return a; + }; + + var array_ = embed_tokens(function() { + expect("["); + return new AST_Array({ + elements: expr_list("]", !options.strict, true) + }); + }); + + var object_ = embed_tokens(function() { + expect("{"); + var first = true, a = []; + while (!is("punc", "}")) { + if (first) first = false; else expect(","); + if (!options.strict && is("punc", "}")) + // allow trailing comma + break; + var start = S.token; + var type = start.type; + var name = as_property_name(); + if (type == "name" && !is("punc", ":")) { + if (name == "get") { + a.push(new AST_ObjectGetter({ + start : start, + key : as_atom_node(), + value : function_(AST_Accessor), + end : prev() + })); + continue; + } + if (name == "set") { + a.push(new AST_ObjectSetter({ + start : start, + key : as_atom_node(), + value : function_(AST_Accessor), + end : prev() + })); + continue; + } + } + expect(":"); + a.push(new AST_ObjectKeyVal({ + start : start, + quote : start.quote, + key : name, + value : expression(false), + end : prev() + })); + } + next(); + return new AST_Object({ properties: a }); + }); + + function as_property_name() { + var tmp = S.token; + next(); + switch (tmp.type) { + case "num": + case "string": + case "name": + case "operator": + case "keyword": + case "atom": + return tmp.value; + default: + unexpected(); + } + }; + + function as_name() { + var tmp = S.token; + next(); + switch (tmp.type) { + case "name": + case "operator": + case "keyword": + case "atom": + return tmp.value; + default: + unexpected(); + } + }; + + function _make_symbol(type) { + var name = S.token.value; + return new (name == "this" ? AST_This : type)({ + name : String(name), + start : S.token, + end : S.token + }); + }; + + function as_symbol(type, noerror) { + if (!is("name")) { + if (!noerror) croak("SyntaxError: Name expected"); + return null; + } + var sym = _make_symbol(type); + next(); + return sym; + }; + + var subscripts = function(expr, allow_calls) { + var start = expr.start; + if (is("punc", ".")) { + next(); + return subscripts(new AST_Dot({ + start : start, + expression : expr, + property : as_name(), + end : prev() + }), allow_calls); + } + if (is("punc", "[")) { + next(); + var prop = expression(true); + expect("]"); + return subscripts(new AST_Sub({ + start : start, + expression : expr, + property : prop, + end : prev() + }), allow_calls); + } + if (allow_calls && is("punc", "(")) { + next(); + return subscripts(new AST_Call({ + start : start, + expression : expr, + args : expr_list(")"), + end : prev() + }), true); + } + return expr; + }; + + var maybe_unary = function(allow_calls) { + var start = S.token; + if (is("operator") && UNARY_PREFIX(start.value)) { + next(); + handle_regexp(); + var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls)); + ex.start = start; + ex.end = prev(); + return ex; + } + var val = expr_atom(allow_calls); + while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) { + val = make_unary(AST_UnaryPostfix, S.token.value, val); + val.start = start; + val.end = S.token; + next(); + } + return val; + }; + + function make_unary(ctor, op, expr) { + if ((op == "++" || op == "--") && !is_assignable(expr)) + croak("SyntaxError: Invalid use of " + op + " operator"); + return new ctor({ operator: op, expression: expr }); + }; + + var expr_op = function(left, min_prec, no_in) { + var op = is("operator") ? S.token.value : null; + if (op == "in" && no_in) op = null; + var prec = op != null ? PRECEDENCE[op] : null; + if (prec != null && prec > min_prec) { + next(); + var right = expr_op(maybe_unary(true), prec, no_in); + return expr_op(new AST_Binary({ + start : left.start, + left : left, + operator : op, + right : right, + end : right.end + }), min_prec, no_in); + } + return left; + }; + + function expr_ops(no_in) { + return expr_op(maybe_unary(true), 0, no_in); + }; + + var maybe_conditional = function(no_in) { + var start = S.token; + var expr = expr_ops(no_in); + if (is("operator", "?")) { + next(); + var yes = expression(false); + expect(":"); + return new AST_Conditional({ + start : start, + condition : expr, + consequent : yes, + alternative : expression(false, no_in), + end : prev() + }); + } + return expr; + }; + + function is_assignable(expr) { + if (!options.strict) return true; + if (expr instanceof AST_This) return false; + return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol); + }; + + var maybe_assign = function(no_in) { + var start = S.token; + var left = maybe_conditional(no_in), val = S.token.value; + if (is("operator") && ASSIGNMENT(val)) { + if (is_assignable(left)) { + next(); + return new AST_Assign({ + start : start, + left : left, + operator : val, + right : maybe_assign(no_in), + end : prev() + }); + } + croak("SyntaxError: Invalid assignment"); + } + return left; + }; + + var expression = function(commas, no_in) { + var start = S.token; + var expr = maybe_assign(no_in); + if (commas && is("punc", ",")) { + next(); + return new AST_Seq({ + start : start, + car : expr, + cdr : expression(true, no_in), + end : peek() + }); + } + return expr; + }; + + function in_loop(cont) { + ++S.in_loop; + var ret = cont(); + --S.in_loop; + return ret; + }; + + if (options.expression) { + return expression(true); + } + + return (function(){ + var start = S.token; + var body = []; + S.input.push_directives_stack(); + while (!is("eof")) + body.push(statement()); + S.input.pop_directives_stack(); + var end = prev(); + var toplevel = options.toplevel; + if (toplevel) { + toplevel.body = toplevel.body.concat(body); + toplevel.end = end; + } else { + toplevel = new AST_Toplevel({ start: start, body: body, end: end }); + } + return toplevel; + })(); + +}; + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +// Tree transformer helpers. + +function TreeTransformer(before, after) { + TreeWalker.call(this); + this.before = before; + this.after = after; +} +TreeTransformer.prototype = new TreeWalker; + +(function(undefined){ + + function _(node, descend) { + node.DEFMETHOD("transform", function(tw, in_list){ + var x, y; + tw.push(this); + if (tw.before) x = tw.before(this, descend, in_list); + if (x === undefined) { + if (!tw.after) { + x = this; + descend(x, tw); + } else { + tw.stack[tw.stack.length - 1] = x = this; + descend(x, tw); + y = tw.after(x, in_list); + if (y !== undefined) x = y; + } + } + tw.pop(this); + return x; + }); + }; + + function do_list(list, tw) { + return MAP(list, function(node){ + return node.transform(tw, true); + }); + }; + + _(AST_Node, noop); + + _(AST_LabeledStatement, function(self, tw){ + self.label = self.label.transform(tw); + self.body = self.body.transform(tw); + }); + + _(AST_SimpleStatement, function(self, tw){ + self.body = self.body.transform(tw); + }); + + _(AST_Block, function(self, tw){ + self.body = do_list(self.body, tw); + }); + + _(AST_DWLoop, function(self, tw){ + self.condition = self.condition.transform(tw); + self.body = self.body.transform(tw); + }); + + _(AST_For, function(self, tw){ + if (self.init) self.init = self.init.transform(tw); + if (self.condition) self.condition = self.condition.transform(tw); + if (self.step) self.step = self.step.transform(tw); + self.body = self.body.transform(tw); + }); + + _(AST_ForIn, function(self, tw){ + self.init = self.init.transform(tw); + self.object = self.object.transform(tw); + self.body = self.body.transform(tw); + }); + + _(AST_With, function(self, tw){ + self.expression = self.expression.transform(tw); + self.body = self.body.transform(tw); + }); + + _(AST_Exit, function(self, tw){ + if (self.value) self.value = self.value.transform(tw); + }); + + _(AST_LoopControl, function(self, tw){ + if (self.label) self.label = self.label.transform(tw); + }); + + _(AST_If, function(self, tw){ + self.condition = self.condition.transform(tw); + self.body = self.body.transform(tw); + if (self.alternative) self.alternative = self.alternative.transform(tw); + }); + + _(AST_Switch, function(self, tw){ + self.expression = self.expression.transform(tw); + self.body = do_list(self.body, tw); + }); + + _(AST_Case, function(self, tw){ + self.expression = self.expression.transform(tw); + self.body = do_list(self.body, tw); + }); + + _(AST_Try, function(self, tw){ + self.body = do_list(self.body, tw); + if (self.bcatch) self.bcatch = self.bcatch.transform(tw); + if (self.bfinally) self.bfinally = self.bfinally.transform(tw); + }); + + _(AST_Catch, function(self, tw){ + self.argname = self.argname.transform(tw); + self.body = do_list(self.body, tw); + }); + + _(AST_Definitions, function(self, tw){ + self.definitions = do_list(self.definitions, tw); + }); + + _(AST_VarDef, function(self, tw){ + self.name = self.name.transform(tw); + if (self.value) self.value = self.value.transform(tw); + }); + + _(AST_Lambda, function(self, tw){ + if (self.name) self.name = self.name.transform(tw); + self.argnames = do_list(self.argnames, tw); + self.body = do_list(self.body, tw); + }); + + _(AST_Call, function(self, tw){ + self.expression = self.expression.transform(tw); + self.args = do_list(self.args, tw); + }); + + _(AST_Seq, function(self, tw){ + self.car = self.car.transform(tw); + self.cdr = self.cdr.transform(tw); + }); + + _(AST_Dot, function(self, tw){ + self.expression = self.expression.transform(tw); + }); + + _(AST_Sub, function(self, tw){ + self.expression = self.expression.transform(tw); + self.property = self.property.transform(tw); + }); + + _(AST_Unary, function(self, tw){ + self.expression = self.expression.transform(tw); + }); + + _(AST_Binary, function(self, tw){ + self.left = self.left.transform(tw); + self.right = self.right.transform(tw); + }); + + _(AST_Conditional, function(self, tw){ + self.condition = self.condition.transform(tw); + self.consequent = self.consequent.transform(tw); + self.alternative = self.alternative.transform(tw); + }); + + _(AST_Array, function(self, tw){ + self.elements = do_list(self.elements, tw); + }); + + _(AST_Object, function(self, tw){ + self.properties = do_list(self.properties, tw); + }); + + _(AST_ObjectProperty, function(self, tw){ + self.value = self.value.transform(tw); + }); + +})(); + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +function SymbolDef(scope, index, orig) { + this.name = orig.name; + this.orig = [ orig ]; + this.scope = scope; + this.references = []; + this.global = false; + this.mangled_name = null; + this.undeclared = false; + this.constant = false; + this.index = index; + this.id = SymbolDef.next_id++; +}; + +SymbolDef.next_id = 1; + +SymbolDef.prototype = { + unmangleable: function(options) { + if (!options) options = {}; + + return (this.global && !options.toplevel) + || this.undeclared + || (!options.eval && (this.scope.uses_eval || this.scope.uses_with)) + || (options.keep_fnames + && (this.orig[0] instanceof AST_SymbolLambda + || this.orig[0] instanceof AST_SymbolDefun)); + }, + mangle: function(options) { + var cache = options.cache && options.cache.props; + if (this.global && cache && cache.has(this.name)) { + this.mangled_name = cache.get(this.name); + } + else if (!this.mangled_name && !this.unmangleable(options)) { + var s = this.scope; + if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda) + s = s.parent_scope; + this.mangled_name = s.next_mangled(options, this); + if (this.global && cache) { + cache.set(this.name, this.mangled_name); + } + } + } +}; + +AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ + options = defaults(options, { + screw_ie8: true, + cache: null + }); + + // pass 1: setup scope chaining and handle definitions + var self = this; + var scope = self.parent_scope = null; + var labels = new Dictionary(); + var defun = null; + var last_var_had_const_pragma = false; + var nesting = 0; + var tw = new TreeWalker(function(node, descend){ + if (options.screw_ie8 && node instanceof AST_Catch) { + var save_scope = scope; + scope = new AST_Scope(node); + scope.init_scope_vars(nesting); + scope.parent_scope = save_scope; + descend(); + scope = save_scope; + return true; + } + if (node instanceof AST_Scope) { + node.init_scope_vars(nesting); + var save_scope = node.parent_scope = scope; + var save_defun = defun; + var save_labels = labels; + defun = scope = node; + labels = new Dictionary(); + ++nesting; descend(); --nesting; + scope = save_scope; + defun = save_defun; + labels = save_labels; + return true; // don't descend again in TreeWalker + } + if (node instanceof AST_LabeledStatement) { + var l = node.label; + if (labels.has(l.name)) { + throw new Error(string_template("Label {name} defined twice", l)); + } + labels.set(l.name, l); + descend(); + labels.del(l.name); + return true; // no descend again + } + if (node instanceof AST_With) { + for (var s = scope; s; s = s.parent_scope) + s.uses_with = true; + return; + } + if (node instanceof AST_Symbol) { + node.scope = scope; + } + if (node instanceof AST_Label) { + node.thedef = node; + node.references = []; + } + if (node instanceof AST_SymbolLambda) { + defun.def_function(node); + } + else if (node instanceof AST_SymbolDefun) { + // Careful here, the scope where this should be defined is + // the parent scope. The reason is that we enter a new + // scope when we encounter the AST_Defun node (which is + // instanceof AST_Scope) but we get to the symbol a bit + // later. + (node.scope = defun.parent_scope).def_function(node); + } + else if (node instanceof AST_Var) { + last_var_had_const_pragma = node.has_const_pragma(); + } + else if (node instanceof AST_SymbolVar + || node instanceof AST_SymbolConst) { + var def = defun.def_variable(node); + def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma; + def.init = tw.parent().value; + } + else if (node instanceof AST_SymbolCatch) { + (options.screw_ie8 ? scope : defun) + .def_variable(node); + } + else if (node instanceof AST_LabelRef) { + var sym = labels.get(node.name); + if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", { + name: node.name, + line: node.start.line, + col: node.start.col + })); + node.thedef = sym; + } + }); + self.walk(tw); + + // pass 2: find back references and eval + var func = null; + var globals = self.globals = new Dictionary(); + var tw = new TreeWalker(function(node, descend){ + if (node instanceof AST_Lambda) { + var prev_func = func; + func = node; + descend(); + func = prev_func; + return true; + } + if (node instanceof AST_LoopControl && node.label) { + node.label.thedef.references.push(node); + return true; + } + if (node instanceof AST_SymbolRef) { + var name = node.name; + if (name == "eval" && tw.parent() instanceof AST_Call) { + for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { + s.uses_eval = true; + } + } + var sym = node.scope.find_variable(name); + if (!sym) { + var g; + if (globals.has(name)) { + g = globals.get(name); + } else { + g = new SymbolDef(self, globals.size(), node); + g.undeclared = true; + g.global = true; + globals.set(name, g); + } + node.thedef = g; + if (func && name == "arguments") { + func.uses_arguments = true; + } + } else { + node.thedef = sym; + } + node.reference(); + return true; + } + }); + self.walk(tw); + + if (options.cache) { + this.cname = options.cache.cname; + } +}); + +AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){ + this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) + this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) + this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement + this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` + this.parent_scope = null; // the parent scope + this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes + this.cname = -1; // the current index for mangling functions/variables + this.nesting = nesting; // the nesting level of this scope (0 means toplevel) +}); + +AST_Lambda.DEFMETHOD("init_scope_vars", function(){ + AST_Scope.prototype.init_scope_vars.apply(this, arguments); + this.uses_arguments = false; + + var symbol = new AST_VarDef({ name: "arguments", start: this.start, end: this.end }); + var def = new SymbolDef(this, this.variables.size(), symbol); + this.variables.set(symbol.name, def); +}); + +AST_SymbolRef.DEFMETHOD("reference", function() { + var def = this.definition(); + def.references.push(this); + var s = this.scope; + while (s) { + push_uniq(s.enclosed, def); + if (s === def.scope) break; + s = s.parent_scope; + } + this.frame = this.scope.nesting - def.scope.nesting; +}); + +AST_Scope.DEFMETHOD("find_variable", function(name){ + if (name instanceof AST_Symbol) name = name.name; + return this.variables.get(name) + || (this.parent_scope && this.parent_scope.find_variable(name)); +}); + +AST_Scope.DEFMETHOD("def_function", function(symbol){ + this.functions.set(symbol.name, this.def_variable(symbol)); +}); + +AST_Scope.DEFMETHOD("def_variable", function(symbol){ + var def; + if (!this.variables.has(symbol.name)) { + def = new SymbolDef(this, this.variables.size(), symbol); + this.variables.set(symbol.name, def); + def.global = !this.parent_scope; + } else { + def = this.variables.get(symbol.name); + def.orig.push(symbol); + } + return symbol.thedef = def; +}); + +AST_Scope.DEFMETHOD("next_mangled", function(options){ + var ext = this.enclosed; + out: while (true) { + var m = base54(++this.cname); + if (!is_identifier(m)) continue; // skip over "do" + + // https://github.com/mishoo/UglifyJS2/issues/242 -- do not + // shadow a name excepted from mangling. + if (options.except.indexOf(m) >= 0) continue; + + // we must ensure that the mangled name does not shadow a name + // from some parent scope that is referenced in this or in + // inner scopes. + for (var i = ext.length; --i >= 0;) { + var sym = ext[i]; + var name = sym.mangled_name || (sym.unmangleable(options) && sym.name); + if (m == name) continue out; + } + return m; + } +}); + +AST_Function.DEFMETHOD("next_mangled", function(options, def){ + // #179, #326 + // in Safari strict mode, something like (function x(x){...}) is a syntax error; + // a function expression's argument cannot shadow the function expression's name + + var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition(); + + // the function's mangled_name is null when keep_fnames is true + var tricky_name = tricky_def ? tricky_def.mangled_name || tricky_def.name : null; + + while (true) { + var name = AST_Lambda.prototype.next_mangled.call(this, options, def); + if (!tricky_name || tricky_name != name) + return name; + } +}); + +AST_Scope.DEFMETHOD("references", function(sym){ + if (sym instanceof AST_Symbol) sym = sym.definition(); + return this.enclosed.indexOf(sym) < 0 ? null : sym; +}); + +AST_Symbol.DEFMETHOD("unmangleable", function(options){ + return this.definition().unmangleable(options); +}); + +// property accessors are not mangleable +AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){ + return true; +}); + +// labels are always mangleable +AST_Label.DEFMETHOD("unmangleable", function(){ + return false; +}); + +AST_Symbol.DEFMETHOD("unreferenced", function(){ + return this.definition().references.length == 0 + && !(this.scope.uses_eval || this.scope.uses_with); +}); + +AST_Symbol.DEFMETHOD("undeclared", function(){ + return this.definition().undeclared; +}); + +AST_LabelRef.DEFMETHOD("undeclared", function(){ + return false; +}); + +AST_Label.DEFMETHOD("undeclared", function(){ + return false; +}); + +AST_Symbol.DEFMETHOD("definition", function(){ + return this.thedef; +}); + +AST_Symbol.DEFMETHOD("global", function(){ + return this.definition().global; +}); + +AST_Var.DEFMETHOD("has_const_pragma", function() { + var comments_before = this.start && this.start.comments_before; + var lastComment = comments_before && comments_before[comments_before.length - 1]; + return lastComment && /@const\b/.test(lastComment.value); +}); + +AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ + return defaults(options, { + except : [], + eval : false, + sort : false, // Ignored. Flag retained for backwards compatibility. + toplevel : false, + screw_ie8 : true, + keep_fnames : false + }); +}); + +AST_Toplevel.DEFMETHOD("mangle_names", function(options){ + options = this._default_mangler_options(options); + + // Never mangle arguments + options.except.push('arguments'); + + // We only need to mangle declaration nodes. Special logic wired + // into the code generator will display the mangled name if it's + // present (and for AST_SymbolRef-s it'll use the mangled name of + // the AST_SymbolDeclaration that it points to). + var lname = -1; + var to_mangle = []; + + if (options.cache) { + this.globals.each(function(symbol){ + if (options.except.indexOf(symbol.name) < 0) { + to_mangle.push(symbol); + } + }); + } + + var tw = new TreeWalker(function(node, descend){ + if (node instanceof AST_LabeledStatement) { + // lname is incremented when we get to the AST_Label + var save_nesting = lname; + descend(); + lname = save_nesting; + return true; // don't descend again in TreeWalker + } + if (node instanceof AST_Scope) { + var p = tw.parent(), a = []; + node.variables.each(function(symbol){ + if (options.except.indexOf(symbol.name) < 0) { + a.push(symbol); + } + }); + to_mangle.push.apply(to_mangle, a); + return; + } + if (node instanceof AST_Label) { + var name; + do name = base54(++lname); while (!is_identifier(name)); + node.mangled_name = name; + return true; + } + if (options.screw_ie8 && node instanceof AST_SymbolCatch) { + to_mangle.push(node.definition()); + return; + } + }); + this.walk(tw); + to_mangle.forEach(function(def){ def.mangle(options) }); + + if (options.cache) { + options.cache.cname = this.cname; + } +}); + +AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ + options = this._default_mangler_options(options); + var tw = new TreeWalker(function(node){ + if (node instanceof AST_Constant) + base54.consider(node.print_to_string()); + else if (node instanceof AST_Return) + base54.consider("return"); + else if (node instanceof AST_Throw) + base54.consider("throw"); + else if (node instanceof AST_Continue) + base54.consider("continue"); + else if (node instanceof AST_Break) + base54.consider("break"); + else if (node instanceof AST_Debugger) + base54.consider("debugger"); + else if (node instanceof AST_Directive) + base54.consider(node.value); + else if (node instanceof AST_While) + base54.consider("while"); + else if (node instanceof AST_Do) + base54.consider("do while"); + else if (node instanceof AST_If) { + base54.consider("if"); + if (node.alternative) base54.consider("else"); + } + else if (node instanceof AST_Var) + base54.consider("var"); + else if (node instanceof AST_Const) + base54.consider("const"); + else if (node instanceof AST_Lambda) + base54.consider("function"); + else if (node instanceof AST_For) + base54.consider("for"); + else if (node instanceof AST_ForIn) + base54.consider("for in"); + else if (node instanceof AST_Switch) + base54.consider("switch"); + else if (node instanceof AST_Case) + base54.consider("case"); + else if (node instanceof AST_Default) + base54.consider("default"); + else if (node instanceof AST_With) + base54.consider("with"); + else if (node instanceof AST_ObjectSetter) + base54.consider("set" + node.key); + else if (node instanceof AST_ObjectGetter) + base54.consider("get" + node.key); + else if (node instanceof AST_ObjectKeyVal) + base54.consider(node.key); + else if (node instanceof AST_New) + base54.consider("new"); + else if (node instanceof AST_This) + base54.consider("this"); + else if (node instanceof AST_Try) + base54.consider("try"); + else if (node instanceof AST_Catch) + base54.consider("catch"); + else if (node instanceof AST_Finally) + base54.consider("finally"); + else if (node instanceof AST_Symbol && node.unmangleable(options)) + base54.consider(node.name); + else if (node instanceof AST_Unary || node instanceof AST_Binary) + base54.consider(node.operator); + else if (node instanceof AST_Dot) + base54.consider(node.property); + }); + this.walk(tw); + base54.sort(); +}); + +var base54 = (function() { + var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789"; + var chars, frequency; + function reset() { + frequency = Object.create(null); + chars = string.split("").map(function(ch){ return ch.charCodeAt(0) }); + chars.forEach(function(ch){ frequency[ch] = 0 }); + } + base54.consider = function(str){ + for (var i = str.length; --i >= 0;) { + var code = str.charCodeAt(i); + if (code in frequency) ++frequency[code]; + } + }; + base54.sort = function() { + chars = mergeSort(chars, function(a, b){ + if (is_digit(a) && !is_digit(b)) return 1; + if (is_digit(b) && !is_digit(a)) return -1; + return frequency[b] - frequency[a]; + }); + }; + base54.reset = reset; + reset(); + base54.get = function(){ return chars }; + base54.freq = function(){ return frequency }; + function base54(num) { + var ret = "", base = 54; + num++; + do { + num--; + ret += String.fromCharCode(chars[num % base]); + num = Math.floor(num / base); + base = 64; + } while (num > 0); + return ret; + }; + return base54; +})(); + +AST_Toplevel.DEFMETHOD("scope_warnings", function(options){ + options = defaults(options, { + undeclared : false, // this makes a lot of noise + unreferenced : true, + assign_to_global : true, + func_arguments : true, + nested_defuns : true, + eval : true + }); + var tw = new TreeWalker(function(node){ + if (options.undeclared + && node instanceof AST_SymbolRef + && node.undeclared()) + { + // XXX: this also warns about JS standard names, + // i.e. Object, Array, parseInt etc. Should add a list of + // exceptions. + AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", { + name: node.name, + file: node.start.file, + line: node.start.line, + col: node.start.col + }); + } + if (options.assign_to_global) + { + var sym = null; + if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) + sym = node.left; + else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef) + sym = node.init; + if (sym + && (sym.undeclared() + || (sym.global() && sym.scope !== sym.definition().scope))) { + AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", { + msg: sym.undeclared() ? "Accidental global?" : "Assignment to global", + name: sym.name, + file: sym.start.file, + line: sym.start.line, + col: sym.start.col + }); + } + } + if (options.eval + && node instanceof AST_SymbolRef + && node.undeclared() + && node.name == "eval") { + AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start); + } + if (options.unreferenced + && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label) + && !(node instanceof AST_SymbolCatch) + && node.unreferenced()) { + AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", { + type: node instanceof AST_Label ? "Label" : "Symbol", + name: node.name, + file: node.start.file, + line: node.start.line, + col: node.start.col + }); + } + if (options.func_arguments + && node instanceof AST_Lambda + && node.uses_arguments) { + AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", { + name: node.name ? node.name.name : "anonymous", + file: node.start.file, + line: node.start.line, + col: node.start.col + }); + } + if (options.nested_defuns + && node instanceof AST_Defun + && !(tw.parent() instanceof AST_Scope)) { + AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", { + name: node.name.name, + type: tw.parent().TYPE, + file: node.start.file, + line: node.start.line, + col: node.start.col + }); + } + }); + this.walk(tw); +}); + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/; + +function OutputStream(options) { + + options = defaults(options, { + indent_start : 0, + indent_level : 4, + quote_keys : false, + space_colon : true, + ascii_only : false, + unescape_regexps : false, + inline_script : false, + width : 80, + max_line_len : 32000, + beautify : false, + source_map : null, + bracketize : false, + semicolons : true, + comments : false, + shebang : true, + preserve_line : false, + screw_ie8 : true, + preamble : null, + quote_style : 0, + keep_quoted_props: false + }, true); + + var indentation = 0; + var current_col = 0; + var current_line = 1; + var current_pos = 0; + var OUTPUT = ""; + + function to_ascii(str, identifier) { + return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) { + var code = ch.charCodeAt(0).toString(16); + if (code.length <= 2 && !identifier) { + while (code.length < 2) code = "0" + code; + return "\\x" + code; + } else { + while (code.length < 4) code = "0" + code; + return "\\u" + code; + } + }); + }; + + function make_string(str, quote) { + var dq = 0, sq = 0; + str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, + function(s, i){ + switch (s) { + case '"': ++dq; return '"'; + case "'": ++sq; return "'"; + case "\\": return "\\\\"; + case "\n": return "\\n"; + case "\r": return "\\r"; + case "\t": return "\\t"; + case "\b": return "\\b"; + case "\f": return "\\f"; + case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B"; + case "\u2028": return "\\u2028"; + case "\u2029": return "\\u2029"; + case "\ufeff": return "\\ufeff"; + case "\0": + return /[0-7]/.test(str.charAt(i+1)) ? "\\x00" : "\\0"; + } + return s; + }); + function quote_single() { + return "'" + str.replace(/\x27/g, "\\'") + "'"; + } + function quote_double() { + return '"' + str.replace(/\x22/g, '\\"') + '"'; + } + if (options.ascii_only) str = to_ascii(str); + switch (options.quote_style) { + case 1: + return quote_single(); + case 2: + return quote_double(); + case 3: + return quote == "'" ? quote_single() : quote_double(); + default: + return dq > sq ? quote_single() : quote_double(); + } + }; + + function encode_string(str, quote) { + var ret = make_string(str, quote); + if (options.inline_script) { + ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1"); + ret = ret.replace(/\x3c!--/g, "\\x3c!--"); + ret = ret.replace(/--\x3e/g, "--\\x3e"); + } + return ret; + }; + + function make_name(name) { + name = name.toString(); + if (options.ascii_only) + name = to_ascii(name, true); + return name; + }; + + function make_indent(back) { + return repeat_string(" ", options.indent_start + indentation - back * options.indent_level); + }; + + /* -----[ beautification/minification ]----- */ + + var might_need_space = false; + var might_need_semicolon = false; + var last = null; + + function last_char() { + return last.charAt(last.length - 1); + }; + + function maybe_newline() { + if (options.max_line_len && current_col > options.max_line_len) + print("\n"); + }; + + var requireSemicolonChars = makePredicate("( [ + * / - , ."); + + function print(str) { + str = String(str); + var ch = str.charAt(0); + if (might_need_semicolon) { + might_need_semicolon = false; + + if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) { + if (options.semicolons || requireSemicolonChars(ch)) { + OUTPUT += ";"; + current_col++; + current_pos++; + } else { + OUTPUT += "\n"; + current_pos++; + current_line++; + current_col = 0; + + if (/^\s+$/.test(str)) { + // reset the semicolon flag, since we didn't print one + // now and might still have to later + might_need_semicolon = true; + } + } + + if (!options.beautify) + might_need_space = false; + } + } + + if (!options.beautify && options.preserve_line && stack[stack.length - 1]) { + var target_line = stack[stack.length - 1].start.line; + while (current_line < target_line) { + OUTPUT += "\n"; + current_pos++; + current_line++; + current_col = 0; + might_need_space = false; + } + } + + if (might_need_space) { + var prev = last_char(); + if ((is_identifier_char(prev) + && (is_identifier_char(ch) || ch == "\\")) + || (/^[\+\-\/]$/.test(ch) && ch == prev)) + { + OUTPUT += " "; + current_col++; + current_pos++; + } + might_need_space = false; + } + var a = str.split(/\r?\n/), n = a.length - 1; + current_line += n; + if (n == 0) { + current_col += a[n].length; + } else { + current_col = a[n].length; + } + current_pos += str.length; + last = str; + OUTPUT += str; + }; + + var space = options.beautify ? function() { + print(" "); + } : function() { + might_need_space = true; + }; + + var indent = options.beautify ? function(half) { + if (options.beautify) { + print(make_indent(half ? 0.5 : 0)); + } + } : noop; + + var with_indent = options.beautify ? function(col, cont) { + if (col === true) col = next_indent(); + var save_indentation = indentation; + indentation = col; + var ret = cont(); + indentation = save_indentation; + return ret; + } : function(col, cont) { return cont() }; + + var newline = options.beautify ? function() { + print("\n"); + } : maybe_newline; + + var semicolon = options.beautify ? function() { + print(";"); + } : function() { + might_need_semicolon = true; + }; + + function force_semicolon() { + might_need_semicolon = false; + print(";"); + }; + + function next_indent() { + return indentation + options.indent_level; + }; + + function with_block(cont) { + var ret; + print("{"); + newline(); + with_indent(next_indent(), function(){ + ret = cont(); + }); + indent(); + print("}"); + return ret; + }; + + function with_parens(cont) { + print("("); + //XXX: still nice to have that for argument lists + //var ret = with_indent(current_col, cont); + var ret = cont(); + print(")"); + return ret; + }; + + function with_square(cont) { + print("["); + //var ret = with_indent(current_col, cont); + var ret = cont(); + print("]"); + return ret; + }; + + function comma() { + print(","); + space(); + }; + + function colon() { + print(":"); + if (options.space_colon) space(); + }; + + var add_mapping = options.source_map ? function(token, name) { + try { + if (token) options.source_map.add( + token.file || "?", + current_line, current_col, + token.line, token.col, + (!name && token.type == "name") ? token.value : name + ); + } catch(ex) { + AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", { + file: token.file, + line: token.line, + col: token.col, + cline: current_line, + ccol: current_col, + name: name || "" + }) + } + } : noop; + + function get() { + return OUTPUT; + }; + + if (options.preamble) { + print(options.preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n")); + } + + var stack = []; + return { + get : get, + toString : get, + indent : indent, + indentation : function() { return indentation }, + current_width : function() { return current_col - indentation }, + should_break : function() { return options.width && this.current_width() >= options.width }, + newline : newline, + print : print, + space : space, + comma : comma, + colon : colon, + last : function() { return last }, + semicolon : semicolon, + force_semicolon : force_semicolon, + to_ascii : to_ascii, + print_name : function(name) { print(make_name(name)) }, + print_string : function(str, quote, escape_directive) { + var encoded = encode_string(str, quote); + if (escape_directive === true && encoded.indexOf("\\") === -1) { + // Insert semicolons to break directive prologue + if (!EXPECT_DIRECTIVE.test(OUTPUT)) { + force_semicolon(); + } + force_semicolon(); + } + print(encoded); + }, + encode_string : encode_string, + next_indent : next_indent, + with_indent : with_indent, + with_block : with_block, + with_parens : with_parens, + with_square : with_square, + add_mapping : add_mapping, + option : function(opt) { return options[opt] }, + line : function() { return current_line }, + col : function() { return current_col }, + pos : function() { return current_pos }, + push_node : function(node) { stack.push(node) }, + pop_node : function() { return stack.pop() }, + stack : function() { return stack }, + parent : function(n) { + return stack[stack.length - 2 - (n || 0)]; + } + }; + +}; + +/* -----[ code generators ]----- */ + +(function(){ + + /* -----[ utils ]----- */ + + function DEFPRINT(nodetype, generator) { + nodetype.DEFMETHOD("_codegen", generator); + }; + + var use_asm = false; + var in_directive = false; + + AST_Node.DEFMETHOD("print", function(stream, force_parens){ + var self = this, generator = self._codegen, prev_use_asm = use_asm; + if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope) { + use_asm = true; + } + function doit() { + self.add_comments(stream); + self.add_source_map(stream); + generator(self, stream); + } + stream.push_node(self); + if (force_parens || self.needs_parens(stream)) { + stream.with_parens(doit); + } else { + doit(); + } + stream.pop_node(); + if (self instanceof AST_Scope) { + use_asm = prev_use_asm; + } + }); + + AST_Node.DEFMETHOD("print_to_string", function(options){ + var s = OutputStream(options); + if (!options) s._readonly = true; + this.print(s); + return s.get(); + }); + + /* -----[ comments ]----- */ + + AST_Node.DEFMETHOD("add_comments", function(output){ + if (output._readonly) return; + var c = output.option("comments"), self = this; + var start = self.start; + if (start && !start._comments_dumped) { + start._comments_dumped = true; + var comments = start.comments_before || []; + + // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112 + // and https://github.com/mishoo/UglifyJS2/issues/372 + if (self instanceof AST_Exit && self.value) { + self.value.walk(new TreeWalker(function(node){ + if (node.start && node.start.comments_before) { + comments = comments.concat(node.start.comments_before); + node.start.comments_before = []; + } + if (node instanceof AST_Function || + node instanceof AST_Array || + node instanceof AST_Object) + { + return true; // don't go inside. + } + })); + } + + if (!c) { + comments = comments.filter(function(comment) { + return comment.type == "comment5"; + }); + } else if (c.test) { + comments = comments.filter(function(comment){ + return comment.type == "comment5" || c.test(comment.value); + }); + } else if (typeof c == "function") { + comments = comments.filter(function(comment){ + return comment.type == "comment5" || c(self, comment); + }); + } + + // Keep single line comments after nlb, after nlb + if (!output.option("beautify") && comments.length > 0 && + /comment[134]/.test(comments[0].type) && + output.col() !== 0 && comments[0].nlb) + { + output.print("\n"); + } + + comments.forEach(function(c){ + if (/comment[134]/.test(c.type)) { + output.print("//" + c.value + "\n"); + output.indent(); + } + else if (c.type == "comment2") { + output.print("/*" + c.value + "*/"); + if (start.nlb) { + output.print("\n"); + output.indent(); + } else { + output.space(); + } + } + else if (output.pos() === 0 && c.type == "comment5" && output.option("shebang")) { + output.print("#!" + c.value + "\n"); + output.indent(); + } + }); + } + }); + + /* -----[ PARENTHESES ]----- */ + + function PARENS(nodetype, func) { + if (Array.isArray(nodetype)) { + nodetype.forEach(function(nodetype){ + PARENS(nodetype, func); + }); + } else { + nodetype.DEFMETHOD("needs_parens", func); + } + }; + + PARENS(AST_Node, function(){ + return false; + }); + + // a function expression needs parens around it when it's provably + // the first token to appear in a statement. + PARENS(AST_Function, function(output){ + return first_in_statement(output); + }); + + // same goes for an object literal, because otherwise it would be + // interpreted as a block of code. + PARENS(AST_Object, function(output){ + return first_in_statement(output); + }); + + PARENS([ AST_Unary, AST_Undefined ], function(output){ + var p = output.parent(); + return p instanceof AST_PropAccess && p.expression === this + || p instanceof AST_Call && p.expression === this; + }); + + PARENS(AST_Seq, function(output){ + var p = output.parent(); + return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4) + || p instanceof AST_Unary // !(foo, bar, baz) + || p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8 + || p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4 + || p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2 + || p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ] + || p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2 + || p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30) + * ==> 20 (side effect, set a := 10 and b := 20) */ + ; + }); + + PARENS(AST_Binary, function(output){ + var p = output.parent(); + // (foo && bar)() + if (p instanceof AST_Call && p.expression === this) + return true; + // typeof (foo && bar) + if (p instanceof AST_Unary) + return true; + // (foo && bar)["prop"], (foo && bar).prop + if (p instanceof AST_PropAccess && p.expression === this) + return true; + // this deals with precedence: 3 * (2 + 1) + if (p instanceof AST_Binary) { + var po = p.operator, pp = PRECEDENCE[po]; + var so = this.operator, sp = PRECEDENCE[so]; + if (pp > sp + || (pp == sp + && this === p.right)) { + return true; + } + } + }); + + PARENS(AST_PropAccess, function(output){ + var p = output.parent(); + if (p instanceof AST_New && p.expression === this) { + // i.e. new (foo.bar().baz) + // + // if there's one call into this subtree, then we need + // parens around it too, otherwise the call will be + // interpreted as passing the arguments to the upper New + // expression. + try { + this.walk(new TreeWalker(function(node){ + if (node instanceof AST_Call) throw p; + })); + } catch(ex) { + if (ex !== p) throw ex; + return true; + } + } + }); + + PARENS(AST_Call, function(output){ + var p = output.parent(), p1; + if (p instanceof AST_New && p.expression === this) + return true; + + // workaround for Safari bug. + // https://bugs.webkit.org/show_bug.cgi?id=123506 + return this.expression instanceof AST_Function + && p instanceof AST_PropAccess + && p.expression === this + && (p1 = output.parent(1)) instanceof AST_Assign + && p1.left === p; + }); + + PARENS(AST_New, function(output){ + var p = output.parent(); + if (!need_constructor_parens(this, output) + && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]() + || p instanceof AST_Call && p.expression === this)) // (new foo)(bar) + return true; + }); + + PARENS(AST_Number, function(output){ + var p = output.parent(); + if (p instanceof AST_PropAccess && p.expression === this) { + var value = this.getValue(); + if (value < 0 || /^0/.test(make_num(value))) { + return true; + } + } + }); + + PARENS([ AST_Assign, AST_Conditional ], function (output){ + var p = output.parent(); + // !(a = false) → true + if (p instanceof AST_Unary) + return true; + // 1 + (a = 2) + 3 → 6, side effect setting a = 2 + if (p instanceof AST_Binary && !(p instanceof AST_Assign)) + return true; + // (a = func)() —or— new (a = Object)() + if (p instanceof AST_Call && p.expression === this) + return true; + // (a = foo) ? bar : baz + if (p instanceof AST_Conditional && p.condition === this) + return true; + // (a = foo)["prop"] —or— (a = foo).prop + if (p instanceof AST_PropAccess && p.expression === this) + return true; + }); + + /* -----[ PRINTERS ]----- */ + + DEFPRINT(AST_Directive, function(self, output){ + output.print_string(self.value, self.quote); + output.semicolon(); + }); + DEFPRINT(AST_Debugger, function(self, output){ + output.print("debugger"); + output.semicolon(); + }); + + /* -----[ statements ]----- */ + + function display_body(body, is_toplevel, output, allow_directives) { + var last = body.length - 1; + in_directive = allow_directives; + body.forEach(function(stmt, i){ + if (in_directive === true && !(stmt instanceof AST_Directive || + stmt instanceof AST_EmptyStatement || + (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) + )) { + in_directive = false; + } + if (!(stmt instanceof AST_EmptyStatement)) { + output.indent(); + stmt.print(output); + if (!(i == last && is_toplevel)) { + output.newline(); + if (is_toplevel) output.newline(); + } + } + if (in_directive === true && + stmt instanceof AST_SimpleStatement && + stmt.body instanceof AST_String + ) { + in_directive = false; + } + }); + in_directive = false; + }; + + AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){ + force_statement(this.body, output); + }); + + DEFPRINT(AST_Statement, function(self, output){ + self.body.print(output); + output.semicolon(); + }); + DEFPRINT(AST_Toplevel, function(self, output){ + display_body(self.body, true, output, true); + output.print(""); + }); + DEFPRINT(AST_LabeledStatement, function(self, output){ + self.label.print(output); + output.colon(); + self.body.print(output); + }); + DEFPRINT(AST_SimpleStatement, function(self, output){ + self.body.print(output); + output.semicolon(); + }); + function print_bracketed(body, output, allow_directives) { + if (body.length > 0) output.with_block(function(){ + display_body(body, false, output, allow_directives); + }); + else output.print("{}"); + }; + DEFPRINT(AST_BlockStatement, function(self, output){ + print_bracketed(self.body, output); + }); + DEFPRINT(AST_EmptyStatement, function(self, output){ + output.semicolon(); + }); + DEFPRINT(AST_Do, function(self, output){ + output.print("do"); + output.space(); + self._do_print_body(output); + output.space(); + output.print("while"); + output.space(); + output.with_parens(function(){ + self.condition.print(output); + }); + output.semicolon(); + }); + DEFPRINT(AST_While, function(self, output){ + output.print("while"); + output.space(); + output.with_parens(function(){ + self.condition.print(output); + }); + output.space(); + self._do_print_body(output); + }); + DEFPRINT(AST_For, function(self, output){ + output.print("for"); + output.space(); + output.with_parens(function(){ + if (self.init && !(self.init instanceof AST_EmptyStatement)) { + if (self.init instanceof AST_Definitions) { + self.init.print(output); + } else { + parenthesize_for_noin(self.init, output, true); + } + output.print(";"); + output.space(); + } else { + output.print(";"); + } + if (self.condition) { + self.condition.print(output); + output.print(";"); + output.space(); + } else { + output.print(";"); + } + if (self.step) { + self.step.print(output); + } + }); + output.space(); + self._do_print_body(output); + }); + DEFPRINT(AST_ForIn, function(self, output){ + output.print("for"); + output.space(); + output.with_parens(function(){ + self.init.print(output); + output.space(); + output.print("in"); + output.space(); + self.object.print(output); + }); + output.space(); + self._do_print_body(output); + }); + DEFPRINT(AST_With, function(self, output){ + output.print("with"); + output.space(); + output.with_parens(function(){ + self.expression.print(output); + }); + output.space(); + self._do_print_body(output); + }); + + /* -----[ functions ]----- */ + AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){ + var self = this; + if (!nokeyword) { + output.print("function"); + } + if (self.name) { + output.space(); + self.name.print(output); + } + output.with_parens(function(){ + self.argnames.forEach(function(arg, i){ + if (i) output.comma(); + arg.print(output); + }); + }); + output.space(); + print_bracketed(self.body, output, true); + }); + DEFPRINT(AST_Lambda, function(self, output){ + self._do_print(output); + }); + + /* -----[ exits ]----- */ + AST_Exit.DEFMETHOD("_do_print", function(output, kind){ + output.print(kind); + if (this.value) { + output.space(); + this.value.print(output); + } + output.semicolon(); + }); + DEFPRINT(AST_Return, function(self, output){ + self._do_print(output, "return"); + }); + DEFPRINT(AST_Throw, function(self, output){ + self._do_print(output, "throw"); + }); + + /* -----[ loop control ]----- */ + AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){ + output.print(kind); + if (this.label) { + output.space(); + this.label.print(output); + } + output.semicolon(); + }); + DEFPRINT(AST_Break, function(self, output){ + self._do_print(output, "break"); + }); + DEFPRINT(AST_Continue, function(self, output){ + self._do_print(output, "continue"); + }); + + /* -----[ if ]----- */ + function make_then(self, output) { + if (output.option("bracketize")) { + make_block(self.body, output); + return; + } + // The squeezer replaces "block"-s that contain only a single + // statement with the statement itself; technically, the AST + // is correct, but this can create problems when we output an + // IF having an ELSE clause where the THEN clause ends in an + // IF *without* an ELSE block (then the outer ELSE would refer + // to the inner IF). This function checks for this case and + // adds the block brackets if needed. + if (!self.body) + return output.force_semicolon(); + if (self.body instanceof AST_Do) { + // Unconditionally use the if/do-while workaround for all browsers. + // https://github.com/mishoo/UglifyJS/issues/#issue/57 IE + // croaks with "syntax error" on code like this: if (foo) + // do ... while(cond); else ... we need block brackets + // around do/while + make_block(self.body, output); + return; + } + var b = self.body; + while (true) { + if (b instanceof AST_If) { + if (!b.alternative) { + make_block(self.body, output); + return; + } + b = b.alternative; + } + else if (b instanceof AST_StatementWithBody) { + b = b.body; + } + else break; + } + force_statement(self.body, output); + }; + DEFPRINT(AST_If, function(self, output){ + output.print("if"); + output.space(); + output.with_parens(function(){ + self.condition.print(output); + }); + output.space(); + if (self.alternative) { + make_then(self, output); + output.space(); + output.print("else"); + output.space(); + force_statement(self.alternative, output); + } else { + self._do_print_body(output); + } + }); + + /* -----[ switch ]----- */ + DEFPRINT(AST_Switch, function(self, output){ + output.print("switch"); + output.space(); + output.with_parens(function(){ + self.expression.print(output); + }); + output.space(); + if (self.body.length > 0) output.with_block(function(){ + self.body.forEach(function(stmt, i){ + if (i) output.newline(); + output.indent(true); + stmt.print(output); + }); + }); + else output.print("{}"); + }); + AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){ + if (this.body.length > 0) { + output.newline(); + this.body.forEach(function(stmt){ + output.indent(); + stmt.print(output); + output.newline(); + }); + } + }); + DEFPRINT(AST_Default, function(self, output){ + output.print("default:"); + self._do_print_body(output); + }); + DEFPRINT(AST_Case, function(self, output){ + output.print("case"); + output.space(); + self.expression.print(output); + output.print(":"); + self._do_print_body(output); + }); + + /* -----[ exceptions ]----- */ + DEFPRINT(AST_Try, function(self, output){ + output.print("try"); + output.space(); + print_bracketed(self.body, output); + if (self.bcatch) { + output.space(); + self.bcatch.print(output); + } + if (self.bfinally) { + output.space(); + self.bfinally.print(output); + } + }); + DEFPRINT(AST_Catch, function(self, output){ + output.print("catch"); + output.space(); + output.with_parens(function(){ + self.argname.print(output); + }); + output.space(); + print_bracketed(self.body, output); + }); + DEFPRINT(AST_Finally, function(self, output){ + output.print("finally"); + output.space(); + print_bracketed(self.body, output); + }); + + /* -----[ var/const ]----- */ + AST_Definitions.DEFMETHOD("_do_print", function(output, kind){ + output.print(kind); + output.space(); + this.definitions.forEach(function(def, i){ + if (i) output.comma(); + def.print(output); + }); + var p = output.parent(); + var in_for = p instanceof AST_For || p instanceof AST_ForIn; + var avoid_semicolon = in_for && p.init === this; + if (!avoid_semicolon) + output.semicolon(); + }); + DEFPRINT(AST_Var, function(self, output){ + self._do_print(output, "var"); + }); + DEFPRINT(AST_Const, function(self, output){ + self._do_print(output, "const"); + }); + + function parenthesize_for_noin(node, output, noin) { + if (!noin) node.print(output); + else try { + // need to take some precautions here: + // https://github.com/mishoo/UglifyJS2/issues/60 + node.walk(new TreeWalker(function(node){ + if (node instanceof AST_Binary && node.operator == "in") + throw output; + })); + node.print(output); + } catch(ex) { + if (ex !== output) throw ex; + node.print(output, true); + } + }; + + DEFPRINT(AST_VarDef, function(self, output){ + self.name.print(output); + if (self.value) { + output.space(); + output.print("="); + output.space(); + var p = output.parent(1); + var noin = p instanceof AST_For || p instanceof AST_ForIn; + parenthesize_for_noin(self.value, output, noin); + } + }); + + /* -----[ other expressions ]----- */ + DEFPRINT(AST_Call, function(self, output){ + self.expression.print(output); + if (self instanceof AST_New && !need_constructor_parens(self, output)) + return; + output.with_parens(function(){ + self.args.forEach(function(expr, i){ + if (i) output.comma(); + expr.print(output); + }); + }); + }); + DEFPRINT(AST_New, function(self, output){ + output.print("new"); + output.space(); + AST_Call.prototype._codegen(self, output); + }); + + AST_Seq.DEFMETHOD("_do_print", function(output){ + this.car.print(output); + if (this.cdr) { + output.comma(); + if (output.should_break()) { + output.newline(); + output.indent(); + } + this.cdr.print(output); + } + }); + DEFPRINT(AST_Seq, function(self, output){ + self._do_print(output); + // var p = output.parent(); + // if (p instanceof AST_Statement) { + // output.with_indent(output.next_indent(), function(){ + // self._do_print(output); + // }); + // } else { + // self._do_print(output); + // } + }); + DEFPRINT(AST_Dot, function(self, output){ + var expr = self.expression; + expr.print(output); + if (expr instanceof AST_Number && expr.getValue() >= 0) { + if (!/[xa-f.)]/i.test(output.last())) { + output.print("."); + } + } + output.print("."); + // the name after dot would be mapped about here. + output.add_mapping(self.end); + output.print_name(self.property); + }); + DEFPRINT(AST_Sub, function(self, output){ + self.expression.print(output); + output.print("["); + self.property.print(output); + output.print("]"); + }); + DEFPRINT(AST_UnaryPrefix, function(self, output){ + var op = self.operator; + output.print(op); + if (/^[a-z]/i.test(op) + || (/[+-]$/.test(op) + && self.expression instanceof AST_UnaryPrefix + && /^[+-]/.test(self.expression.operator))) { + output.space(); + } + self.expression.print(output); + }); + DEFPRINT(AST_UnaryPostfix, function(self, output){ + self.expression.print(output); + output.print(self.operator); + }); + DEFPRINT(AST_Binary, function(self, output){ + var op = self.operator; + self.left.print(output); + if (op[0] == ">" /* ">>" ">>>" ">" ">=" */ + && self.left instanceof AST_UnaryPostfix + && self.left.operator == "--") { + // space is mandatory to avoid outputting --> + output.print(" "); + } else { + // the space is optional depending on "beautify" + output.space(); + } + output.print(op); + if ((op == "<" || op == "<<") + && self.right instanceof AST_UnaryPrefix + && self.right.operator == "!" + && self.right.expression instanceof AST_UnaryPrefix + && self.right.expression.operator == "--") { + // space is mandatory to avoid outputting x&&y?z:a + if (consequent instanceof AST_Conditional + && consequent.alternative.equivalent_to(alternative)) { + return make_node(AST_Conditional, self, { + condition: make_node(AST_Binary, self, { + left: self.condition, + operator: "&&", + right: consequent.condition + }), + consequent: consequent.consequent, + alternative: alternative + }); + } + // y?1:1 --> 1 + if (consequent.is_constant(compressor) + && alternative.is_constant(compressor) + && consequent.equivalent_to(alternative)) { + var consequent_value = consequent.constant_value(compressor); + if (self.condition.has_side_effects(compressor)) { + return AST_Seq.from_array([self.condition, make_node_from_constant(compressor, consequent_value, self)]); + } else { + return make_node_from_constant(compressor, consequent_value, self); + } + } + + if (is_true(self.consequent)) { + if (is_false(self.alternative)) { + // c ? true : false ---> !!c + return booleanize(self.condition); + } + // c ? true : x ---> !!c || x + return make_node(AST_Binary, self, { + operator: "||", + left: booleanize(self.condition), + right: self.alternative + }); + } + if (is_false(self.consequent)) { + if (is_true(self.alternative)) { + // c ? false : true ---> !c + return booleanize(self.condition.negate(compressor)); + } + // c ? false : x ---> !c && x + return make_node(AST_Binary, self, { + operator: "&&", + left: booleanize(self.condition.negate(compressor)), + right: self.alternative + }); + } + if (is_true(self.alternative)) { + // c ? x : true ---> !c || x + return make_node(AST_Binary, self, { + operator: "||", + left: booleanize(self.condition.negate(compressor)), + right: self.consequent + }); + } + if (is_false(self.alternative)) { + // c ? x : false ---> !!c && x + return make_node(AST_Binary, self, { + operator: "&&", + left: booleanize(self.condition), + right: self.consequent + }); + } + + return self; + + function booleanize(node) { + if (node.is_boolean()) return node; + // !!expression + return make_node(AST_UnaryPrefix, node, { + operator: "!", + expression: node.negate(compressor) + }); + } + + // AST_True or !0 + function is_true(node) { + return node instanceof AST_True + || (node instanceof AST_UnaryPrefix + && node.operator == "!" + && node.expression instanceof AST_Constant + && !node.expression.value); + } + // AST_False or !1 + function is_false(node) { + return node instanceof AST_False + || (node instanceof AST_UnaryPrefix + && node.operator == "!" + && node.expression instanceof AST_Constant + && !!node.expression.value); + } + }); + + OPT(AST_Boolean, function(self, compressor){ + if (compressor.option("booleans")) { + var p = compressor.parent(); + if (p instanceof AST_Binary && (p.operator == "==" + || p.operator == "!=")) { + compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", { + operator : p.operator, + value : self.value, + file : p.start.file, + line : p.start.line, + col : p.start.col, + }); + return make_node(AST_Number, self, { + value: +self.value + }); + } + return make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: make_node(AST_Number, self, { + value: 1 - self.value + }) + }); + } + return self; + }); + + OPT(AST_Sub, function(self, compressor){ + var prop = self.property; + if (prop instanceof AST_String && compressor.option("properties")) { + prop = prop.getValue(); + if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) { + return make_node(AST_Dot, self, { + expression : self.expression, + property : prop + }).optimize(compressor); + } + var v = parseFloat(prop); + if (!isNaN(v) && v.toString() == prop) { + self.property = make_node(AST_Number, self.property, { + value: v + }); + } + } + return self; + }); + + OPT(AST_Dot, function(self, compressor){ + var prop = self.property; + if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) { + return make_node(AST_Sub, self, { + expression : self.expression, + property : make_node(AST_String, self, { + value: prop + }) + }).optimize(compressor); + } + return self.evaluate(compressor)[0]; + }); + + function literals_in_boolean_context(self, compressor) { + if (compressor.option("booleans") && compressor.in_boolean_context() && !self.has_side_effects(compressor)) { + return make_node(AST_True, self); + } + return self; + }; + OPT(AST_Array, literals_in_boolean_context); + OPT(AST_Object, literals_in_boolean_context); + OPT(AST_RegExp, literals_in_boolean_context); + + OPT(AST_Return, function(self, compressor){ + if (self.value instanceof AST_Undefined) { + self.value = null; + } + return self; + }); + +})(); + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +// a small wrapper around fitzgen's source-map library +function SourceMap(options) { + options = defaults(options, { + file : null, + root : null, + orig : null, + + orig_line_diff : 0, + dest_line_diff : 0, + }); + var generator = new MOZ_SourceMap.SourceMapGenerator({ + file : options.file, + sourceRoot : options.root + }); + var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig); + + if (orig_map && Array.isArray(options.orig.sources)) { + options.orig.sources.forEach(function(source) { + var sourceContent = orig_map.sourceContentFor(source, true); + if (sourceContent) { + generator.setSourceContent(source, sourceContent); + } + }); + } + + function add(source, gen_line, gen_col, orig_line, orig_col, name) { + if (orig_map) { + var info = orig_map.originalPositionFor({ + line: orig_line, + column: orig_col + }); + if (info.source === null) { + return; + } + source = info.source; + orig_line = info.line; + orig_col = info.column; + name = info.name || name; + } + generator.addMapping({ + generated : { line: gen_line + options.dest_line_diff, column: gen_col }, + original : { line: orig_line + options.orig_line_diff, column: orig_col }, + source : source, + name : name + }); + }; + return { + add : add, + get : function() { return generator }, + toString : function() { return JSON.stringify(generator.toJSON()); } + }; +}; + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +(function(){ + + var normalize_directives = function(body) { + var in_directive = true; + + for (var i = 0; i < body.length; i++) { + if (in_directive && body[i] instanceof AST_Statement && body[i].body instanceof AST_String) { + body[i] = new AST_Directive({ + start: body[i].start, + end: body[i].end, + value: body[i].body.value + }); + } else if (in_directive && !(body[i] instanceof AST_Statement && body[i].body instanceof AST_String)) { + in_directive = false; + } + } + + return body; + }; + + var MOZ_TO_ME = { + Program: function(M) { + return new AST_Toplevel({ + start: my_start_token(M), + end: my_end_token(M), + body: normalize_directives(M.body.map(from_moz)) + }); + }, + FunctionDeclaration: function(M) { + return new AST_Defun({ + start: my_start_token(M), + end: my_end_token(M), + name: from_moz(M.id), + argnames: M.params.map(from_moz), + body: normalize_directives(from_moz(M.body).body) + }); + }, + FunctionExpression: function(M) { + return new AST_Function({ + start: my_start_token(M), + end: my_end_token(M), + name: from_moz(M.id), + argnames: M.params.map(from_moz), + body: normalize_directives(from_moz(M.body).body) + }); + }, + ExpressionStatement: function(M) { + return new AST_SimpleStatement({ + start: my_start_token(M), + end: my_end_token(M), + body: from_moz(M.expression) + }); + }, + TryStatement: function(M) { + var handlers = M.handlers || [M.handler]; + if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) { + throw new Error("Multiple catch clauses are not supported."); + } + return new AST_Try({ + start : my_start_token(M), + end : my_end_token(M), + body : from_moz(M.block).body, + bcatch : from_moz(handlers[0]), + bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null + }); + }, + Property: function(M) { + var key = M.key; + var name = key.type == "Identifier" ? key.name : key.value; + var args = { + start : my_start_token(key), + end : my_end_token(M.value), + key : name, + value : from_moz(M.value) + }; + switch (M.kind) { + case "init": + return new AST_ObjectKeyVal(args); + case "set": + args.value.name = from_moz(key); + return new AST_ObjectSetter(args); + case "get": + args.value.name = from_moz(key); + return new AST_ObjectGetter(args); + } + }, + ArrayExpression: function(M) { + return new AST_Array({ + start : my_start_token(M), + end : my_end_token(M), + elements : M.elements.map(function(elem){ + return elem === null ? new AST_Hole() : from_moz(elem); + }) + }); + }, + ObjectExpression: function(M) { + return new AST_Object({ + start : my_start_token(M), + end : my_end_token(M), + properties : M.properties.map(function(prop){ + prop.type = "Property"; + return from_moz(prop) + }) + }); + }, + SequenceExpression: function(M) { + return AST_Seq.from_array(M.expressions.map(from_moz)); + }, + MemberExpression: function(M) { + return new (M.computed ? AST_Sub : AST_Dot)({ + start : my_start_token(M), + end : my_end_token(M), + property : M.computed ? from_moz(M.property) : M.property.name, + expression : from_moz(M.object) + }); + }, + SwitchCase: function(M) { + return new (M.test ? AST_Case : AST_Default)({ + start : my_start_token(M), + end : my_end_token(M), + expression : from_moz(M.test), + body : M.consequent.map(from_moz) + }); + }, + VariableDeclaration: function(M) { + return new (M.kind === "const" ? AST_Const : AST_Var)({ + start : my_start_token(M), + end : my_end_token(M), + definitions : M.declarations.map(from_moz) + }); + }, + Literal: function(M) { + var val = M.value, args = { + start : my_start_token(M), + end : my_end_token(M) + }; + if (val === null) return new AST_Null(args); + switch (typeof val) { + case "string": + args.value = val; + return new AST_String(args); + case "number": + args.value = val; + return new AST_Number(args); + case "boolean": + return new (val ? AST_True : AST_False)(args); + default: + var rx = M.regex; + if (rx && rx.pattern) { + // RegExpLiteral as per ESTree AST spec + args.value = new RegExp(rx.pattern, rx.flags).toString(); + } else { + // support legacy RegExp + args.value = M.regex && M.raw ? M.raw : val; + } + return new AST_RegExp(args); + } + }, + Identifier: function(M) { + var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; + return new ( p.type == "LabeledStatement" ? AST_Label + : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar) + : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg) + : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg) + : p.type == "CatchClause" ? AST_SymbolCatch + : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef + : AST_SymbolRef)({ + start : my_start_token(M), + end : my_end_token(M), + name : M.name + }); + } + }; + + MOZ_TO_ME.UpdateExpression = + MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) { + var prefix = "prefix" in M ? M.prefix + : M.type == "UnaryExpression" ? true : false; + return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({ + start : my_start_token(M), + end : my_end_token(M), + operator : M.operator, + expression : from_moz(M.argument) + }); + }; + + map("EmptyStatement", AST_EmptyStatement); + map("BlockStatement", AST_BlockStatement, "body@body"); + map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative"); + map("LabeledStatement", AST_LabeledStatement, "label>label, body>body"); + map("BreakStatement", AST_Break, "label>label"); + map("ContinueStatement", AST_Continue, "label>label"); + map("WithStatement", AST_With, "object>expression, body>body"); + map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body"); + map("ReturnStatement", AST_Return, "argument>value"); + map("ThrowStatement", AST_Throw, "argument>value"); + map("WhileStatement", AST_While, "test>condition, body>body"); + map("DoWhileStatement", AST_Do, "test>condition, body>body"); + map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body"); + map("ForInStatement", AST_ForIn, "left>init, right>object, body>body"); + map("DebuggerStatement", AST_Debugger); + map("VariableDeclarator", AST_VarDef, "id>name, init>value"); + map("CatchClause", AST_Catch, "param>argname, body%body"); + + map("ThisExpression", AST_This); + map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right"); + map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right"); + map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right"); + map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative"); + map("NewExpression", AST_New, "callee>expression, arguments@args"); + map("CallExpression", AST_Call, "callee>expression, arguments@args"); + + def_to_moz(AST_Toplevel, function To_Moz_Program(M) { + return { + type: "Program", + body: M.body.map(to_moz) + }; + }); + + def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) { + return { + type: "FunctionDeclaration", + id: to_moz(M.name), + params: M.argnames.map(to_moz), + body: to_moz_block(M) + } + }); + + def_to_moz(AST_Function, function To_Moz_FunctionExpression(M) { + return { + type: "FunctionExpression", + id: to_moz(M.name), + params: M.argnames.map(to_moz), + body: to_moz_block(M) + } + }); + + def_to_moz(AST_Directive, function To_Moz_Directive(M) { + return { + type: "ExpressionStatement", + expression: { + type: "Literal", + value: M.value + } + }; + }); + + def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) { + return { + type: "ExpressionStatement", + expression: to_moz(M.body) + }; + }); + + def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) { + return { + type: "SwitchCase", + test: to_moz(M.expression), + consequent: M.body.map(to_moz) + }; + }); + + def_to_moz(AST_Try, function To_Moz_TryStatement(M) { + return { + type: "TryStatement", + block: to_moz_block(M), + handler: to_moz(M.bcatch), + guardedHandlers: [], + finalizer: to_moz(M.bfinally) + }; + }); + + def_to_moz(AST_Catch, function To_Moz_CatchClause(M) { + return { + type: "CatchClause", + param: to_moz(M.argname), + guard: null, + body: to_moz_block(M) + }; + }); + + def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) { + return { + type: "VariableDeclaration", + kind: M instanceof AST_Const ? "const" : "var", + declarations: M.definitions.map(to_moz) + }; + }); + + def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) { + return { + type: "SequenceExpression", + expressions: M.to_array().map(to_moz) + }; + }); + + def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) { + var isComputed = M instanceof AST_Sub; + return { + type: "MemberExpression", + object: to_moz(M.expression), + computed: isComputed, + property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property} + }; + }); + + def_to_moz(AST_Unary, function To_Moz_Unary(M) { + return { + type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression", + operator: M.operator, + prefix: M instanceof AST_UnaryPrefix, + argument: to_moz(M.expression) + }; + }); + + def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) { + return { + type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression", + left: to_moz(M.left), + operator: M.operator, + right: to_moz(M.right) + }; + }); + + def_to_moz(AST_Array, function To_Moz_ArrayExpression(M) { + return { + type: "ArrayExpression", + elements: M.elements.map(to_moz) + }; + }); + + def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) { + return { + type: "ObjectExpression", + properties: M.properties.map(to_moz) + }; + }); + + def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) { + var key = ( + is_identifier(M.key) + ? {type: "Identifier", name: M.key} + : {type: "Literal", value: M.key} + ); + var kind; + if (M instanceof AST_ObjectKeyVal) { + kind = "init"; + } else + if (M instanceof AST_ObjectGetter) { + kind = "get"; + } else + if (M instanceof AST_ObjectSetter) { + kind = "set"; + } + return { + type: "Property", + kind: kind, + key: key, + value: to_moz(M.value) + }; + }); + + def_to_moz(AST_Symbol, function To_Moz_Identifier(M) { + var def = M.definition(); + return { + type: "Identifier", + name: def ? def.mangled_name || def.name : M.name + }; + }); + + def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) { + var value = M.value; + return { + type: "Literal", + value: value, + raw: value.toString(), + regex: { + pattern: value.source, + flags: value.toString().match(/[gimuy]*$/)[0] + } + }; + }); + + def_to_moz(AST_Constant, function To_Moz_Literal(M) { + var value = M.value; + if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) { + return { + type: "UnaryExpression", + operator: "-", + prefix: true, + argument: { + type: "Literal", + value: -value, + raw: M.start.raw + } + }; + } + return { + type: "Literal", + value: value, + raw: M.start.raw + }; + }); + + def_to_moz(AST_Atom, function To_Moz_Atom(M) { + return { + type: "Identifier", + name: String(M.value) + }; + }); + + AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); + AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); + AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null }); + + AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast); + AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast); + + /* -----[ tools ]----- */ + + function raw_token(moznode) { + if (moznode.type == "Literal") { + return moznode.raw != null ? moznode.raw : moznode.value + ""; + } + } + + function my_start_token(moznode) { + var loc = moznode.loc, start = loc && loc.start; + var range = moznode.range; + return new AST_Token({ + file : loc && loc.source, + line : start && start.line, + col : start && start.column, + pos : range ? range[0] : moznode.start, + endline : start && start.line, + endcol : start && start.column, + endpos : range ? range[0] : moznode.start, + raw : raw_token(moznode), + }); + }; + + function my_end_token(moznode) { + var loc = moznode.loc, end = loc && loc.end; + var range = moznode.range; + return new AST_Token({ + file : loc && loc.source, + line : end && end.line, + col : end && end.column, + pos : range ? range[1] : moznode.end, + endline : end && end.line, + endcol : end && end.column, + endpos : range ? range[1] : moznode.end, + raw : raw_token(moznode), + }); + }; + + function map(moztype, mytype, propmap) { + var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; + moz_to_me += "return new U2." + mytype.name + "({\n" + + "start: my_start_token(M),\n" + + "end: my_end_token(M)"; + + var me_to_moz = "function To_Moz_" + moztype + "(M){\n"; + me_to_moz += "return {\n" + + "type: " + JSON.stringify(moztype); + + if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){ + var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop); + if (!m) throw new Error("Can't understand property map: " + prop); + var moz = m[1], how = m[2], my = m[3]; + moz_to_me += ",\n" + my + ": "; + me_to_moz += ",\n" + moz + ": "; + switch (how) { + case "@": + moz_to_me += "M." + moz + ".map(from_moz)"; + me_to_moz += "M." + my + ".map(to_moz)"; + break; + case ">": + moz_to_me += "from_moz(M." + moz + ")"; + me_to_moz += "to_moz(M." + my + ")"; + break; + case "=": + moz_to_me += "M." + moz; + me_to_moz += "M." + my; + break; + case "%": + moz_to_me += "from_moz(M." + moz + ").body"; + me_to_moz += "to_moz_block(M)"; + break; + default: + throw new Error("Can't understand operator in propmap: " + prop); + } + }); + + moz_to_me += "\n})\n}"; + me_to_moz += "\n}\n}"; + + //moz_to_me = parse(moz_to_me).print_to_string({ beautify: true }); + //me_to_moz = parse(me_to_moz).print_to_string({ beautify: true }); + //console.log(moz_to_me); + + moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")( + exports, my_start_token, my_end_token, from_moz + ); + me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")( + to_moz, to_moz_block + ); + MOZ_TO_ME[moztype] = moz_to_me; + def_to_moz(mytype, me_to_moz); + }; + + var FROM_MOZ_STACK = null; + + function from_moz(node) { + FROM_MOZ_STACK.push(node); + var ret = node != null ? MOZ_TO_ME[node.type](node) : null; + FROM_MOZ_STACK.pop(); + return ret; + }; + + AST_Node.from_mozilla_ast = function(node){ + var save_stack = FROM_MOZ_STACK; + FROM_MOZ_STACK = []; + var ast = from_moz(node); + FROM_MOZ_STACK = save_stack; + return ast; + }; + + function set_moz_loc(mynode, moznode, myparent) { + var start = mynode.start; + var end = mynode.end; + if (start.pos != null && end.endpos != null) { + moznode.range = [start.pos, end.endpos]; + } + if (start.line) { + moznode.loc = { + start: {line: start.line, column: start.col}, + end: end.endline ? {line: end.endline, column: end.endcol} : null + }; + if (start.file) { + moznode.loc.source = start.file; + } + } + return moznode; + }; + + function def_to_moz(mytype, handler) { + mytype.DEFMETHOD("to_mozilla_ast", function() { + return set_moz_loc(this, handler(this)); + }); + }; + + function to_moz(node) { + return node != null ? node.to_mozilla_ast() : null; + }; + + function to_moz_block(node) { + return { + type: "BlockStatement", + body: node.body.map(to_moz) + }; + }; + +})(); + +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS2 + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +function find_builtins() { + var a = []; + [ Object, Array, Function, Number, + String, Boolean, Error, Math, + Date, RegExp + ].forEach(function(ctor){ + Object.getOwnPropertyNames(ctor).map(add); + if (ctor.prototype) { + Object.getOwnPropertyNames(ctor.prototype).map(add); + } + }); + function add(name) { + push_uniq(a, name); + } + return a; +} + +function mangle_properties(ast, options) { + options = defaults(options, { + reserved : null, + cache : null, + only_cache : false, + regex : null, + ignore_quoted : false + }); + + var reserved = options.reserved; + if (reserved == null) + reserved = find_builtins(); + + var cache = options.cache; + if (cache == null) { + cache = { + cname: -1, + props: new Dictionary() + }; + } + + var regex = options.regex; + var ignore_quoted = options.ignore_quoted; + + var names_to_mangle = []; + var unmangleable = []; + var ignored = {}; + + // step 1: find candidates to mangle + ast.walk(new TreeWalker(function(node){ + if (node instanceof AST_ObjectKeyVal) { + add(node.key, ignore_quoted && node.quote); + } + else if (node instanceof AST_ObjectProperty) { + // setter or getter, since KeyVal is handled above + add(node.key.name); + } + else if (node instanceof AST_Dot) { + add(node.property); + } + else if (node instanceof AST_Sub) { + addStrings(node.property, ignore_quoted); + } + })); + + // step 2: transform the tree, renaming properties + return ast.transform(new TreeTransformer(function(node){ + if (node instanceof AST_ObjectKeyVal) { + if (!(ignore_quoted && node.quote)) + node.key = mangle(node.key); + } + else if (node instanceof AST_ObjectProperty) { + // setter or getter + node.key.name = mangle(node.key.name); + } + else if (node instanceof AST_Dot) { + node.property = mangle(node.property); + } + else if (node instanceof AST_Sub) { + if (!ignore_quoted) + node.property = mangleStrings(node.property); + } + // else if (node instanceof AST_String) { + // if (should_mangle(node.value)) { + // AST_Node.warn( + // "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", { + // file : node.start.file, + // line : node.start.line, + // col : node.start.col, + // prop : node.value + // } + // ); + // } + // } + })); + + // only function declarations after this line + + function can_mangle(name) { + if (unmangleable.indexOf(name) >= 0) return false; + if (reserved.indexOf(name) >= 0) return false; + if (options.only_cache) { + return cache.props.has(name); + } + if (/^[0-9.]+$/.test(name)) return false; + return true; + } + + function should_mangle(name) { + if (ignore_quoted && name in ignored) return false; + if (regex && !regex.test(name)) return false; + if (reserved.indexOf(name) >= 0) return false; + return cache.props.has(name) + || names_to_mangle.indexOf(name) >= 0; + } + + function add(name, ignore) { + if (ignore) { + ignored[name] = true; + return; + } + + if (can_mangle(name)) + push_uniq(names_to_mangle, name); + + if (!should_mangle(name)) { + push_uniq(unmangleable, name); + } + } + + function mangle(name) { + if (!should_mangle(name)) { + return name; + } + + var mangled = cache.props.get(name); + if (!mangled) { + do { + mangled = base54(++cache.cname); + } while (!can_mangle(mangled)); + cache.props.set(name, mangled); + } + return mangled; + } + + function addStrings(node, ignore) { + var out = {}; + try { + (function walk(node){ + node.walk(new TreeWalker(function(node){ + if (node instanceof AST_Seq) { + walk(node.cdr); + return true; + } + if (node instanceof AST_String) { + add(node.value, ignore); + return true; + } + if (node instanceof AST_Conditional) { + walk(node.consequent); + walk(node.alternative); + return true; + } + throw out; + })); + })(node); + } catch(ex) { + if (ex !== out) throw ex; + } + } + + function mangleStrings(node) { + return node.transform(new TreeTransformer(function(node){ + if (node instanceof AST_Seq) { + node.cdr = mangleStrings(node.cdr); + } + else if (node instanceof AST_String) { + node.value = mangle(node.value); + } + else if (node instanceof AST_Conditional) { + node.consequent = mangleStrings(node.consequent); + node.alternative = mangleStrings(node.alternative); + } + return node; + })); + } + +} + +AST_Node.warn_function = function(txt) { logger.error("uglifyjs WARN: " + txt); }; +exports.minify = function(files, options, name) { + options = defaults(options, { + spidermonkey : false, + outSourceMap : null, + sourceRoot : null, + inSourceMap : null, + sourceMapUrl : null, + fromString : false, + warnings : false, + mangle : {}, + mangleProperties : false, + nameCache : null, + output : null, + compress : {}, + parse : {} + }); + base54.reset(); + + // 1. parse + var toplevel = null, + sourcesContent = {}; + + if (options.spidermonkey) { + toplevel = AST_Node.from_mozilla_ast(files); + } else { + var addFile = function(file, fileUrl) { + var code = options.fromString + ? file + : rjsFile.readFile(file, "utf8"); + sourcesContent[fileUrl] = code; + toplevel = parse(code, { + filename: fileUrl, + toplevel: toplevel, + bare_returns: options.parse ? options.parse.bare_returns : undefined + }); + } + if (!options.fromString) files = simple_glob(files); + [].concat(files).forEach(function (files, i) { + if (typeof files === 'string') { + addFile(files, options.fromString ? i : files); + } else { + for (var fileUrl in files) { + addFile(files[fileUrl], fileUrl); + } + } + }); + } + if (options.wrap) { + toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll); + } + + // 2. compress + if (options.compress) { + var compress = { warnings: options.warnings }; + merge(compress, options.compress); + toplevel.figure_out_scope(); + var sq = Compressor(compress); + toplevel = sq.compress(toplevel); + } + + // 3. mangle properties + if (options.mangleProperties || options.nameCache) { + options.mangleProperties.cache = readNameCache(options.nameCache, "props"); + toplevel = mangle_properties(toplevel, options.mangleProperties); + writeNameCache(options.nameCache, "props", options.mangleProperties.cache); + } + + // 4. mangle + if (options.mangle) { + toplevel.figure_out_scope(options.mangle); + toplevel.compute_char_frequency(options.mangle); + toplevel.mangle_names(options.mangle); + } + + // 5. output + var inMap = options.inSourceMap; + var output = {}; + if (typeof options.inSourceMap == "string") { + inMap = JSON.parse(rjsFile.readFile(options.inSourceMap, "utf8")); + } + if (options.outSourceMap) { + output.source_map = SourceMap({ + file: options.outSourceMap, + orig: inMap, + root: options.sourceRoot + }); + if (options.sourceMapIncludeSources) { + for (var file in sourcesContent) { + if (sourcesContent.hasOwnProperty(file)) { + output.source_map.get().setSourceContent(file, sourcesContent[file]); + } + } + } + + } + if (options.output) { + merge(output, options.output); + } + var stream = OutputStream(output); + toplevel.print(stream); + + var mappingUrlPrefix = "\n//# sourceMappingURL="; + if (options.outSourceMap && typeof options.outSourceMap === "string" && options.sourceMapUrl !== false) { + stream += mappingUrlPrefix + (typeof options.sourceMapUrl === "string" ? options.sourceMapUrl : options.outSourceMap); + } + + var source_map = output.source_map; + if (source_map) { + source_map = source_map + ""; + } + + return { + code : stream + "", + map : source_map + }; +}; + +// exports.describe_ast = function() { +// function doitem(ctor) { +// var sub = {}; +// ctor.SUBCLASSES.forEach(function(ctor){ +// sub[ctor.TYPE] = doitem(ctor); +// }); +// var ret = {}; +// if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS; +// if (ctor.SUBCLASSES.length > 0) ret.sub = sub; +// return ret; +// } +// return doitem(AST_Node).sub; +// } + +exports.describe_ast = function() { + var out = OutputStream({ beautify: true }); + function doitem(ctor) { + out.print("AST_" + ctor.TYPE); + var props = ctor.SELF_PROPS.filter(function(prop){ + return !/^\$/.test(prop); + }); + if (props.length > 0) { + out.space(); + out.with_parens(function(){ + props.forEach(function(prop, i){ + if (i) out.space(); + out.print(prop); + }); + }); + } + if (ctor.documentation) { + out.space(); + out.print_string(ctor.documentation); + } + if (ctor.SUBCLASSES.length > 0) { + out.space(); + out.with_block(function(){ + ctor.SUBCLASSES.forEach(function(ctor, i){ + out.indent(); + doitem(ctor); + out.newline(); + }); + }); + } + }; + doitem(AST_Node); + return out + ""; +}; + +}); +/*jslint plusplus: true */ +/*global define: false */ + +define('parse', ['./esprimaAdapter', 'lang'], function (esprima, lang) { + 'use strict'; + + function arrayToString(ary) { + var output = '['; + if (ary) { + ary.forEach(function (item, i) { + output += (i > 0 ? ',' : '') + '"' + lang.jsEscape(item) + '"'; + }); + } + output += ']'; + + return output; + } + + //This string is saved off because JSLint complains + //about obj.arguments use, as 'reserved word' + var argPropName = 'arguments', + //Default object to use for "scope" checking for UMD identifiers. + emptyScope = {}, + mixin = lang.mixin, + hasProp = lang.hasProp; + + //From an esprima example for traversing its ast. + function traverse(object, visitor) { + var child; + + if (!object) { + return; + } + + if (visitor.call(null, object) === false) { + return false; + } + for (var i = 0, keys = Object.keys(object); i < keys.length; i++) { + child = object[keys[i]]; + if (typeof child === 'object' && child !== null) { + if (traverse(child, visitor) === false) { + return false; + } + } + } + } + + //Like traverse, but visitor returning false just + //stops that subtree analysis, not the rest of tree + //visiting. + function traverseBroad(object, visitor) { + var child; + + if (!object) { + return; + } + + if (visitor.call(null, object) === false) { + return false; + } + for (var i = 0, keys = Object.keys(object); i < keys.length; i++) { + child = object[key]; + if (typeof child === 'object' && child !== null) { + traverseBroad(child, visitor); + } + } + } + + /** + * Pulls out dependencies from an array literal with just string members. + * If string literals, will just return those string values in an array, + * skipping other items in the array. + * + * @param {Node} node an AST node. + * + * @returns {Array} an array of strings. + * If null is returned, then it means the input node was not a valid + * dependency. + */ + function getValidDeps(node) { + if (!node || node.type !== 'ArrayExpression' || !node.elements) { + return; + } + + var deps = []; + + node.elements.some(function (elem) { + if (elem.type === 'Literal') { + deps.push(elem.value); + } + }); + + return deps.length ? deps : undefined; + } + + // Detects regular or arrow function expressions as the desired expression + // type. + function isFnExpression(node) { + return (node && (node.type === 'FunctionExpression' || + node.type === 'ArrowFunctionExpression')); + } + + /** + * Main parse function. Returns a string of any valid require or + * define/require.def calls as part of one JavaScript source string. + * @param {String} moduleName the module name that represents this file. + * It is used to create a default define if there is not one already for the + * file. This allows properly tracing dependencies for builds. Otherwise, if + * the file just has a require() call, the file dependencies will not be + * properly reflected: the file will come before its dependencies. + * @param {String} moduleName + * @param {String} fileName + * @param {String} fileContents + * @param {Object} options optional options. insertNeedsDefine: true will + * add calls to require.needsDefine() if appropriate. + * @returns {String} JS source string or null, if no require or + * define/require.def calls are found. + */ + function parse(moduleName, fileName, fileContents, options) { + options = options || {}; + + //Set up source input + var i, moduleCall, depString, + moduleDeps = [], + result = '', + moduleList = [], + needsDefine = true, + astRoot = esprima.parse(fileContents); + + parse.recurse(astRoot, function (callName, config, name, deps, node, factoryIdentifier, fnExpScope) { + if (!deps) { + deps = []; + } + + if (callName === 'define' && (!name || name === moduleName)) { + needsDefine = false; + } + + if (!name) { + //If there is no module name, the dependencies are for + //this file/default module name. + moduleDeps = moduleDeps.concat(deps); + } else { + moduleList.push({ + name: name, + deps: deps + }); + } + + if (callName === 'define' && factoryIdentifier && hasProp(fnExpScope, factoryIdentifier)) { + return factoryIdentifier; + } + + //If define was found, no need to dive deeper, unless + //the config explicitly wants to dig deeper. + return !!options.findNestedDependencies; + }, options); + + if (options.insertNeedsDefine && needsDefine) { + result += 'require.needsDefine("' + moduleName + '");'; + } + + if (moduleDeps.length || moduleList.length) { + for (i = 0; i < moduleList.length; i++) { + moduleCall = moduleList[i]; + if (result) { + result += '\n'; + } + + //If this is the main module for this file, combine any + //"anonymous" dependencies (could come from a nested require + //call) with this module. + if (moduleCall.name === moduleName) { + moduleCall.deps = moduleCall.deps.concat(moduleDeps); + moduleDeps = []; + } + + depString = arrayToString(moduleCall.deps); + result += 'define("' + moduleCall.name + '",' + + depString + ');'; + } + if (moduleDeps.length) { + if (result) { + result += '\n'; + } + depString = arrayToString(moduleDeps); + result += 'define("' + moduleName + '",' + depString + ');'; + } + } + + return result || null; + } + + parse.traverse = traverse; + parse.traverseBroad = traverseBroad; + parse.isFnExpression = isFnExpression; + + /** + * Handles parsing a file recursively for require calls. + * @param {Array} parentNode the AST node to start with. + * @param {Function} onMatch function to call on a parse match. + * @param {Object} [options] This is normally the build config options if + * it is passed. + * @param {Object} [fnExpScope] holds list of function expresssion + * argument identifiers, set up internally, not passed in + */ + parse.recurse = function (object, onMatch, options, fnExpScope) { + //Like traverse, but skips if branches that would not be processed + //after has application that results in tests of true or false boolean + //literal values. + var keys, child, result, i, params, param, tempObject, + hasHas = options && options.has; + + fnExpScope = fnExpScope || emptyScope; + + if (!object) { + return; + } + + //If has replacement has resulted in if(true){} or if(false){}, take + //the appropriate branch and skip the other one. + if (hasHas && object.type === 'IfStatement' && object.test.type && + object.test.type === 'Literal') { + if (object.test.value) { + //Take the if branch + this.recurse(object.consequent, onMatch, options, fnExpScope); + } else { + //Take the else branch + this.recurse(object.alternate, onMatch, options, fnExpScope); + } + } else { + result = this.parseNode(object, onMatch, fnExpScope); + if (result === false) { + return; + } else if (typeof result === 'string') { + return result; + } + + //Build up a "scope" object that informs nested recurse calls if + //the define call references an identifier that is likely a UMD + //wrapped function expression argument. + //Catch (function(a) {... wrappers + if (object.type === 'ExpressionStatement' && object.expression && + object.expression.type === 'CallExpression' && object.expression.callee && + isFnExpression(object.expression.callee)) { + tempObject = object.expression.callee; + } + // Catch !function(a) {... wrappers + if (object.type === 'UnaryExpression' && object.argument && + object.argument.type === 'CallExpression' && object.argument.callee && + isFnExpression(object.argument.callee)) { + tempObject = object.argument.callee; + } + if (tempObject && tempObject.params && tempObject.params.length) { + params = tempObject.params; + fnExpScope = mixin({}, fnExpScope, true); + for (i = 0; i < params.length; i++) { + param = params[i]; + if (param.type === 'Identifier') { + fnExpScope[param.name] = true; + } + } + } + + for (i = 0, keys = Object.keys(object); i < keys.length; i++) { + child = object[keys[i]]; + if (typeof child === 'object' && child !== null) { + result = this.recurse(child, onMatch, options, fnExpScope); + if (typeof result === 'string' && hasProp(fnExpScope, result)) { + //The result was still in fnExpScope so break. Otherwise, + //was a return from a a tree that had a UMD definition, + //but now out of that scope so keep siblings. + break; + } + } + } + + //Check for an identifier for a factory function identifier being + //passed in as a function expression, indicating a UMD-type of + //wrapping. + if (typeof result === 'string') { + if (hasProp(fnExpScope, result)) { + //result still in scope, keep jumping out indicating the + //identifier still in use. + return result; + } + + return; + } + } + }; + + /** + * Determines if the file defines the require/define module API. + * Specifically, it looks for the `define.amd = ` expression. + * @param {String} fileName + * @param {String} fileContents + * @returns {Boolean} + */ + parse.definesRequire = function (fileName, fileContents) { + var foundDefine = false, + foundDefineAmd = false; + + traverse(esprima.parse(fileContents), function (node) { + // Look for a top level declaration of a define, like + // var requirejs, require, define, off Program body. + if (node.type === 'Program' && node.body && node.body.length) { + foundDefine = node.body.some(function(bodyNode) { + // var define + if (bodyNode.type === 'VariableDeclaration') { + var decls = bodyNode.declarations; + if (decls) { + var hasVarDefine = decls.some(function(declNode) { + return (declNode.type === 'VariableDeclarator' && + declNode.id && + declNode.id.type === 'Identifier' && + declNode.id.name === 'define'); + }); + if (hasVarDefine) { + return true; + } + } + } + + // function define() {} + if (bodyNode.type === 'FunctionDeclaration' && + bodyNode.id && + bodyNode.id.type === 'Identifier' && + bodyNode.id.name === 'define') { + return true; + } + + + + + + + }); + } + + // Need define variable found first, before detecting define.amd. + if (foundDefine && parse.hasDefineAmd(node)) { + foundDefineAmd = true; + + //Stop traversal + return false; + } + }); + + return foundDefine && foundDefineAmd; + }; + + /** + * Finds require("") calls inside a CommonJS anonymous module wrapped in a + * define(function(require, exports, module){}) wrapper. These dependencies + * will be added to a modified define() call that lists the dependencies + * on the outside of the function. + * @param {String} fileName + * @param {String|Object} fileContents: a string of contents, or an already + * parsed AST tree. + * @returns {Array} an array of module names that are dependencies. Always + * returns an array, but could be of length zero. + */ + parse.getAnonDeps = function (fileName, fileContents) { + var astRoot = typeof fileContents === 'string' ? + esprima.parse(fileContents) : fileContents, + defFunc = this.findAnonDefineFactory(astRoot); + + return parse.getAnonDepsFromNode(defFunc); + }; + + /** + * Finds require("") calls inside a CommonJS anonymous module wrapped + * in a define function, given an AST node for the definition function. + * @param {Node} node the AST node for the definition function. + * @returns {Array} and array of dependency names. Can be of zero length. + */ + parse.getAnonDepsFromNode = function (node) { + var deps = [], + funcArgLength; + + if (node) { + this.findRequireDepNames(node, deps); + + //If no deps, still add the standard CommonJS require, exports, + //module, in that order, to the deps, but only if specified as + //function args. In particular, if exports is used, it is favored + //over the return value of the function, so only add it if asked. + funcArgLength = node.params && node.params.length; + if (funcArgLength) { + deps = (funcArgLength > 1 ? ["require", "exports", "module"] : + ["require"]).concat(deps); + } + } + return deps; + }; + + parse.isDefineNodeWithArgs = function (node) { + return node && node.type === 'CallExpression' && + node.callee && node.callee.type === 'Identifier' && + node.callee.name === 'define' && node[argPropName]; + }; + + /** + * Finds the function in define(function (require, exports, module){}); + * @param {Array} node + * @returns {Boolean} + */ + parse.findAnonDefineFactory = function (node) { + var match; + + traverse(node, function (node) { + var arg0, arg1; + + if (parse.isDefineNodeWithArgs(node)) { + + //Just the factory function passed to define + arg0 = node[argPropName][0]; + if (isFnExpression(arg0)) { + match = arg0; + return false; + } + + //A string literal module ID followed by the factory function. + arg1 = node[argPropName][1]; + if (arg0.type === 'Literal' && isFnExpression(arg1)) { + match = arg1; + return false; + } + } + }); + + return match; + }; + + /** + * Finds any config that is passed to requirejs. That includes calls to + * require/requirejs.config(), as well as require({}, ...) and + * requirejs({}, ...) + * @param {String} fileContents + * + * @returns {Object} a config details object with the following properties: + * - config: {Object} the config object found. Can be undefined if no + * config found. + * - range: {Array} the start index and end index in the contents where + * the config was found. Can be undefined if no config found. + * Can throw an error if the config in the file cannot be evaluated in + * a build context to valid JavaScript. + */ + parse.findConfig = function (fileContents) { + /*jslint evil: true */ + var jsConfig, foundConfig, stringData, foundRange, quote, quoteMatch, + quoteRegExp = /(:\s|\[\s*)(['"])/, + astRoot = esprima.parse(fileContents, { + loc: true + }); + + traverse(astRoot, function (node) { + var arg, + requireType = parse.hasRequire(node); + + if (requireType && (requireType === 'require' || + requireType === 'requirejs' || + requireType === 'requireConfig' || + requireType === 'requirejsConfig')) { + + arg = node[argPropName] && node[argPropName][0]; + + if (arg && arg.type === 'ObjectExpression') { + stringData = parse.nodeToString(fileContents, arg); + jsConfig = stringData.value; + foundRange = stringData.range; + return false; + } + } else { + arg = parse.getRequireObjectLiteral(node); + if (arg) { + stringData = parse.nodeToString(fileContents, arg); + jsConfig = stringData.value; + foundRange = stringData.range; + return false; + } + } + }); + + if (jsConfig) { + // Eval the config + quoteMatch = quoteRegExp.exec(jsConfig); + quote = (quoteMatch && quoteMatch[2]) || '"'; + foundConfig = eval('(' + jsConfig + ')'); + } + + return { + config: foundConfig, + range: foundRange, + quote: quote + }; + }; + + /** Returns the node for the object literal assigned to require/requirejs, + * for holding a declarative config. + */ + parse.getRequireObjectLiteral = function (node) { + if (node.id && node.id.type === 'Identifier' && + (node.id.name === 'require' || node.id.name === 'requirejs') && + node.init && node.init.type === 'ObjectExpression') { + return node.init; + } + }; + + /** + * Renames require/requirejs/define calls to be ns + '.' + require/requirejs/define + * Does *not* do .config calls though. See pragma.namespace for the complete + * set of namespace transforms. This function is used because require calls + * inside a define() call should not be renamed, so a simple regexp is not + * good enough. + * @param {String} fileContents the contents to transform. + * @param {String} ns the namespace, *not* including trailing dot. + * @return {String} the fileContents with the namespace applied + */ + parse.renameNamespace = function (fileContents, ns) { + var lines, + locs = [], + astRoot = esprima.parse(fileContents, { + loc: true + }); + + parse.recurse(astRoot, function (callName, config, name, deps, node) { + locs.push(node.loc); + //Do not recurse into define functions, they should be using + //local defines. + return callName !== 'define'; + }, {}); + + if (locs.length) { + lines = fileContents.split('\n'); + + //Go backwards through the found locs, adding in the namespace name + //in front. + locs.reverse(); + locs.forEach(function (loc) { + var startIndex = loc.start.column, + //start.line is 1-based, not 0 based. + lineIndex = loc.start.line - 1, + line = lines[lineIndex]; + + lines[lineIndex] = line.substring(0, startIndex) + + ns + '.' + + line.substring(startIndex, + line.length); + }); + + fileContents = lines.join('\n'); + } + + return fileContents; + }; + + /** + * Finds all dependencies specified in dependency arrays and inside + * simplified commonjs wrappers. + * @param {String} fileName + * @param {String} fileContents + * + * @returns {Array} an array of dependency strings. The dependencies + * have not been normalized, they may be relative IDs. + */ + parse.findDependencies = function (fileName, fileContents, options) { + var dependencies = [], + astRoot = esprima.parse(fileContents); + + parse.recurse(astRoot, function (callName, config, name, deps) { + if (deps) { + dependencies = dependencies.concat(deps); + } + }, options); + + return dependencies; + }; + + /** + * Finds only CJS dependencies, ones that are the form + * require('stringLiteral') + */ + parse.findCjsDependencies = function (fileName, fileContents) { + var dependencies = []; + + traverse(esprima.parse(fileContents), function (node) { + var arg; + + if (node && node.type === 'CallExpression' && node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'require' && node[argPropName] && + node[argPropName].length === 1) { + arg = node[argPropName][0]; + if (arg.type === 'Literal') { + dependencies.push(arg.value); + } + } + }); + + return dependencies; + }; + + //function define() {} + parse.hasDefDefine = function (node) { + return node.type === 'FunctionDeclaration' && node.id && + node.id.type === 'Identifier' && node.id.name === 'define'; + }; + + //define.amd = ... + parse.hasDefineAmd = function (node) { + return node && node.type === 'AssignmentExpression' && + node.left && node.left.type === 'MemberExpression' && + node.left.object && node.left.object.name === 'define' && + node.left.property && node.left.property.name === 'amd'; + }; + + //define.amd reference, as in: if (define.amd) + parse.refsDefineAmd = function (node) { + return node && node.type === 'MemberExpression' && + node.object && node.object.name === 'define' && + node.object.type === 'Identifier' && + node.property && node.property.name === 'amd' && + node.property.type === 'Identifier'; + }; + + //require(), requirejs(), require.config() and requirejs.config() + parse.hasRequire = function (node) { + var callName, + c = node && node.callee; + + if (node && node.type === 'CallExpression' && c) { + if (c.type === 'Identifier' && + (c.name === 'require' || + c.name === 'requirejs')) { + //A require/requirejs({}, ...) call + callName = c.name; + } else if (c.type === 'MemberExpression' && + c.object && + c.object.type === 'Identifier' && + (c.object.name === 'require' || + c.object.name === 'requirejs') && + c.property && c.property.name === 'config') { + // require/requirejs.config({}) call + callName = c.object.name + 'Config'; + } + } + + return callName; + }; + + //define() + parse.hasDefine = function (node) { + return node && node.type === 'CallExpression' && node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'define'; + }; + + /** + * If there is a named define in the file, returns the name. Does not + * scan for mulitple names, just the first one. + */ + parse.getNamedDefine = function (fileContents) { + var name; + traverse(esprima.parse(fileContents), function (node) { + if (node && node.type === 'CallExpression' && node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'define' && + node[argPropName] && node[argPropName][0] && + node[argPropName][0].type === 'Literal') { + name = node[argPropName][0].value; + return false; + } + }); + + return name; + }; + + /** + * Finds all the named define module IDs in a file. + */ + parse.getAllNamedDefines = function (fileContents, excludeMap) { + var names = []; + parse.recurse(esprima.parse(fileContents), + function (callName, config, name, deps, node, factoryIdentifier, fnExpScope) { + if (callName === 'define' && name) { + if (!excludeMap.hasOwnProperty(name)) { + names.push(name); + } + } + + //If a UMD definition that points to a factory that is an Identifier, + //indicate processing should not traverse inside the UMD definition. + if (callName === 'define' && factoryIdentifier && hasProp(fnExpScope, factoryIdentifier)) { + return factoryIdentifier; + } + + //If define was found, no need to dive deeper, unless + //the config explicitly wants to dig deeper. + return true; + }, {}); + + return names; + }; + + /** + * Determines if define(), require({}|[]) or requirejs was called in the + * file. Also finds out if define() is declared and if define.amd is called. + */ + parse.usesAmdOrRequireJs = function (fileName, fileContents) { + var uses; + + traverse(esprima.parse(fileContents), function (node) { + var type, callName, arg; + + if (parse.hasDefDefine(node)) { + //function define() {} + type = 'declaresDefine'; + } else if (parse.hasDefineAmd(node)) { + type = 'defineAmd'; + } else { + callName = parse.hasRequire(node); + if (callName) { + arg = node[argPropName] && node[argPropName][0]; + if (arg && (arg.type === 'ObjectExpression' || + arg.type === 'ArrayExpression')) { + type = callName; + } + } else if (parse.hasDefine(node)) { + type = 'define'; + } + } + + if (type) { + if (!uses) { + uses = {}; + } + uses[type] = true; + } + }); + + return uses; + }; + + /** + * Determines if require(''), exports.x =, module.exports =, + * __dirname, __filename are used. So, not strictly traditional CommonJS, + * also checks for Node variants. + */ + parse.usesCommonJs = function (fileName, fileContents) { + var uses = null, + assignsExports = false; + + + traverse(esprima.parse(fileContents), function (node) { + var type, + exp = node.expression || node.init; + + if (node.type === 'Identifier' && + (node.name === '__dirname' || node.name === '__filename')) { + type = node.name.substring(2); + } else if (node.type === 'VariableDeclarator' && node.id && + node.id.type === 'Identifier' && + node.id.name === 'exports') { + //Hmm, a variable assignment for exports, so does not use cjs + //exports. + type = 'varExports'; + } else if (exp && exp.type === 'AssignmentExpression' && exp.left && + exp.left.type === 'MemberExpression' && exp.left.object) { + if (exp.left.object.name === 'module' && exp.left.property && + exp.left.property.name === 'exports') { + type = 'moduleExports'; + } else if (exp.left.object.name === 'exports' && + exp.left.property) { + type = 'exports'; + } else if (exp.left.object.type === 'MemberExpression' && + exp.left.object.object.name === 'module' && + exp.left.object.property.name === 'exports' && + exp.left.object.property.type === 'Identifier') { + type = 'moduleExports'; + } + + } else if (node && node.type === 'CallExpression' && node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'require' && node[argPropName] && + node[argPropName].length === 1 && + node[argPropName][0].type === 'Literal') { + type = 'require'; + } + + if (type) { + if (type === 'varExports') { + assignsExports = true; + } else if (type !== 'exports' || !assignsExports) { + if (!uses) { + uses = {}; + } + uses[type] = true; + } + } + }); + + return uses; + }; + + + parse.findRequireDepNames = function (node, deps) { + traverse(node, function (node) { + var arg; + + if (node && node.type === 'CallExpression' && node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'require' && + node[argPropName] && node[argPropName].length === 1) { + + arg = node[argPropName][0]; + if (arg.type === 'Literal') { + deps.push(arg.value); + } + } + }); + }; + + /** + * Determines if a specific node is a valid require or define/require.def + * call. + * @param {Array} node + * @param {Function} onMatch a function to call when a match is found. + * It is passed the match name, and the config, name, deps possible args. + * The config, name and deps args are not normalized. + * @param {Object} fnExpScope an object whose keys are all function + * expression identifiers that should be in scope. Useful for UMD wrapper + * detection to avoid parsing more into the wrapped UMD code. + * + * @returns {String} a JS source string with the valid require/define call. + * Otherwise null. + */ + parse.parseNode = function (node, onMatch, fnExpScope) { + var name, deps, cjsDeps, arg, factory, exp, refsDefine, bodyNode, + args = node && node[argPropName], + callName = parse.hasRequire(node), + isUmd = false; + + if (callName === 'require' || callName === 'requirejs') { + //A plain require/requirejs call + arg = node[argPropName] && node[argPropName][0]; + if (arg && arg.type !== 'ArrayExpression') { + if (arg.type === 'ObjectExpression') { + //A config call, try the second arg. + arg = node[argPropName][1]; + } + } + + deps = getValidDeps(arg); + if (!deps) { + return; + } + + return onMatch("require", null, null, deps, node); + } else if (parse.hasDefine(node) && args && args.length) { + name = args[0]; + deps = args[1]; + factory = args[2]; + + if (name.type === 'ArrayExpression') { + //No name, adjust args + factory = deps; + deps = name; + name = null; + } else if (isFnExpression(name)) { + //Just the factory, no name or deps + factory = name; + name = deps = null; + } else if (name.type === 'Identifier' && args.length === 1 && + hasProp(fnExpScope, name.name)) { + //define(e) where e is a UMD identifier for the factory + //function. + isUmd = true; + factory = name; + name = null; + } else if (name.type !== 'Literal') { + //An object literal, just null out + name = deps = factory = null; + } + + if (name && name.type === 'Literal' && deps) { + if (isFnExpression(deps)) { + //deps is the factory + factory = deps; + deps = null; + } else if (deps.type === 'ObjectExpression') { + //deps is object literal, null out + deps = factory = null; + } else if (deps.type === 'Identifier') { + if (args.length === 2) { + //define('id', factory) + deps = factory = null; + } else if (args.length === 3 && isFnExpression(factory)) { + //define('id', depsIdentifier, factory) + //Since identifier, cannot know the deps, but do not + //error out, assume they are taken care of outside of + //static parsing. + deps = null; + } + } + } + + if (deps && deps.type === 'ArrayExpression') { + deps = getValidDeps(deps); + } else if (isFnExpression(factory)) { + //If no deps and a factory function, could be a commonjs sugar + //wrapper, scan the function for dependencies. + cjsDeps = parse.getAnonDepsFromNode(factory); + if (cjsDeps.length) { + deps = cjsDeps; + } + } else if (deps || (factory && !isUmd)) { + //Does not match the shape of an AMD call. + return; + } + + //Just save off the name as a string instead of an AST object. + if (name && name.type === 'Literal') { + name = name.value; + } + + return onMatch("define", null, name, deps, node, + (factory && factory.type === 'Identifier' ? factory.name : undefined), + fnExpScope); + } else if (node.type === 'CallExpression' && node.callee && + isFnExpression(node.callee) && + node.callee.body && node.callee.body.body && + node.callee.body.body.length === 1 && + node.callee.body.body[0].type === 'IfStatement') { + bodyNode = node.callee.body.body[0]; + //Look for a define(Identifier) case, but only if inside an + //if that has a define.amd test + if (bodyNode.consequent && bodyNode.consequent.body) { + exp = bodyNode.consequent.body[0]; + if (exp.type === 'ExpressionStatement' && exp.expression && + parse.hasDefine(exp.expression) && + exp.expression.arguments && + exp.expression.arguments.length === 1 && + exp.expression.arguments[0].type === 'Identifier') { + + //Calls define(Identifier) as first statement in body. + //Confirm the if test references define.amd + traverse(bodyNode.test, function (node) { + if (parse.refsDefineAmd(node)) { + refsDefine = true; + return false; + } + }); + + if (refsDefine) { + return onMatch("define", null, null, null, exp.expression, + exp.expression.arguments[0].name, fnExpScope); + } + } + } + } + }; + + /** + * Converts an AST node into a JS source string by extracting + * the node's location from the given contents string. Assumes + * esprima.parse() with loc was done. + * @param {String} contents + * @param {Object} node + * @returns {String} a JS source string. + */ + parse.nodeToString = function (contents, node) { + var extracted, + loc = node.loc, + lines = contents.split('\n'), + firstLine = loc.start.line > 1 ? + lines.slice(0, loc.start.line - 1).join('\n') + '\n' : + '', + preamble = firstLine + + lines[loc.start.line - 1].substring(0, loc.start.column); + + if (loc.start.line === loc.end.line) { + extracted = lines[loc.start.line - 1].substring(loc.start.column, + loc.end.column); + } else { + extracted = lines[loc.start.line - 1].substring(loc.start.column) + + '\n' + + lines.slice(loc.start.line, loc.end.line - 1).join('\n') + + '\n' + + lines[loc.end.line - 1].substring(0, loc.end.column); + } + + return { + value: extracted, + range: [ + preamble.length, + preamble.length + extracted.length + ] + }; + }; + + /** + * Extracts license comments from JS text. + * @param {String} fileName + * @param {String} contents + * @returns {String} a string of license comments. + */ + parse.getLicenseComments = function (fileName, contents) { + var commentNode, refNode, subNode, value, i, j, + //xpconnect's Reflect does not support comment or range, but + //prefer continued operation vs strict parity of operation, + //as license comments can be expressed in other ways, like + //via wrap args, or linked via sourcemaps. + ast = esprima.parse(contents, { + comment: true, + range: true + }), + result = '', + existsMap = {}, + lineEnd = contents.indexOf('\r') === -1 ? '\n' : '\r\n'; + + if (ast.comments) { + for (i = 0; i < ast.comments.length; i++) { + commentNode = ast.comments[i]; + + if (commentNode.type === 'Line') { + value = '//' + commentNode.value + lineEnd; + refNode = commentNode; + + if (i + 1 >= ast.comments.length) { + value += lineEnd; + } else { + //Look for immediately adjacent single line comments + //since it could from a multiple line comment made out + //of single line comments. Like this comment. + for (j = i + 1; j < ast.comments.length; j++) { + subNode = ast.comments[j]; + if (subNode.type === 'Line' && + subNode.range[0] === refNode.range[1] + 1) { + //Adjacent single line comment. Collect it. + value += '//' + subNode.value + lineEnd; + refNode = subNode; + } else { + //No more single line comment blocks. Break out + //and continue outer looping. + break; + } + } + value += lineEnd; + i = j - 1; + } + } else { + value = '/*' + commentNode.value + '*/' + lineEnd + lineEnd; + } + + if (!existsMap[value] && (value.indexOf('license') !== -1 || + (commentNode.type === 'Block' && + value.indexOf('/*!') === 0) || + value.indexOf('opyright') !== -1 || + value.indexOf('(c)') !== -1)) { + + result += value; + existsMap[value] = true; + } + + } + } + + return result; + }; + + return parse; +}); +/*global define */ + +define('transform', [ './esprimaAdapter', './parse', 'logger', 'lang'], +function (esprima, parse, logger, lang) { + 'use strict'; + var transform, + baseIndentRegExp = /^([ \t]+)/, + indentRegExp = /\{[\r\n]+([ \t]+)/, + keyRegExp = /^[_A-Za-z]([A-Za-z\d_]*)$/, + bulkIndentRegExps = { + '\n': /\n/g, + '\r\n': /\r\n/g + }; + + function applyIndent(str, indent, lineReturn) { + var regExp = bulkIndentRegExps[lineReturn]; + return str.replace(regExp, '$&' + indent); + } + + transform = { + toTransport: function (namespace, moduleName, path, contents, onFound, options) { + options = options || {}; + + var astRoot, contentLines, modLine, + foundAnon, + scanCount = 0, + scanReset = false, + defineInfos = [], + applySourceUrl = function (contents) { + if (options.useSourceUrl) { + contents = 'eval("' + lang.jsEscape(contents) + + '\\n//# sourceURL=' + (path.indexOf('/') === 0 ? '' : '/') + + path + + '");\n'; + } + return contents; + }; + + try { + astRoot = esprima.parse(contents, { + loc: true + }); + } catch (e) { + logger.trace('toTransport skipping ' + path + ': ' + + e.toString()); + return contents; + } + + //Find the define calls and their position in the files. + parse.traverse(astRoot, function (node) { + var args, firstArg, firstArgLoc, factoryNode, + needsId, depAction, foundId, init, + sourceUrlData, range, + namespaceExists = false; + + // If a bundle script with a define declaration, do not + // parse any further at this level. Likely a built layerobj + // by some other tool. + if (node.type === 'VariableDeclarator' && + node.id && node.id.name === 'define' && + node.id.type === 'Identifier') { + init = node.init; + if (init && init.callee && + init.callee.type === 'CallExpression' && + init.callee.callee && + init.callee.callee.type === 'Identifier' && + init.callee.callee.name === 'require' && + init.callee.arguments && init.callee.arguments.length === 1 && + init.callee.arguments[0].type === 'Literal' && + init.callee.arguments[0].value && + init.callee.arguments[0].value.indexOf('amdefine') !== -1) { + // the var define = require('amdefine')(module) case, + // keep going in that case. + } else { + return false; + } + } + + namespaceExists = namespace && + node.type === 'CallExpression' && + node.callee && node.callee.object && + node.callee.object.type === 'Identifier' && + node.callee.object.name === namespace && + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'define'; + + if (namespaceExists || parse.isDefineNodeWithArgs(node)) { + //The arguments are where its at. + args = node.arguments; + if (!args || !args.length) { + return; + } + + firstArg = args[0]; + firstArgLoc = firstArg.loc; + + if (args.length === 1) { + if (firstArg.type === 'Identifier') { + //The define(factory) case, but + //only allow it if one Identifier arg, + //to limit impact of false positives. + needsId = true; + depAction = 'empty'; + } else if (parse.isFnExpression(firstArg)) { + //define(function(){}) + factoryNode = firstArg; + needsId = true; + depAction = 'scan'; + } else if (firstArg.type === 'ObjectExpression') { + //define({}); + needsId = true; + depAction = 'skip'; + } else if (firstArg.type === 'Literal' && + typeof firstArg.value === 'number') { + //define('12345'); + needsId = true; + depAction = 'skip'; + } else if (firstArg.type === 'UnaryExpression' && + firstArg.operator === '-' && + firstArg.argument && + firstArg.argument.type === 'Literal' && + typeof firstArg.argument.value === 'number') { + //define('-12345'); + needsId = true; + depAction = 'skip'; + } else if (firstArg.type === 'MemberExpression' && + firstArg.object && + firstArg.property && + firstArg.property.type === 'Identifier') { + //define(this.key); + needsId = true; + depAction = 'empty'; + } + } else if (firstArg.type === 'ArrayExpression') { + //define([], ...); + needsId = true; + depAction = 'skip'; + } else if (firstArg.type === 'Literal' && + typeof firstArg.value === 'string') { + //define('string', ....) + //Already has an ID. + needsId = false; + if (args.length === 2 && + parse.isFnExpression(args[1])) { + //Needs dependency scanning. + factoryNode = args[1]; + depAction = 'scan'; + } else { + depAction = 'skip'; + } + } else { + //Unknown define entity, keep looking, even + //in the subtree for this node. + return; + } + + range = { + foundId: foundId, + needsId: needsId, + depAction: depAction, + namespaceExists: namespaceExists, + node: node, + defineLoc: node.loc, + firstArgLoc: firstArgLoc, + factoryNode: factoryNode, + sourceUrlData: sourceUrlData + }; + + //Only transform ones that do not have IDs. If it has an + //ID but no dependency array, assume it is something like + //a phonegap implementation, that has its own internal + //define that cannot handle dependency array constructs, + //and if it is a named module, then it means it has been + //set for transport form. + if (range.needsId) { + if (foundAnon) { + logger.trace(path + ' has more than one anonymous ' + + 'define. May be a built file from another ' + + 'build system like, Ender. Skipping normalization.'); + defineInfos = []; + return false; + } else { + foundAnon = range; + defineInfos.push(range); + } + } else if (depAction === 'scan') { + scanCount += 1; + if (scanCount > 1) { + //Just go back to an array that just has the + //anon one, since this is an already optimized + //file like the phonegap one. + if (!scanReset) { + defineInfos = foundAnon ? [foundAnon] : []; + scanReset = true; + } + } else { + defineInfos.push(range); + } + } + } + }); + + + if (!defineInfos.length) { + return applySourceUrl(contents); + } + + //Reverse the matches, need to start from the bottom of + //the file to modify it, so that the ranges are still true + //further up. + defineInfos.reverse(); + + contentLines = contents.split('\n'); + + modLine = function (loc, contentInsertion) { + var startIndex = loc.start.column, + //start.line is 1-based, not 0 based. + lineIndex = loc.start.line - 1, + line = contentLines[lineIndex]; + contentLines[lineIndex] = line.substring(0, startIndex) + + contentInsertion + + line.substring(startIndex, + line.length); + }; + + defineInfos.forEach(function (info) { + var deps, + contentInsertion = '', + depString = ''; + + //Do the modifications "backwards", in other words, start with the + //one that is farthest down and work up, so that the ranges in the + //defineInfos still apply. So that means deps, id, then namespace. + if (info.needsId && moduleName) { + contentInsertion += "'" + moduleName + "',"; + } + + if (info.depAction === 'scan') { + deps = parse.getAnonDepsFromNode(info.factoryNode); + + if (deps.length) { + depString = '[' + deps.map(function (dep) { + return "'" + dep + "'"; + }) + ']'; + } else { + depString = '[]'; + } + depString += ','; + + if (info.factoryNode) { + //Already have a named module, need to insert the + //dependencies after the name. + modLine(info.factoryNode.loc, depString); + } else { + contentInsertion += depString; + } + } + + if (contentInsertion) { + modLine(info.firstArgLoc, contentInsertion); + } + + //Do namespace last so that ui does not mess upthe parenRange + //used above. + if (namespace && !info.namespaceExists) { + modLine(info.defineLoc, namespace + '.'); + } + + //Notify any listener for the found info + if (onFound) { + onFound(info); + } + }); + + contents = contentLines.join('\n'); + + return applySourceUrl(contents); + }, + + /** + * Modify the contents of a require.config/requirejs.config call. This + * call will LOSE any existing comments that are in the config string. + * + * @param {String} fileContents String that may contain a config call + * @param {Function} onConfig Function called when the first config + * call is found. It will be passed an Object which is the current + * config, and the onConfig function should return an Object to use + * as the config. + * @return {String} the fileContents with the config changes applied. + */ + modifyConfig: function (fileContents, onConfig) { + var details = parse.findConfig(fileContents), + config = details.config; + + if (config) { + config = onConfig(config); + if (config) { + return transform.serializeConfig(config, + fileContents, + details.range[0], + details.range[1], + { + quote: details.quote + }); + } + } + + return fileContents; + }, + + serializeConfig: function (config, fileContents, start, end, options) { + //Calculate base level of indent + var indent, match, configString, outDentRegExp, + baseIndent = '', + startString = fileContents.substring(0, start), + existingConfigString = fileContents.substring(start, end), + lineReturn = existingConfigString.indexOf('\r') === -1 ? '\n' : '\r\n', + lastReturnIndex = startString.lastIndexOf('\n'); + + //Get the basic amount of indent for the require config call. + if (lastReturnIndex === -1) { + lastReturnIndex = 0; + } + + match = baseIndentRegExp.exec(startString.substring(lastReturnIndex + 1, start)); + if (match && match[1]) { + baseIndent = match[1]; + } + + //Calculate internal indentation for config + match = indentRegExp.exec(existingConfigString); + if (match && match[1]) { + indent = match[1]; + } + + if (!indent || indent.length < baseIndent) { + indent = ' '; + } else { + indent = indent.substring(baseIndent.length); + } + + outDentRegExp = new RegExp('(' + lineReturn + ')' + indent, 'g'); + + configString = transform.objectToString(config, { + indent: indent, + lineReturn: lineReturn, + outDentRegExp: outDentRegExp, + quote: options && options.quote + }); + + //Add in the base indenting level. + configString = applyIndent(configString, baseIndent, lineReturn); + + return startString + configString + fileContents.substring(end); + }, + + /** + * Tries converting a JS object to a string. This will likely suck, and + * is tailored to the type of config expected in a loader config call. + * So, hasOwnProperty fields, strings, numbers, arrays and functions, + * no weird recursively referenced stuff. + * @param {Object} obj the object to convert + * @param {Object} options options object with the following values: + * {String} indent the indentation to use for each level + * {String} lineReturn the type of line return to use + * {outDentRegExp} outDentRegExp the regexp to use to outdent functions + * {String} quote the quote type to use, ' or ". Optional. Default is " + * @param {String} totalIndent the total indent to print for this level + * @return {String} a string representation of the object. + */ + objectToString: function (obj, options, totalIndent) { + var startBrace, endBrace, nextIndent, + first = true, + value = '', + lineReturn = options.lineReturn, + indent = options.indent, + outDentRegExp = options.outDentRegExp, + quote = options.quote || '"'; + + totalIndent = totalIndent || ''; + nextIndent = totalIndent + indent; + + if (obj === null) { + value = 'null'; + } else if (obj === undefined) { + value = 'undefined'; + } else if (typeof obj === 'number' || typeof obj === 'boolean') { + value = obj; + } else if (typeof obj === 'string') { + //Use double quotes in case the config may also work as JSON. + value = quote + lang.jsEscape(obj) + quote; + } else if (lang.isArray(obj)) { + lang.each(obj, function (item, i) { + value += (i !== 0 ? ',' + lineReturn : '' ) + + nextIndent + + transform.objectToString(item, + options, + nextIndent); + }); + + startBrace = '['; + endBrace = ']'; + } else if (lang.isFunction(obj) || lang.isRegExp(obj)) { + //The outdent regexp just helps pretty up the conversion + //just in node. Rhino strips comments and does a different + //indent scheme for Function toString, so not really helpful + //there. + value = obj.toString().replace(outDentRegExp, '$1'); + } else { + //An object + lang.eachProp(obj, function (v, prop) { + value += (first ? '': ',' + lineReturn) + + nextIndent + + (keyRegExp.test(prop) ? prop : quote + lang.jsEscape(prop) + quote )+ + ': ' + + transform.objectToString(v, + options, + nextIndent); + first = false; + }); + startBrace = '{'; + endBrace = '}'; + } + + if (startBrace) { + value = startBrace + + lineReturn + + value + + lineReturn + totalIndent + + endBrace; + } + + return value; + } + }; + + return transform; +}); +/*jslint regexp: true, plusplus: true */ +/*global define: false */ + +define('pragma', ['parse', 'logger'], function (parse, logger) { + 'use strict'; + function Temp() {} + + function create(obj, mixin) { + Temp.prototype = obj; + var temp = new Temp(), prop; + + //Avoid any extra memory hanging around + Temp.prototype = null; + + if (mixin) { + for (prop in mixin) { + if (mixin.hasOwnProperty(prop) && !temp.hasOwnProperty(prop)) { + temp[prop] = mixin[prop]; + } + } + } + + return temp; // Object + } + + var pragma = { + conditionalRegExp: /(exclude|include)Start\s*\(\s*["'](\w+)["']\s*,(.*)\)/, + useStrictRegExp: /(^|[^{]\r?\n)['"]use strict['"];/g, + hasRegExp: /has\s*\(\s*['"]([^'"]+)['"]\s*\)/g, + configRegExp: /(^|[^\.])(requirejs|require)(\.config)\s*\(/g, + nsWrapRegExp: /\/\*requirejs namespace: true \*\//, + apiDefRegExp: /var requirejs,\s*require,\s*define;/, + defineCheckRegExp: /typeof(\s+|\s*\(\s*)define(\s*\))?\s*===?\s*["']function["']\s*&&\s*define\s*\.\s*amd/g, + defineStringCheckRegExp: /typeof\s+define\s*===?\s*["']function["']\s*&&\s*define\s*\[\s*["']amd["']\s*\]/g, + defineTypeFirstCheckRegExp: /\s*["']function["']\s*==(=?)\s*typeof\s+define\s*&&\s*define\s*\.\s*amd/g, + defineJQueryRegExp: /typeof\s+define\s*===?\s*["']function["']\s*&&\s*define\s*\.\s*amd\s*&&\s*define\s*\.\s*amd\s*\.\s*jQuery/g, + defineHasRegExp: /typeof\s+define\s*==(=)?\s*['"]function['"]\s*&&\s*typeof\s+define\.amd\s*==(=)?\s*['"]object['"]\s*&&\s*define\.amd/g, + defineTernaryRegExp: /typeof\s+define\s*===?\s*['"]function["']\s*&&\s*define\s*\.\s*amd\s*\?\s*define/, + defineExistsRegExp: /\s+typeof\s+define\s*!==?\s*['"]undefined["']\s*/, + defineExistsAndAmdRegExp: /typeof\s+define\s*!==?\s*['"]undefined["']\s*&&\s*define\s*\.\s*amd\s*/, + amdefineRegExp: /if\s*\(\s*typeof define\s*\!==\s*['"]function['"]\s*\)\s*\{\s*[^\{\}]+amdefine[^\{\}]+\}/g, + + removeStrict: function (contents, config) { + return config.useStrict ? contents : contents.replace(pragma.useStrictRegExp, '$1'); + }, + + namespace: function (fileContents, ns, onLifecycleName) { + if (ns) { + //Namespace require/define calls + fileContents = fileContents.replace(pragma.configRegExp, '$1' + ns + '.$2$3('); + + + fileContents = parse.renameNamespace(fileContents, ns); + + //Namespace define ternary use: + fileContents = fileContents.replace(pragma.defineTernaryRegExp, + "typeof " + ns + ".define === 'function' && " + ns + ".define.amd ? " + ns + ".define"); + + //Namespace define jquery use: + fileContents = fileContents.replace(pragma.defineJQueryRegExp, + "typeof " + ns + ".define === 'function' && " + ns + ".define.amd && " + ns + ".define.amd.jQuery"); + + //Namespace has.js define use: + fileContents = fileContents.replace(pragma.defineHasRegExp, + "typeof " + ns + ".define === 'function' && typeof " + ns + ".define.amd === 'object' && " + ns + ".define.amd"); + + //Namespace async.js define use: + fileContents = fileContents.replace(pragma.defineExistsAndAmdRegExp, + "typeof " + ns + ".define !== 'undefined' && " + ns + ".define.amd"); + + //Namespace define checks. + //Do these ones last, since they are a subset of the more specific + //checks above. + fileContents = fileContents.replace(pragma.defineCheckRegExp, + "typeof " + ns + ".define === 'function' && " + ns + ".define.amd"); + fileContents = fileContents.replace(pragma.defineStringCheckRegExp, + "typeof " + ns + ".define === 'function' && " + ns + ".define['amd']"); + fileContents = fileContents.replace(pragma.defineTypeFirstCheckRegExp, + "'function' === typeof " + ns + ".define && " + ns + ".define.amd"); + fileContents = fileContents.replace(pragma.defineExistsRegExp, + "typeof " + ns + ".define !== 'undefined'"); + + //Check for require.js with the require/define definitions + if (pragma.apiDefRegExp.test(fileContents) && + fileContents.indexOf("if (!" + ns + " || !" + ns + ".requirejs)") === -1) { + //Wrap the file contents in a typeof check, and a function + //to contain the API globals. + fileContents = "var " + ns + ";(function () { if (!" + ns + " || !" + ns + ".requirejs) {\n" + + "if (!" + ns + ") { " + ns + ' = {}; } else { require = ' + ns + '; }\n' + + fileContents + + "\n" + + ns + ".requirejs = requirejs;" + + ns + ".require = require;" + + ns + ".define = define;\n" + + "}\n}());"; + } + + //Finally, if the file wants a special wrapper because it ties + //in to the requirejs internals in a way that would not fit + //the above matches, do that. Look for /*requirejs namespace: true*/ + if (pragma.nsWrapRegExp.test(fileContents)) { + //Remove the pragma. + fileContents = fileContents.replace(pragma.nsWrapRegExp, ''); + + //Alter the contents. + fileContents = '(function () {\n' + + 'var require = ' + ns + '.require,' + + 'requirejs = ' + ns + '.requirejs,' + + 'define = ' + ns + '.define;\n' + + fileContents + + '\n}());'; + } + } + + return fileContents; + }, + + /** + * processes the fileContents for some //>> conditional statements + */ + process: function (fileName, fileContents, config, onLifecycleName, pluginCollector) { + /*jslint evil: true */ + var foundIndex = -1, startIndex = 0, lineEndIndex, conditionLine, + matches, type, marker, condition, isTrue, endRegExp, endMatches, + endMarkerIndex, shouldInclude, startLength, lifecycleHas, deps, + i, dep, moduleName, collectorMod, + lifecyclePragmas, pragmas = config.pragmas, hasConfig = config.has, + //Legacy arg defined to help in dojo conversion script. Remove later + //when dojo no longer needs conversion: + kwArgs = pragmas; + + //Mix in a specific lifecycle scoped object, to allow targeting + //some pragmas/has tests to only when files are saved, or at different + //lifecycle events. Do not bother with kwArgs in this section, since + //the old dojo kwArgs were for all points in the build lifecycle. + if (onLifecycleName) { + lifecyclePragmas = config['pragmas' + onLifecycleName]; + lifecycleHas = config['has' + onLifecycleName]; + + if (lifecyclePragmas) { + pragmas = create(pragmas || {}, lifecyclePragmas); + } + + if (lifecycleHas) { + hasConfig = create(hasConfig || {}, lifecycleHas); + } + } + + //Replace has references if desired + if (hasConfig) { + fileContents = fileContents.replace(pragma.hasRegExp, function (match, test) { + if (hasConfig.hasOwnProperty(test)) { + return !!hasConfig[test]; + } + return match; + }); + } + + if (!config.skipPragmas) { + + while ((foundIndex = fileContents.indexOf("//>>", startIndex)) !== -1) { + //Found a conditional. Get the conditional line. + lineEndIndex = fileContents.indexOf("\n", foundIndex); + if (lineEndIndex === -1) { + lineEndIndex = fileContents.length - 1; + } + + //Increment startIndex past the line so the next conditional search can be done. + startIndex = lineEndIndex + 1; + + //Break apart the conditional. + conditionLine = fileContents.substring(foundIndex, lineEndIndex + 1); + matches = conditionLine.match(pragma.conditionalRegExp); + if (matches) { + type = matches[1]; + marker = matches[2]; + condition = matches[3]; + isTrue = false; + //See if the condition is true. + try { + isTrue = !!eval("(" + condition + ")"); + } catch (e) { + throw "Error in file: " + + fileName + + ". Conditional comment: " + + conditionLine + + " failed with this error: " + e; + } + + //Find the endpoint marker. + endRegExp = new RegExp('\\/\\/\\>\\>\\s*' + type + 'End\\(\\s*[\'"]' + marker + '[\'"]\\s*\\)', "g"); + endMatches = endRegExp.exec(fileContents.substring(startIndex, fileContents.length)); + if (endMatches) { + endMarkerIndex = startIndex + endRegExp.lastIndex - endMatches[0].length; + + //Find the next line return based on the match position. + lineEndIndex = fileContents.indexOf("\n", endMarkerIndex); + if (lineEndIndex === -1) { + lineEndIndex = fileContents.length - 1; + } + + //Should we include the segment? + shouldInclude = ((type === "exclude" && !isTrue) || (type === "include" && isTrue)); + + //Remove the conditional comments, and optionally remove the content inside + //the conditional comments. + startLength = startIndex - foundIndex; + fileContents = fileContents.substring(0, foundIndex) + + (shouldInclude ? fileContents.substring(startIndex, endMarkerIndex) : "") + + fileContents.substring(lineEndIndex + 1, fileContents.length); + + //Move startIndex to foundIndex, since that is the new position in the file + //where we need to look for more conditionals in the next while loop pass. + startIndex = foundIndex; + } else { + throw "Error in file: " + + fileName + + ". Cannot find end marker for conditional comment: " + + conditionLine; + + } + } + } + } + + //If need to find all plugin resources to optimize, do that now, + //before namespacing, since the namespacing will change the API + //names. + //If there is a plugin collector, scan the file for plugin resources. + if (config.optimizeAllPluginResources && pluginCollector) { + try { + deps = parse.findDependencies(fileName, fileContents); + if (deps.length) { + for (i = 0; i < deps.length; i++) { + dep = deps[i]; + if (dep.indexOf('!') !== -1) { + moduleName = dep.split('!')[0]; + collectorMod = pluginCollector[moduleName]; + if (!collectorMod) { + collectorMod = pluginCollector[moduleName] = []; + } + collectorMod.push(dep); + } + } + } + } catch (eDep) { + logger.error('Parse error looking for plugin resources in ' + + fileName + ', skipping.'); + } + } + + //Strip amdefine use for node-shared modules. + if (!config.keepAmdefine) { + fileContents = fileContents.replace(pragma.amdefineRegExp, ''); + } + + //Do namespacing + if (onLifecycleName === 'OnSave' && config.namespace) { + fileContents = pragma.namespace(fileContents, config.namespace, onLifecycleName); + } + + + return pragma.removeStrict(fileContents, config); + } + }; + + return pragma; +}); + +if(env === 'browser') { +/*jslint strict: false */ +/*global define: false */ + +define('browser/optimize', {}); + +} + +if(env === 'node') { +/*jslint strict: false */ +/*global define: false */ + +define('node/optimize', {}); + +} + +if(env === 'rhino') { +/*jslint sloppy: true, plusplus: true */ +/*global define, java, Packages, com */ + +define('rhino/optimize', ['logger', 'env!env/file'], function (logger, file) { + + //Add .reduce to Rhino so UglifyJS can run in Rhino, + //inspired by https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce + //but rewritten for brevity, and to be good enough for use by UglifyJS. + if (!Array.prototype.reduce) { + Array.prototype.reduce = function (fn /*, initialValue */) { + var i = 0, + length = this.length, + accumulator; + + if (arguments.length >= 2) { + accumulator = arguments[1]; + } else { + if (length) { + while (!(i in this)) { + i++; + } + accumulator = this[i++]; + } + } + + for (; i < length; i++) { + if (i in this) { + accumulator = fn.call(undefined, accumulator, this[i], i, this); + } + } + + return accumulator; + }; + } + + var JSSourceFilefromCode, optimize, + mapRegExp = /"file":"[^"]+"/; + + //Bind to Closure compiler, but if it is not available, do not sweat it. + try { + // Try older closure compiler that worked on Java 6 + JSSourceFilefromCode = java.lang.Class.forName('com.google.javascript.jscomp.JSSourceFile').getMethod('fromCode', [java.lang.String, java.lang.String]); + } catch (e) { + try { + // Try for newer closure compiler that needs Java 7+ + JSSourceFilefromCode = java.lang.Class.forName('com.google.javascript.jscomp.SourceFile').getMethod('fromCode', [java.lang.String, java.lang.String]); + } catch (e) { + try { + // Try Nashorn style + var stringClass = Java.type("java.lang.String").class; + JSSourceFilefromCode = Java.type("com.google.javascript.jscomp.SourceFile").class.getMethod("fromCode", [stringClass, stringClass]); + } catch (e) {} + } + } + + //Helper for closure compiler, because of weird Java-JavaScript interactions. + function closurefromCode(filename, content) { + return JSSourceFilefromCode.invoke(null, [filename, content]); + } + + + function getFileWriter(fileName, encoding) { + var outFile = new java.io.File(fileName), outWriter, parentDir; + + parentDir = outFile.getAbsoluteFile().getParentFile(); + if (!parentDir.exists()) { + if (!parentDir.mkdirs()) { + throw "Could not create directory: " + parentDir.getAbsolutePath(); + } + } + + if (encoding) { + outWriter = new java.io.OutputStreamWriter(new java.io.FileOutputStream(outFile), encoding); + } else { + outWriter = new java.io.OutputStreamWriter(new java.io.FileOutputStream(outFile)); + } + + return new java.io.BufferedWriter(outWriter); + } + + optimize = { + closure: function (fileName, fileContents, outFileName, keepLines, config) { + config = config || {}; + var result, mappings, optimized, compressed, baseName, writer, + outBaseName, outFileNameMap, outFileNameMapContent, + srcOutFileName, concatNameMap, + jscomp = Packages.com.google.javascript.jscomp, + flags = Packages.com.google.common.flags, + //Set up source input + jsSourceFile = closurefromCode(String(fileName), String(fileContents)), + sourceListArray = new java.util.ArrayList(), + externList = new java.util.ArrayList(), + options, option, FLAG_compilation_level, compiler, externExportsPath, + Compiler = Packages.com.google.javascript.jscomp.Compiler, + CommandLineRunner = Packages.com.google.javascript.jscomp.CommandLineRunner; + + logger.trace("Minifying file: " + fileName); + + baseName = (new java.io.File(fileName)).getName(); + + //Set up options + options = new jscomp.CompilerOptions(); + for (option in config.CompilerOptions) { + // options are false by default and jslint wanted an if statement in this for loop + if (config.CompilerOptions[option]) { + options[option] = config.CompilerOptions[option]; + } + + } + options.prettyPrint = keepLines || options.prettyPrint; + + FLAG_compilation_level = jscomp.CompilationLevel[config.CompilationLevel || 'SIMPLE_OPTIMIZATIONS']; + FLAG_compilation_level.setOptionsForCompilationLevel(options); + + if (config.generateSourceMaps) { + mappings = new java.util.ArrayList(); + + mappings.add(new com.google.javascript.jscomp.SourceMap.LocationMapping(fileName, baseName + ".src.js")); + options.setSourceMapLocationMappings(mappings); + options.setSourceMapOutputPath(fileName + ".map"); + } + + //If we need to pass an externs file to Closure so that it does not create aliases + //for certain symbols, do so here. + externList.addAll(CommandLineRunner.getDefaultExterns()); + if (config.externExportsPath) { + externExportsPath = config.externExportsPath; + externList.add(jscomp.SourceFile.fromFile(externExportsPath)); + } + + //Trigger the compiler + Compiler.setLoggingLevel(Packages.java.util.logging.Level[config.loggingLevel || 'WARNING']); + compiler = new Compiler(); + + //fill the sourceArrrayList; we need the ArrayList because the only overload of compile + //accepting the getDefaultExterns return value (a List) also wants the sources as a List + sourceListArray.add(jsSourceFile); + + result = compiler.compile(externList, sourceListArray, options); + if (result.success) { + optimized = String(compiler.toSource()); + + if (config.generateSourceMaps && result.sourceMap && outFileName) { + outBaseName = (new java.io.File(outFileName)).getName(); + + srcOutFileName = outFileName + ".src.js"; + outFileNameMap = outFileName + ".map"; + + //If previous .map file exists, move it to the ".src.js" + //location. Need to update the sourceMappingURL part in the + //src.js file too. + if (file.exists(outFileNameMap)) { + concatNameMap = outFileNameMap.replace(/\.map$/, '.src.js.map'); + file.saveFile(concatNameMap, file.readFile(outFileNameMap)); + file.saveFile(srcOutFileName, + fileContents.replace(/\/\# sourceMappingURL=(.+).map/, + '/# sourceMappingURL=$1.src.js.map')); + } else { + file.saveUtf8File(srcOutFileName, fileContents); + } + + writer = getFileWriter(outFileNameMap, "utf-8"); + result.sourceMap.appendTo(writer, outFileName); + writer.close(); + + //Not sure how better to do this, but right now the .map file + //leaks the full OS path in the "file" property. Manually + //modify it to not do that. + file.saveFile(outFileNameMap, + file.readFile(outFileNameMap).replace(mapRegExp, '"file":"' + baseName + '"')); + + fileContents = optimized + "\n//# sourceMappingURL=" + outBaseName + ".map"; + } else { + fileContents = optimized; + } + return fileContents; + } else { + throw new Error('Cannot closure compile file: ' + fileName + '. Skipping it.'); + } + + return fileContents; + } + }; + + return optimize; +}); +} + +if(env === 'xpconnect') { +define('xpconnect/optimize', {}); +} +/*jslint plusplus: true, nomen: true, regexp: true */ +/*global define: false */ + +define('optimize', [ 'lang', 'logger', 'env!env/optimize', 'env!env/file', 'parse', + 'pragma', 'uglifyjs', + 'source-map'], +function (lang, logger, envOptimize, file, parse, + pragma, uglify, + sourceMap) { + 'use strict'; + + var optimize, + cssImportRegExp = /\@import\s+(url\()?\s*([^);]+)\s*(\))?([\w, ]*)(;)?/ig, + cssCommentImportRegExp = /\/\*[^\*]*@import[^\*]*\*\//g, + cssUrlRegExp = /\url\(\s*([^\)]+)\s*\)?/g, + protocolRegExp = /^\w+:/, + SourceMapGenerator = sourceMap.SourceMapGenerator, + SourceMapConsumer =sourceMap.SourceMapConsumer; + + /** + * If an URL from a CSS url value contains start/end quotes, remove them. + * This is not done in the regexp, since my regexp fu is not that strong, + * and the CSS spec allows for ' and " in the URL if they are backslash escaped. + * @param {String} url + */ + function cleanCssUrlQuotes(url) { + //Make sure we are not ending in whitespace. + //Not very confident of the css regexps above that there will not be ending + //whitespace. + url = url.replace(/\s+$/, ""); + + if (url.charAt(0) === "'" || url.charAt(0) === "\"") { + url = url.substring(1, url.length - 1); + } + + return url; + } + + function fixCssUrlPaths(fileName, path, contents, cssPrefix) { + return contents.replace(cssUrlRegExp, function (fullMatch, urlMatch) { + var firstChar, hasProtocol, parts, i, + fixedUrlMatch = cleanCssUrlQuotes(urlMatch); + + fixedUrlMatch = fixedUrlMatch.replace(lang.backSlashRegExp, "/"); + + //Only do the work for relative URLs. Skip things that start with / or #, or have + //a protocol. + firstChar = fixedUrlMatch.charAt(0); + hasProtocol = protocolRegExp.test(fixedUrlMatch); + if (firstChar !== "/" && firstChar !== "#" && !hasProtocol) { + //It is a relative URL, tack on the cssPrefix and path prefix + urlMatch = cssPrefix + path + fixedUrlMatch; + } else if (!hasProtocol) { + logger.trace(fileName + "\n URL not a relative URL, skipping: " + urlMatch); + } + + //Collapse .. and . + parts = urlMatch.split("/"); + for (i = parts.length - 1; i > 0; i--) { + if (parts[i] === ".") { + parts.splice(i, 1); + } else if (parts[i] === "..") { + if (i !== 0 && parts[i - 1] !== "..") { + parts.splice(i - 1, 2); + i -= 1; + } + } + } + + return "url(" + parts.join("/") + ")"; + }); + } + + /** + * Inlines nested stylesheets that have @import calls in them. + * @param {String} fileName the file name + * @param {String} fileContents the file contents + * @param {String} cssImportIgnore comma delimited string of files to ignore + * @param {String} cssPrefix string to be prefixed before relative URLs + * @param {Object} included an object used to track the files already imported + */ + function flattenCss(fileName, fileContents, cssImportIgnore, cssPrefix, included, topLevel) { + //Find the last slash in the name. + fileName = fileName.replace(lang.backSlashRegExp, "/"); + var endIndex = fileName.lastIndexOf("/"), + //Make a file path based on the last slash. + //If no slash, so must be just a file name. Use empty string then. + filePath = (endIndex !== -1) ? fileName.substring(0, endIndex + 1) : "", + //store a list of merged files + importList = [], + skippedList = []; + + //First make a pass by removing any commented out @import calls. + fileContents = fileContents.replace(cssCommentImportRegExp, ''); + + //Make sure we have a delimited ignore list to make matching faster + if (cssImportIgnore && cssImportIgnore.charAt(cssImportIgnore.length - 1) !== ",") { + cssImportIgnore += ","; + } + + fileContents = fileContents.replace(cssImportRegExp, function (fullMatch, urlStart, importFileName, urlEnd, mediaTypes) { + //Only process media type "all" or empty media type rules. + if (mediaTypes && ((mediaTypes.replace(/^\s\s*/, '').replace(/\s\s*$/, '')) !== "all")) { + skippedList.push(fileName); + return fullMatch; + } + + importFileName = cleanCssUrlQuotes(importFileName); + + //Ignore the file import if it is part of an ignore list. + if (cssImportIgnore && cssImportIgnore.indexOf(importFileName + ",") !== -1) { + return fullMatch; + } + + //Make sure we have a unix path for the rest of the operation. + importFileName = importFileName.replace(lang.backSlashRegExp, "/"); + + try { + //if a relative path, then tack on the filePath. + //If it is not a relative path, then the readFile below will fail, + //and we will just skip that import. + var fullImportFileName = importFileName.charAt(0) === "/" ? importFileName : filePath + importFileName, + importContents = file.readFile(fullImportFileName), + importEndIndex, importPath, flat; + + //Skip the file if it has already been included. + if (included[fullImportFileName]) { + return ''; + } + included[fullImportFileName] = true; + + //Make sure to flatten any nested imports. + flat = flattenCss(fullImportFileName, importContents, cssImportIgnore, cssPrefix, included); + importContents = flat.fileContents; + + if (flat.importList.length) { + importList.push.apply(importList, flat.importList); + } + if (flat.skippedList.length) { + skippedList.push.apply(skippedList, flat.skippedList); + } + + //Make the full import path + importEndIndex = importFileName.lastIndexOf("/"); + + //Make a file path based on the last slash. + //If no slash, so must be just a file name. Use empty string then. + importPath = (importEndIndex !== -1) ? importFileName.substring(0, importEndIndex + 1) : ""; + + //fix url() on relative import (#5) + importPath = importPath.replace(/^\.\//, ''); + + //Modify URL paths to match the path represented by this file. + importContents = fixCssUrlPaths(importFileName, importPath, importContents, cssPrefix); + + importList.push(fullImportFileName); + return importContents; + } catch (e) { + logger.warn(fileName + "\n Cannot inline css import, skipping: " + importFileName); + return fullMatch; + } + }); + + if (cssPrefix && topLevel) { + //Modify URL paths to match the path represented by this file. + fileContents = fixCssUrlPaths(fileName, '', fileContents, cssPrefix); + } + + return { + importList : importList, + skippedList: skippedList, + fileContents : fileContents + }; + } + + optimize = { + /** + * Optimizes a file that contains JavaScript content. Optionally collects + * plugin resources mentioned in a file, and then passes the content + * through an minifier if one is specified via config.optimize. + * + * @param {String} fileName the name of the file to optimize + * @param {String} fileContents the contents to optimize. If this is + * a null value, then fileName will be used to read the fileContents. + * @param {String} outFileName the name of the file to use for the + * saved optimized content. + * @param {Object} config the build config object. + * @param {Array} [pluginCollector] storage for any plugin resources + * found. + */ + jsFile: function (fileName, fileContents, outFileName, config, pluginCollector) { + if (!fileContents) { + fileContents = file.readFile(fileName); + } + + fileContents = optimize.js(fileName, fileContents, outFileName, config, pluginCollector); + + file.saveUtf8File(outFileName, fileContents); + }, + + /** + * Optimizes a file that contains JavaScript content. Optionally collects + * plugin resources mentioned in a file, and then passes the content + * through an minifier if one is specified via config.optimize. + * + * @param {String} fileName the name of the file that matches the + * fileContents. + * @param {String} fileContents the string of JS to optimize. + * @param {Object} [config] the build config object. + * @param {Array} [pluginCollector] storage for any plugin resources + * found. + */ + js: function (fileName, fileContents, outFileName, config, pluginCollector) { + var optFunc, optConfig, + parts = (String(config.optimize)).split('.'), + optimizerName = parts[0], + keepLines = parts[1] === 'keepLines', + licenseContents = ''; + + config = config || {}; + + //Apply pragmas/namespace renaming + fileContents = pragma.process(fileName, fileContents, config, 'OnSave', pluginCollector); + + //Optimize the JS files if asked. + if (optimizerName && optimizerName !== 'none') { + optFunc = envOptimize[optimizerName] || optimize.optimizers[optimizerName]; + if (!optFunc) { + throw new Error('optimizer with name of "' + + optimizerName + + '" not found for this environment'); + } + + optConfig = config[optimizerName] || {}; + if (config.generateSourceMaps) { + optConfig.generateSourceMaps = !!config.generateSourceMaps; + optConfig._buildSourceMap = config._buildSourceMap; + } + + try { + if (config.preserveLicenseComments) { + //Pull out any license comments for prepending after optimization. + try { + licenseContents = parse.getLicenseComments(fileName, fileContents); + } catch (e) { + throw new Error('Cannot parse file: ' + fileName + ' for comments. Skipping it. Error is:\n' + e.toString()); + } + } + + if (config.generateSourceMaps && licenseContents) { + optConfig.preamble = licenseContents; + licenseContents = ''; + } + + fileContents = licenseContents + optFunc(fileName, + fileContents, + outFileName, + keepLines, + optConfig); + if (optConfig._buildSourceMap && optConfig._buildSourceMap !== config._buildSourceMap) { + config._buildSourceMap = optConfig._buildSourceMap; + } + } catch (e) { + if (config.throwWhen && config.throwWhen.optimize) { + throw e; + } else { + logger.error(e); + } + } + } else { + if (config._buildSourceMap) { + config._buildSourceMap = null; + } + } + + return fileContents; + }, + + /** + * Optimizes one CSS file, inlining @import calls, stripping comments, and + * optionally removes line returns. + * @param {String} fileName the path to the CSS file to optimize + * @param {String} outFileName the path to save the optimized file. + * @param {Object} config the config object with the optimizeCss and + * cssImportIgnore options. + */ + cssFile: function (fileName, outFileName, config) { + + //Read in the file. Make sure we have a JS string. + var originalFileContents = file.readFile(fileName), + flat = flattenCss(fileName, originalFileContents, config.cssImportIgnore, config.cssPrefix, {}, true), + //Do not use the flattened CSS if there was one that was skipped. + fileContents = flat.skippedList.length ? originalFileContents : flat.fileContents, + startIndex, endIndex, buildText, comment; + + if (flat.skippedList.length) { + logger.warn('Cannot inline @imports for ' + fileName + + ',\nthe following files had media queries in them:\n' + + flat.skippedList.join('\n')); + } + + //Do comment removal. + try { + if (config.optimizeCss.indexOf(".keepComments") === -1) { + startIndex = 0; + //Get rid of comments. + while ((startIndex = fileContents.indexOf("/*", startIndex)) !== -1) { + endIndex = fileContents.indexOf("*/", startIndex + 2); + if (endIndex === -1) { + throw "Improper comment in CSS file: " + fileName; + } + comment = fileContents.substring(startIndex, endIndex); + + if (config.preserveLicenseComments && + (comment.indexOf('license') !== -1 || + comment.indexOf('opyright') !== -1 || + comment.indexOf('(c)') !== -1)) { + //Keep the comment, just increment the startIndex + startIndex = endIndex; + } else { + fileContents = fileContents.substring(0, startIndex) + fileContents.substring(endIndex + 2, fileContents.length); + startIndex = 0; + } + } + } + //Get rid of newlines. + if (config.optimizeCss.indexOf(".keepLines") === -1) { + fileContents = fileContents.replace(/[\r\n]/g, " "); + fileContents = fileContents.replace(/\s+/g, " "); + fileContents = fileContents.replace(/\{\s/g, "{"); + fileContents = fileContents.replace(/\s\}/g, "}"); + } else { + //Remove multiple empty lines. + fileContents = fileContents.replace(/(\r\n)+/g, "\r\n"); + fileContents = fileContents.replace(/(\n)+/g, "\n"); + } + //Remove unnecessary whitespace + if (config.optimizeCss.indexOf(".keepWhitespace") === -1) { + //Remove leading and trailing whitespace from lines + fileContents = fileContents.replace(/^[ \t]+/gm, ""); + fileContents = fileContents.replace(/[ \t]+$/gm, ""); + //Remove whitespace after semicolon, colon, curly brackets and commas + fileContents = fileContents.replace(/(;|:|\{|}|,)[ \t]+/g, "$1"); + //Remove whitespace before opening curly brackets + fileContents = fileContents.replace(/[ \t]+(\{)/g, "$1"); + //Truncate double whitespace + fileContents = fileContents.replace(/([ \t])+/g, "$1"); + //Remove empty lines + fileContents = fileContents.replace(/^[ \t]*[\r\n]/gm,''); + } + } catch (e) { + fileContents = originalFileContents; + logger.error("Could not optimized CSS file: " + fileName + ", error: " + e); + } + + file.saveUtf8File(outFileName, fileContents); + + //text output to stdout and/or written to build.txt file + buildText = "\n"+ outFileName.replace(config.dir, "") +"\n----------------\n"; + flat.importList.push(fileName); + buildText += flat.importList.map(function(path){ + return path.replace(config.dir, ""); + }).join("\n"); + + return { + importList: flat.importList, + buildText: buildText +"\n" + }; + }, + + /** + * Optimizes CSS files, inlining @import calls, stripping comments, and + * optionally removes line returns. + * @param {String} startDir the path to the top level directory + * @param {Object} config the config object with the optimizeCss and + * cssImportIgnore options. + */ + css: function (startDir, config) { + var buildText = "", + importList = [], + shouldRemove = config.dir && config.removeCombined, + i, fileName, result, fileList; + if (config.optimizeCss.indexOf("standard") !== -1) { + fileList = file.getFilteredFileList(startDir, /\.css$/, true); + if (fileList) { + for (i = 0; i < fileList.length; i++) { + fileName = fileList[i]; + logger.trace("Optimizing (" + config.optimizeCss + ") CSS file: " + fileName); + result = optimize.cssFile(fileName, fileName, config); + buildText += result.buildText; + if (shouldRemove) { + result.importList.pop(); + importList = importList.concat(result.importList); + } + } + } + + if (shouldRemove) { + importList.forEach(function (path) { + if (file.exists(path)) { + file.deleteFile(path); + } + }); + } + } + return buildText; + }, + + optimizers: { + uglify: function (fileName, fileContents, outFileName, keepLines, config) { + var result, existingMap, resultMap, finalMap, sourceIndex, + uconfig = {}, + existingMapPath = outFileName + '.map', + preamble = config.preamble || "", + baseName = fileName && fileName.split('/').pop(); + + config = config || {}; + + lang.mixin(uconfig, config, true); + + uconfig.fromString = true; + + if (config.preamble) { + uconfig.output = {preamble: config.preamble}; + } + + + if (config.generateSourceMaps && (outFileName || config._buildSourceMap)) { + uconfig.outSourceMap = baseName + '.map'; + + if (config._buildSourceMap) { + existingMap = JSON.parse(config._buildSourceMap); + uconfig.inSourceMap = existingMap; + } else if (file.exists(existingMapPath)) { + uconfig.inSourceMap = existingMapPath; + existingMap = JSON.parse(file.readFile(existingMapPath)); + } + } + + logger.trace("Uglify file: " + fileName); + + try { + //var tempContents = fileContents.replace(/\/\/\# sourceMappingURL=.*$/, ''); + result = uglify.minify(fileContents, uconfig, baseName + '.src.js'); + if (uconfig.outSourceMap && result.map) { + resultMap = result.map; + if (!existingMap && !config._buildSourceMap) { + file.saveFile(outFileName + '.src.js', fileContents); + } + + fileContents = result.code; + + if (config._buildSourceMap) { + config._buildSourceMap = resultMap; + } else { + file.saveFile(outFileName + '.map', resultMap); + } + } else { + fileContents = result.code; + } + } catch (e) { + throw new Error('Cannot uglify file: ' + fileName + '. Skipping it. Error is:\n' + e.toString()); + } + return preamble + fileContents; + } + } + }; + + return optimize; +}); +/* + * This file patches require.js to communicate with the build system. + */ + +//Using sloppy since this uses eval for some code like plugins, +//which may not be strict mode compliant. So if use strict is used +//below they will have strict rules applied and may cause an error. +/*jslint sloppy: true, nomen: true, plusplus: true, regexp: true */ +/*global require, define: true */ + +//NOT asking for require as a dependency since the goal is to modify the +//global require below +define('requirePatch', [ 'env!env/file', 'pragma', 'parse', 'lang', 'logger', 'commonJs', 'prim'], function ( + file, + pragma, + parse, + lang, + logger, + commonJs, + prim +) { + + var allowRun = true, + hasProp = lang.hasProp, + falseProp = lang.falseProp, + getOwn = lang.getOwn, + // Used to strip out use strict from toString()'d functions for the + // shim config since they will explicitly want to not be bound by strict, + // but some envs, explicitly xpcshell, adds a use strict. + useStrictRegExp = /['"]use strict['"];/g, + //Absolute path if starts with /, \, or x: + absoluteUrlRegExp = /^[\/\\]|^\w:/; + + //Turn off throwing on resolution conflict, that was just an older prim + //idea about finding errors early, but does not comply with how promises + //should operate. + prim.hideResolutionConflict = true; + + //This method should be called when the patches to require should take hold. + return function () { + if (!allowRun) { + return; + } + allowRun = false; + + var layerobj, + pluginBuilderRegExp = /(["']?)pluginBuilder(["']?)\s*[=\:]\s*["']([^'"\s]+)["']/, + oldNewContext = require.s.newContext, + oldDef, + + //create local undefined values for module and exports, + //so that when files are evaled in this function they do not + //see the node values used for r.js + exports, + module; + + /** + * Reset "global" build caches that are kept around between + * build layerobj builds. Useful to do when there are multiple + * top level requirejs.optimize() calls. + */ + require._cacheReset = function () { + //Stored raw text caches, used by browser use. + require._cachedRawText = {}; + //Stored cached file contents for reuse in other layerobjs. + require._cachedFileContents = {}; + //Store which cached files contain a require definition. + require._cachedDefinesRequireUrls = {}; + }; + require._cacheReset(); + + /** + * Makes sure the URL is something that can be supported by the + * optimization tool. + * @param {String} url + * @returns {Boolean} + */ + require._isSupportedBuildUrl = function (url) { + //Ignore URLs with protocols, hosts or question marks, means either network + //access is needed to fetch it or it is too dynamic. Note that + //on Windows, full paths are used for some urls, which include + //the drive, like c:/something, so need to test for something other + //than just a colon. + if (url.indexOf("://") === -1 && url.indexOf("?") === -1 && + url.indexOf('empty:') !== 0 && url.indexOf('//') !== 0) { + return true; + } else { + if (!layerobj.ignoredUrls[url]) { + if (url.indexOf('empty:') === -1) { + logger.info('Cannot optimize network URL, skipping: ' + url); + } + layerobj.ignoredUrls[url] = true; + } + return false; + } + }; + + function normalizeUrlWithBase(context, moduleName, url) { + //Adjust the URL if it was not transformed to use baseUrl, but only + //if the URL is not already an absolute path. + if (require.jsExtRegExp.test(moduleName) && + !absoluteUrlRegExp.test(url)) { + url = (context.config.dir || context.config.dirBaseUrl) + url; + } + return url; + } + + //Overrides the new context call to add existing tracking features. + require.s.newContext = function (name) { + var context = oldNewContext(name), + oldEnable = context.enable, + moduleProto = context.Module.prototype, + oldInit = moduleProto.init, + oldCallPlugin = moduleProto.callPlugin; + + //Only do this for the context used for building. + if (name === '_') { + //For build contexts, do everything sync + context.nextTick = function (fn) { + fn(); + }; + + context.needFullExec = {}; + context.fullExec = {}; + context.plugins = {}; + context.buildShimExports = {}; + + //Override the shim exports function generator to just + //spit out strings that can be used in the stringified + //build output. + context.makeShimExports = function (value) { + var fn; + if (context.config.wrapShim) { + fn = function () { + var str = 'return '; + // If specifies an export that is just a global + // name, no dot for a `this.` and such, then also + // attach to the global, for `var a = {}` files + // where the function closure would hide that from + // the global object. + if (value.exports && value.exports.indexOf('.') === -1) { + str += 'root.' + value.exports + ' = '; + } + + if (value.init) { + str += '(' + value.init.toString() + .replace(useStrictRegExp, '') + '.apply(this, arguments))'; + } + if (value.init && value.exports) { + str += ' || '; + } + if (value.exports) { + str += value.exports; + } + str += ';'; + return str; + }; + } else { + fn = function () { + return '(function (global) {\n' + + ' return function () {\n' + + ' var ret, fn;\n' + + (value.init ? + (' fn = ' + value.init.toString() + .replace(useStrictRegExp, '') + ';\n' + + ' ret = fn.apply(global, arguments);\n') : '') + + (value.exports ? + ' return ret || global.' + value.exports + ';\n' : + ' return ret;\n') + + ' };\n' + + '}(this))'; + }; + } + + return fn; + }; + + context.enable = function (depMap, parent) { + var id = depMap.id, + parentId = parent && parent.map.id, + needFullExec = context.needFullExec, + fullExec = context.fullExec, + mod = getOwn(context.registry, id); + + if (mod && !mod.defined) { + if (parentId && getOwn(needFullExec, parentId)) { + needFullExec[id] = depMap; + } + + } else if ((getOwn(needFullExec, id) && falseProp(fullExec, id)) || + (parentId && getOwn(needFullExec, parentId) && + falseProp(fullExec, id))) { + context.require.undef(id); + } + + return oldEnable.apply(context, arguments); + }; + + //Override load so that the file paths can be collected. + context.load = function (moduleName, url) { + /*jslint evil: true */ + var contents, pluginBuilderMatch, builderName, + shim, shimExports; + + //Do not mark the url as fetched if it is + //not an empty: URL, used by the optimizer. + //In that case we need to be sure to call + //load() for each module that is mapped to + //empty: so that dependencies are satisfied + //correctly. + if (url.indexOf('empty:') === 0) { + delete context.urlFetched[url]; + } + + //Only handle urls that can be inlined, so that means avoiding some + //URLs like ones that require network access or may be too dynamic, + //like JSONP + if (require._isSupportedBuildUrl(url)) { + //Adjust the URL if it was not transformed to use baseUrl. + url = normalizeUrlWithBase(context, moduleName, url); + + //Save the module name to path and path to module name mappings. + layerobj.buildPathMap[moduleName] = url; + layerobj.buildFileToModule[url] = moduleName; + + if (hasProp(context.plugins, moduleName)) { + //plugins need to have their source evaled as-is. + context.needFullExec[moduleName] = true; + } + + prim().start(function () { + if (hasProp(require._cachedFileContents, url) && + (falseProp(context.needFullExec, moduleName) || + getOwn(context.fullExec, moduleName))) { + contents = require._cachedFileContents[url]; + + //If it defines require, mark it so it can be hoisted. + //Done here and in the else below, before the + //else block removes code from the contents. + //Related to #263 + if (!layerobj.existingRequireUrl && require._cachedDefinesRequireUrls[url]) { + layerobj.existingRequireUrl = url; + } + } else { + //Load the file contents, process for conditionals, then + //evaluate it. + return require._cacheReadAsync(url).then(function (text) { + contents = text; + + if (context.config.cjsTranslate && + (!context.config.shim || !lang.hasProp(context.config.shim, moduleName))) { + contents = commonJs.convert(url, contents); + } + + //If there is a read filter, run it now. + if (context.config.onBuildRead) { + contents = context.config.onBuildRead(moduleName, url, contents); + } + + contents = pragma.process(url, contents, context.config, 'OnExecute'); + + //Find out if the file contains a require() definition. Need to know + //this so we can inject plugins right after it, but before they are needed, + //and to make sure this file is first, so that define calls work. + try { + if (!layerobj.existingRequireUrl && parse.definesRequire(url, contents)) { + layerobj.existingRequireUrl = url; + require._cachedDefinesRequireUrls[url] = true; + } + } catch (e1) { + throw new Error('Parse error using esprima ' + + 'for file: ' + url + '\n' + e1); + } + }).then(function () { + if (hasProp(context.plugins, moduleName)) { + //This is a loader plugin, check to see if it has a build extension, + //otherwise the plugin will act as the plugin builder too. + pluginBuilderMatch = pluginBuilderRegExp.exec(contents); + if (pluginBuilderMatch) { + //Load the plugin builder for the plugin contents. + builderName = context.makeModuleMap(pluginBuilderMatch[3], + context.makeModuleMap(moduleName), + null, + true).id; + return require._cacheReadAsync(context.nameToUrl(builderName)); + } + } + return contents; + }).then(function (text) { + contents = text; + + //Parse out the require and define calls. + //Do this even for plugins in case they have their own + //dependencies that may be separate to how the pluginBuilder works. + try { + if (falseProp(context.needFullExec, moduleName)) { + contents = parse(moduleName, url, contents, { + insertNeedsDefine: true, + has: context.config.has, + findNestedDependencies: context.config.findNestedDependencies + }); + } + } catch (e2) { + throw new Error('Parse error using esprima ' + + 'for file: ' + url + '\n' + e2); + } + + require._cachedFileContents[url] = contents; + }); + } + }).then(function () { + if (contents) { + eval(contents); + } + + try { + //If have a string shim config, and this is + //a fully executed module, try to see if + //it created a variable in this eval scope + if (getOwn(context.needFullExec, moduleName)) { + shim = getOwn(context.config.shim, moduleName); + if (shim && shim.exports) { + shimExports = eval(shim.exports); + if (typeof shimExports !== 'undefined') { + context.buildShimExports[moduleName] = shimExports; + } + } + } + + //Need to close out completion of this module + //so that listeners will get notified that it is available. + context.completeLoad(moduleName); + } catch (e) { + //Track which module could not complete loading. + if (!e.moduleTree) { + e.moduleTree = []; + } + e.moduleTree.push(moduleName); + throw e; + } + }).then(null, function (eOuter) { + + if (!eOuter.fileName) { + eOuter.fileName = url; + } + throw eOuter; + }).end(); + } else { + //With unsupported URLs still need to call completeLoad to + //finish loading. + context.completeLoad(moduleName); + } + }; + + //Marks module has having a name, and optionally executes the + //callback, but only if it meets certain criteria. + context.execCb = function (name, cb, args, exports) { + var buildShimExports = getOwn(layerobj.context.buildShimExports, name); + + if (buildShimExports) { + return buildShimExports; + } else if (cb.__requireJsBuild || getOwn(layerobj.context.needFullExec, name)) { + return cb.apply(exports, args); + } + return undefined; + }; + + moduleProto.init = function (depMaps) { + if (context.needFullExec[this.map.id]) { + lang.each(depMaps, lang.bind(this, function (depMap) { + if (typeof depMap === 'string') { + depMap = context.makeModuleMap(depMap, + (this.map.isDefine ? this.map : this.map.parentMap)); + } + + if (!context.fullExec[depMap.id]) { + context.require.undef(depMap.id); + } + })); + } + + return oldInit.apply(this, arguments); + }; + + moduleProto.callPlugin = function () { + var map = this.map, + pluginMap = context.makeModuleMap(map.prefix), + pluginId = pluginMap.id, + pluginMod = getOwn(context.registry, pluginId); + + context.plugins[pluginId] = true; + context.needFullExec[pluginId] = map; + + //If the module is not waiting to finish being defined, + //undef it and start over, to get full execution. + if (falseProp(context.fullExec, pluginId) && (!pluginMod || pluginMod.defined)) { + context.require.undef(pluginMap.id); + } + + return oldCallPlugin.apply(this, arguments); + }; + } + + return context; + }; + + //Clear up the existing context so that the newContext modifications + //above will be active. + delete require.s.contexts._; + + /** Reset state for each build layerobj pass. */ + require._buildReset = function () { + var oldContext = require.s.contexts._; + + //Clear up the existing context. + delete require.s.contexts._; + + //Set up new context, so the layerobj object can hold onto it. + require({}); + + layerobj = require._layerobj = { + buildPathMap: {}, + buildFileToModule: {}, + buildFilePaths: [], + pathAdded: {}, + modulesWithNames: {}, + needsDefine: {}, + existingRequireUrl: "", + ignoredUrls: {}, + context: require.s.contexts._ + }; + + //Return the previous context in case it is needed, like for + //the basic config object. + return oldContext; + }; + + require._buildReset(); + + //Override define() to catch modules that just define an object, so that + //a dummy define call is not put in the build file for them. They do + //not end up getting defined via context.execCb, so we need to catch them + //at the define call. + oldDef = define; + + //This function signature does not have to be exact, just match what we + //are looking for. + define = function (name) { + if (typeof name === "string" && falseProp(layerobj.needsDefine, name)) { + layerobj.modulesWithNames[name] = true; + } + return oldDef.apply(require, arguments); + }; + + define.amd = oldDef.amd; + + //Add some utilities for plugins + require._readFile = file.readFile; + require._fileExists = function (path) { + return file.exists(path); + }; + + //Called when execManager runs for a dependency. Used to figure out + //what order of execution. + require.onResourceLoad = function (context, map) { + var id = map.id, + url; + + // Fix up any maps that need to be normalized as part of the fullExec + // plumbing for plugins to participate in the build. + if (context.plugins && lang.hasProp(context.plugins, id)) { + lang.eachProp(context.needFullExec, function(value, prop) { + // For plugin entries themselves, they do not have a map + // value in needFullExec, just a "true" entry. + if (value !== true && value.prefix === id && value.unnormalized) { + var map = context.makeModuleMap(value.originalName, value.parentMap); + context.needFullExec[map.id] = map; + } + }); + } + + //If build needed a full execution, indicate it + //has been done now. But only do it if the context is tracking + //that. Only valid for the context used in a build, not for + //other contexts being run, like for useLib, plain requirejs + //use in node/rhino. + if (context.needFullExec && getOwn(context.needFullExec, id)) { + context.fullExec[id] = map; + } + + //A plugin. + if (map.prefix) { + if (falseProp(layerobj.pathAdded, id)) { + layerobj.buildFilePaths.push(id); + //For plugins the real path is not knowable, use the name + //for both module to file and file to module mappings. + layerobj.buildPathMap[id] = id; + layerobj.buildFileToModule[id] = id; + layerobj.modulesWithNames[id] = true; + layerobj.pathAdded[id] = true; + } + } else if (map.url && require._isSupportedBuildUrl(map.url)) { + //If the url has not been added to the layerobj yet, and it + //is from an actual file that was loaded, add it now. + url = normalizeUrlWithBase(context, id, map.url); + if (!layerobj.pathAdded[url] && getOwn(layerobj.buildPathMap, id)) { + //Remember the list of dependencies for this layerobj. + layerobj.buildFilePaths.push(url); + layerobj.pathAdded[url] = true; + } + } + }; + + //Called by output of the parse() function, when a file does not + //explicitly call define, probably just require, but the parse() + //function normalizes on define() for dependency mapping and file + //ordering works correctly. + require.needsDefine = function (moduleName) { + layerobj.needsDefine[moduleName] = true; + }; + }; +}); +/*jslint */ +/*global define: false, console: false */ + +define('commonJs', ['env!env/file', 'parse'], function (file, parse) { + 'use strict'; + var commonJs = { + //Set to false if you do not want this file to log. Useful in environments + //like node where you want the work to happen without noise. + useLog: true, + + convertDir: function (commonJsPath, savePath) { + var fileList, i, + jsFileRegExp = /\.js$/, + fileName, convertedFileName, fileContents; + + //Get list of files to convert. + fileList = file.getFilteredFileList(commonJsPath, /\w/, true); + + //Normalize on front slashes and make sure the paths do not end in a slash. + commonJsPath = commonJsPath.replace(/\\/g, "/"); + savePath = savePath.replace(/\\/g, "/"); + if (commonJsPath.charAt(commonJsPath.length - 1) === "/") { + commonJsPath = commonJsPath.substring(0, commonJsPath.length - 1); + } + if (savePath.charAt(savePath.length - 1) === "/") { + savePath = savePath.substring(0, savePath.length - 1); + } + + //Cycle through all the JS files and convert them. + if (!fileList || !fileList.length) { + if (commonJs.useLog) { + if (commonJsPath === "convert") { + //A request just to convert one file. + console.log('\n\n' + commonJs.convert(savePath, file.readFile(savePath))); + } else { + console.log("No files to convert in directory: " + commonJsPath); + } + } + } else { + for (i = 0; i < fileList.length; i++) { + fileName = fileList[i]; + convertedFileName = fileName.replace(commonJsPath, savePath); + + //Handle JS files. + if (jsFileRegExp.test(fileName)) { + fileContents = file.readFile(fileName); + fileContents = commonJs.convert(fileName, fileContents); + file.saveUtf8File(convertedFileName, fileContents); + } else { + //Just copy the file over. + file.copyFile(fileName, convertedFileName, true); + } + } + } + }, + + /** + * Does the actual file conversion. + * + * @param {String} fileName the name of the file. + * + * @param {String} fileContents the contents of a file :) + * + * @returns {String} the converted contents + */ + convert: function (fileName, fileContents) { + //Strip out comments. + try { + var preamble = '', + commonJsProps = parse.usesCommonJs(fileName, fileContents); + + //First see if the module is not already RequireJS-formatted. + if (parse.usesAmdOrRequireJs(fileName, fileContents) || !commonJsProps) { + return fileContents; + } + + if (commonJsProps.dirname || commonJsProps.filename) { + preamble = 'var __filename = module.uri || "", ' + + '__dirname = __filename.substring(0, __filename.lastIndexOf("/") + 1); '; + } + + //Construct the wrapper boilerplate. + fileContents = 'define(function (require, exports, module) {' + + preamble + + fileContents + + '\n});\n'; + + } catch (e) { + console.log("commonJs.convert: COULD NOT CONVERT: " + fileName + ", so skipping it. Error was: " + e); + return fileContents; + } + + return fileContents; + } + }; + + return commonJs; +}); +/*jslint plusplus: true, nomen: true, regexp: true */ +/*global define, requirejs, java, process, console */ + + +define('build', function (require) { + 'use strict'; + + var build, + lang = require('lang'), + prim = require('prim'), + logger = require('logger'), + file = require('env!env/file'), + parse = require('parse'), + optimize = require('optimize'), + pragma = require('pragma'), + transform = require('transform'), + requirePatch = require('requirePatch'), + env = require('env'), + commonJs = require('commonJs'), + SourceMapGenerator = require('source-map').SourceMapGenerator, + hasProp = lang.hasProp, + getOwn = lang.getOwn, + falseProp = lang.falseProp, + endsWithSemiColonRegExp = /;\s*$/, + endsWithSlashRegExp = /[\/\\]$/, + resourceIsModuleIdRegExp = /^[\w\/\\\.]+$/, + deepCopyProps = { + layerobj: true + }; + + //Deep copy a config object, but do not copy over the "layerobj" property, + //as it can be a deeply nested structure with a full requirejs context. + function copyConfig(obj) { + return lang.deeplikeCopy(obj, deepCopyProps); + } + + prim.nextTick = function (fn) { + fn(); + }; + + //Now map require to the outermost requirejs, now that we have + //local dependencies for this module. The rest of the require use is + //manipulating the requirejs loader. + require = requirejs; + + //Caching function for performance. Attached to + //require so it can be reused in requirePatch.js. _cachedRawText + //set up by requirePatch.js + require._cacheReadAsync = function (path, encoding) { + var d; + + if (lang.hasProp(require._cachedRawText, path)) { + d = prim(); + d.resolve(require._cachedRawText[path]); + return d.promise; + } else { + return file.readFileAsync(path, encoding).then(function (text) { + require._cachedRawText[path] = text; + return text; + }); + } + }; + + function makeBuildBaseConfig() { + return { + appDir: "", + pragmas: {}, + paths: {}, + optimize: "uglify", + optimizeCss: "standard.keepLines.keepWhitespace", + inlineText: true, + isBuild: true, + optimizeAllPluginResources: false, + findNestedDependencies: false, + preserveLicenseComments: true, + writeBuildTxt: true, + //Some builds can take a while, up the default limit. + waitSeconds: 30, + //By default, all files/directories are copied, unless + //they match this regexp, by default just excludes .folders + dirExclusionRegExp: file.dirExclusionRegExp, + _buildPathToModuleIndex: {} + }; + } + + /** + * Some JS may not be valid if concatenated with other JS, in particular + * the style of omitting semicolons and rely on ASI. Add a semicolon in + * those cases. + */ + function addSemiColon(text, config) { + if (config.skipSemiColonInsertion || endsWithSemiColonRegExp.test(text)) { + return text; + } else { + return text + ";"; + } + } + + function endsWithSlash(dirName) { + if (dirName.charAt(dirName.length - 1) !== "/") { + dirName += "/"; + } + return dirName; + } + + function endsWithNewLine(text) { + if (text.charAt(text.length - 1) !== "\n") { + text += "\n"; + } + return text; + } + + //Method used by plugin writeFile calls, defined up here to avoid + //jslint warning about "making a function in a loop". + function makeWriteFile(namespace, layerobj) { + function writeFile(name, contents) { + logger.trace('Saving plugin-optimized file: ' + name); + file.saveUtf8File(name, contents); + } + + writeFile.asModule = function (moduleName, fileName, contents) { + writeFile(fileName, + build.toTransport(namespace, moduleName, fileName, contents, layerobj)); + }; + + return writeFile; + } + + /** + * Appends singleContents to fileContents and returns the result. If a sourceMapGenerator + * is provided, adds singleContents to the source map. + * + * @param {string} fileContents - The file contents to which to append singleContents + * @param {string} singleContents - The additional contents to append to fileContents + * @param {string} path - An absolute path of a file whose name to use in the source map. + * The file need not actually exist if the code in singleContents is generated. + * @param {{out: ?string, baseUrl: ?string}} config - The build configuration object. + * @param {?{_buildPath: ?string}} module - An object with module information. + * @param {?SourceMapGenerator} sourceMapGenerator - An instance of Mozilla's SourceMapGenerator, + * or null if no source map is being generated. + * @returns {string} fileContents with singleContents appended + */ + function appendToFileContents(fileContents, singleContents, path, config, module, sourceMapGenerator) { + var refPath, sourceMapPath, resourcePath, pluginId, sourceMapLineNumber, lineCount, parts, i; + if (sourceMapGenerator) { + if (config.out) { + refPath = config.baseUrl; + } else if (module && module._buildPath) { + refPath = module._buildPath; + } else { + refPath = ""; + } + parts = path.split('!'); + if (parts.length === 1) { + //Not a plugin resource, fix the path + sourceMapPath = build.makeRelativeFilePath(refPath, path); + } else { + //Plugin resource. If it looks like just a plugin + //followed by a module ID, pull off the plugin + //and put it at the end of the name, otherwise + //just leave it alone. + pluginId = parts.shift(); + resourcePath = parts.join('!'); + if (resourceIsModuleIdRegExp.test(resourcePath)) { + sourceMapPath = build.makeRelativeFilePath(refPath, require.toUrl(resourcePath)) + + '!' + pluginId; + } else { + sourceMapPath = path; + } + } + + sourceMapLineNumber = fileContents.split('\n').length - 1; + lineCount = singleContents.split('\n').length; + for (i = 1; i <= lineCount; i += 1) { + sourceMapGenerator.addMapping({ + generated: { + line: sourceMapLineNumber + i, + column: 0 + }, + original: { + line: i, + column: 0 + }, + source: sourceMapPath + }); + } + + //Store the content of the original in the source + //map since other transforms later like minification + //can mess up translating back to the original + //source. + sourceMapGenerator.setSourceContent(sourceMapPath, singleContents); + } + fileContents += singleContents; + return fileContents; + } + + /** + * Main API entry point into the build. The args argument can either be + * an array of arguments (like the onese passed on a command-line), + * or it can be a JavaScript object that has the format of a build profile + * file. + * + * If it is an object, then in addition to the normal properties allowed in + * a build profile file, the object should contain one other property: + * + * The object could also contain a "buildFile" property, which is a string + * that is the file path to a build profile that contains the rest + * of the build profile directives. + * + * This function does not return a status, it should throw an error if + * there is a problem completing the build. + */ + build = function (args) { + var buildFile, cmdConfig, errorMsg, errorStack, stackMatch, errorTree, + i, j, errorMod, + stackRegExp = /( {4}at[^\n]+)\n/, + standardIndent = ' '; + + return prim().start(function () { + if (!args || lang.isArray(args)) { + if (!args || args.length < 1) { + logger.error("build.js buildProfile.js\n" + + "where buildProfile.js is the name of the build file (see example.build.js for hints on how to make a build file)."); + return undefined; + } + + //Next args can include a build file path as well as other build args. + //build file path comes first. If it does not contain an = then it is + //a build file path. Otherwise, just all build args. + if (args[0].indexOf("=") === -1) { + buildFile = args[0]; + args.splice(0, 1); + } + + //Remaining args are options to the build + cmdConfig = build.convertArrayToObject(args); + cmdConfig.buildFile = buildFile; + } else { + cmdConfig = args; + } + + return build._run(cmdConfig); + }).then(null, function (e) { + var err; + + errorMsg = e.toString(); + errorTree = e.moduleTree; + stackMatch = stackRegExp.exec(errorMsg); + + if (stackMatch) { + errorMsg += errorMsg.substring(0, stackMatch.index + stackMatch[0].length + 1); + } + + //If a module tree that shows what module triggered the error, + //print it out. + if (errorTree && errorTree.length > 0) { + errorMsg += '\nIn module tree:\n'; + + for (i = errorTree.length - 1; i > -1; i--) { + errorMod = errorTree[i]; + if (errorMod) { + for (j = errorTree.length - i; j > -1; j--) { + errorMsg += standardIndent; + } + errorMsg += errorMod + '\n'; + } + } + + logger.error(errorMsg); + } + + errorStack = e.stack; + + if (typeof args === 'string' && args.indexOf('stacktrace=true') !== -1) { + errorMsg += '\n' + errorStack; + } else { + if (!stackMatch && errorStack) { + //Just trim out the first "at" in the stack. + stackMatch = stackRegExp.exec(errorStack); + if (stackMatch) { + errorMsg += '\n' + stackMatch[0] || ''; + } + } + } + + err = new Error(errorMsg); + err.originalError = e; + throw err; + }); + }; + + build._run = function (cmdConfig) { + var buildPaths, fileName, fileNames, + paths, i, + baseConfig, config, + modules, srcPath, buildContext, + destPath, moduleMap, parentModuleMap, context, + resources, resource, plugin, fileContents, + pluginProcessed = {}, + buildFileContents = "", + pluginCollector = {}; + + return prim().start(function () { + var prop; + + //Can now run the patches to require.js to allow it to be used for + //build generation. Do it here instead of at the top of the module + //because we want normal require behavior to load the build tool + //then want to switch to build mode. + requirePatch(); + + config = build.createConfig(cmdConfig); + paths = config.paths; + + //Remove the previous build dir, in case it contains source transforms, + //like the ones done with onBuildRead and onBuildWrite. + if (config.dir && !config.keepBuildDir && file.exists(config.dir)) { + file.deleteFile(config.dir); + } + + if (!config.out && !config.cssIn) { + //This is not just a one-off file build but a full build profile, with + //lots of files to process. + + //First copy all the baseUrl content + file.copyDir((config.appDir || config.baseUrl), config.dir, /\w/, true); + + //Adjust baseUrl if config.appDir is in play, and set up build output paths. + buildPaths = {}; + if (config.appDir) { + //All the paths should be inside the appDir, so just adjust + //the paths to use the dirBaseUrl + for (prop in paths) { + if (hasProp(paths, prop)) { + buildPaths[prop] = paths[prop].replace(config.appDir, config.dir); + } + } + } else { + //If no appDir, then make sure to copy the other paths to this directory. + for (prop in paths) { + if (hasProp(paths, prop)) { + //Set up build path for each path prefix, but only do so + //if the path falls out of the current baseUrl + if (paths[prop].indexOf(config.baseUrl) === 0) { + buildPaths[prop] = paths[prop].replace(config.baseUrl, config.dirBaseUrl); + } else { + buildPaths[prop] = paths[prop] === 'empty:' ? 'empty:' : prop; + + //Make sure source path is fully formed with baseUrl, + //if it is a relative URL. + srcPath = paths[prop]; + if (srcPath.indexOf('/') !== 0 && srcPath.indexOf(':') === -1) { + srcPath = config.baseUrl + srcPath; + } + + destPath = config.dirBaseUrl + buildPaths[prop]; + + //Skip empty: paths + if (srcPath !== 'empty:') { + //If the srcPath is a directory, copy the whole directory. + if (file.exists(srcPath) && file.isDirectory(srcPath)) { + //Copy files to build area. Copy all files (the /\w/ regexp) + file.copyDir(srcPath, destPath, /\w/, true); + } else { + //Try a .js extension + srcPath += '.js'; + destPath += '.js'; + file.copyFile(srcPath, destPath); + } + } + } + } + } + } + } + + //Figure out source file location for each module layerobj. Do this by seeding require + //with source area configuration. This is needed so that later the module layerobjs + //can be manually copied over to the source area, since the build may be + //require multiple times and the above copyDir call only copies newer files. + require({ + baseUrl: config.baseUrl, + paths: paths, + packagePaths: config.packagePaths, + packages: config.packages + }); + buildContext = require.s.contexts._; + modules = config.modules; + + if (modules) { + modules.forEach(function (module) { + if (module.name) { + module._sourcePath = buildContext.nameToUrl(module.name); + //If the module does not exist, and this is not a "new" module layerobj, + //as indicated by a true "create" property on the module, and + //it is not a plugin-loaded resource, and there is no + //'rawText' containing the module's source then throw an error. + if (!file.exists(module._sourcePath) && !module.create && + module.name.indexOf('!') === -1 && + (!config.rawText || !lang.hasProp(config.rawText, module.name))) { + throw new Error("ERROR: module path does not exist: " + + module._sourcePath + " for module named: " + module.name + + ". Path is relative to: " + file.absPath('.')); + } + } + }); + } + + if (config.out) { + //Just set up the _buildPath for the module layerobj. + require(config); + if (!config.cssIn) { + config.modules[0]._buildPath = typeof config.out === 'function' ? + 'FUNCTION' : config.out; + } + } else if (!config.cssIn) { + //Now set up the config for require to use the build area, and calculate the + //build file locations. Pass along any config info too. + baseConfig = { + baseUrl: config.dirBaseUrl, + paths: buildPaths + }; + + lang.mixin(baseConfig, config); + require(baseConfig); + + if (modules) { + modules.forEach(function (module) { + if (module.name) { + module._buildPath = buildContext.nameToUrl(module.name, null); + + //If buildPath and sourcePath are the same, throw since this + //would result in modifying source. This condition can happen + //with some more tricky paths: config and appDir/baseUrl + //setting, which is a sign of incorrect config. + if (module._buildPath === module._sourcePath && + !config.allowSourceOverwrites) { + throw new Error('Module ID \'' + module.name + + '\' has a source path that is same as output path: ' + + module._sourcePath + + '. Stopping, config is malformed.'); + } + + // Copy the file, but only if it is not provided in rawText. + if (!module.create && (!config.rawText || !lang.hasProp(config.rawText, module.name))) { + file.copyFile(module._sourcePath, module._buildPath); + } + } + }); + } + } + + //Run CSS optimizations before doing JS module tracing, to allow + //things like text loader plugins loading CSS to get the optimized + //CSS. + if (config.optimizeCss && config.optimizeCss !== "none" && config.dir) { + buildFileContents += optimize.css(config.dir, config); + } + }).then(function() { + baseConfig = copyConfig(require.s.contexts._.config); + }).then(function () { + var actions = []; + + if (modules) { + actions = modules.map(function (module, i) { + return function () { + //Save off buildPath to module index in a hash for quicker + //lookup later. + config._buildPathToModuleIndex[file.normalize(module._buildPath)] = i; + + //Call require to calculate dependencies. + return build.traceDependencies(module, config, baseConfig) + .then(function (layerobj) { + module.layerobj = layerobj; + }); + }; + }); + + return prim.serial(actions); + } + }).then(function () { + var actions; + + if (modules) { + //Now build up shadow layerobjs for anything that should be excluded. + //Do this after tracing dependencies for each module, in case one + //of those modules end up being one of the excluded values. + actions = modules.map(function (module) { + return function () { + if (module.exclude) { + module.excludeLayers = []; + return prim.serial(module.exclude.map(function (exclude, i) { + return function () { + //See if it is already in the list of modules. + //If not trace dependencies for it. + var found = build.findBuildModule(exclude, modules); + if (found) { + module.excludeLayers[i] = found; + } else { + return build.traceDependencies({name: exclude}, config, baseConfig) + .then(function (layerobj) { + module.excludeLayers[i] = { layerobj: layerobj }; + }); + } + }; + })); + } + }; + }); + + return prim.serial(actions); + } + }).then(function () { + if (modules) { + return prim.serial(modules.map(function (module) { + return function () { + if (module.exclude) { + //module.exclude is an array of module names. For each one, + //get the nested dependencies for it via a matching entry + //in the module.excludeLayers array. + module.exclude.forEach(function (excludeModule, i) { + var excludeLayer = module.excludeLayers[i].layerobj, + map = excludeLayer.buildFileToModule; + excludeLayer.buildFilePaths.forEach(function(filePath){ + build.removeModulePath(map[filePath], filePath, module.layerobj); + }); + }); + } + if (module.excludeShallow) { + //module.excludeShallow is an array of module names. + //shallow exclusions are just that module itself, and not + //its nested dependencies. + module.excludeShallow.forEach(function (excludeShallowModule) { + var path = getOwn(module.layerobj.buildPathMap, excludeShallowModule); + if (path) { + build.removeModulePath(excludeShallowModule, path, module.layerobj); + } + }); + } + + //Flatten them and collect the build output for each module. + return build.flattenModule(module, module.layerobj, config).then(function (builtModule) { + var finalText, baseName; + //Save it to a temp file for now, in case there are other layerobjs that + //contain optimized content that should not be included in later + //layerobj optimizations. See issue #56. + if (module._buildPath === 'FUNCTION') { + module._buildText = builtModule.text; + module._buildSourceMap = builtModule.sourceMap; + } else { + finalText = builtModule.text; + if (builtModule.sourceMap) { + baseName = module._buildPath.split('/'); + baseName = baseName.pop(); + finalText += '\n//# sourceMappingURL=' + baseName + '.map'; + file.saveUtf8File(module._buildPath + '.map', builtModule.sourceMap); + } + file.saveUtf8File(module._buildPath + '-temp', finalText); + + } + buildFileContents += builtModule.buildText; + }); + }; + })); + } + }).then(function () { + var moduleName, outOrigSourceMap, + bundlesConfig = {}, + bundlesConfigOutFile = config.bundlesConfigOutFile; + + if (modules) { + //Now move the build layerobjs to their final position. + modules.forEach(function (module) { + var entryConfig, + finalPath = module._buildPath; + + if (finalPath !== 'FUNCTION') { + if (file.exists(finalPath)) { + file.deleteFile(finalPath); + } + file.renameFile(finalPath + '-temp', finalPath); + + //If bundles config should be written out, scan the + //built file for module IDs. Favor doing this reparse + //since tracking the IDs as the file is built has some + //edge cases around files that had more than one ID in + //them already, and likely loader plugin-written contents. + if (bundlesConfigOutFile) { + entryConfig = bundlesConfig[module.name] = []; + var bundleContents = file.readFile(finalPath); + var excludeMap = {}; + excludeMap[module.name] = true; + var parsedIds = parse.getAllNamedDefines(bundleContents, excludeMap); + entryConfig.push.apply(entryConfig, parsedIds); + } + + //And finally, if removeCombined is specified, remove + //any of the files that were used in this layerobj. + //Be sure not to remove other build layerobjs. + if (config.removeCombined && !config.out) { + module.layerobj.buildFilePaths.forEach(function (path) { + var isLayer = modules.some(function (mod) { + return mod._buildPath === path; + }), + relPath = build.makeRelativeFilePath(config.dir, path); + + if (file.exists(path) && + // not a build layerobj target + !isLayer && + // not outside the build directory + relPath.indexOf('..') !== 0) { + file.deleteFile(path); + } + }); + } + } + + //Signal layerobj is done + if (config.onModuleBundleComplete) { + config.onModuleBundleComplete(module.onCompleteData); + } + }); + + //Write out bundles config, if it is wanted. + if (bundlesConfigOutFile) { + var text = file.readFile(bundlesConfigOutFile); + text = transform.modifyConfig(text, function (config) { + if (!config.bundles) { + config.bundles = {}; + } + + lang.eachProp(bundlesConfig, function (value, prop) { + config.bundles[prop] = value; + }); + + return config; + }); + + file.saveUtf8File(bundlesConfigOutFile, text); + } + } + + //If removeCombined in play, remove any empty directories that + //may now exist because of its use + if (config.removeCombined && !config.out && config.dir) { + file.deleteEmptyDirs(config.dir); + } + + //Do other optimizations. + if (config.out && !config.cssIn) { + //Just need to worry about one JS file. + fileName = config.modules[0]._buildPath; + if (fileName === 'FUNCTION') { + outOrigSourceMap = config.modules[0]._buildSourceMap; + config._buildSourceMap = outOrigSourceMap; + config.modules[0]._buildText = optimize.js((config.modules[0].name || + config.modules[0].include[0] || + fileName) + '.build.js', + config.modules[0]._buildText, + null, + config); + if (config._buildSourceMap && config._buildSourceMap !== outOrigSourceMap) { + config.modules[0]._buildSourceMap = config._buildSourceMap; + config._buildSourceMap = null; + } + } else { + optimize.jsFile(fileName, null, fileName, config); + } + } else if (!config.cssIn) { + //Normal optimizations across modules. + + //JS optimizations. + fileNames = file.getFilteredFileList(config.dir, /\.js$/, true); + fileNames.forEach(function (fileName) { + var cfg, override, moduleIndex; + + //Generate the module name from the config.dir root. + moduleName = fileName.replace(config.dir, ''); + //Get rid of the extension + moduleName = moduleName.substring(0, moduleName.length - 3); + + //If there is an override for a specific layerobj build module, + //and this file is that module, mix in the override for use + //by optimize.jsFile. + moduleIndex = getOwn(config._buildPathToModuleIndex, fileName); + //Normalize, since getOwn could have returned undefined + moduleIndex = moduleIndex === 0 || moduleIndex > 0 ? moduleIndex : -1; + + //Try to avoid extra work if the other files do not need to + //be read. Build layerobjs should be processed at the very + //least for optimization. + if (moduleIndex > -1 || !config.skipDirOptimize || + config.normalizeDirDefines === "all" || + config.cjsTranslate) { + //Convert the file to transport format, but without a name + //inserted (by passing null for moduleName) since the files are + //standalone, one module per file. + fileContents = file.readFile(fileName); + + + //For builds, if wanting cjs translation, do it now, so that + //the individual modules can be loaded cross domain via + //plain script tags. + if (config.cjsTranslate && + (!config.shim || !lang.hasProp(config.shim, moduleName))) { + fileContents = commonJs.convert(fileName, fileContents); + } + + if (moduleIndex === -1) { + if (config.onBuildRead) { + fileContents = config.onBuildRead(moduleName, + fileName, + fileContents); + } + + //Only do transport normalization if this is not a build + //layerobj (since it was already normalized) and if + //normalizeDirDefines indicated all should be done. + if (config.normalizeDirDefines === "all") { + fileContents = build.toTransport(config.namespace, + null, + fileName, + fileContents); + } + + if (config.onBuildWrite) { + fileContents = config.onBuildWrite(moduleName, + fileName, + fileContents); + } + } + + override = moduleIndex > -1 ? + config.modules[moduleIndex].override : null; + if (override) { + cfg = build.createOverrideConfig(config, override); + } else { + cfg = config; + } + + if (moduleIndex > -1 || !config.skipDirOptimize) { + optimize.jsFile(fileName, fileContents, fileName, cfg, pluginCollector); + } + } + }); + + //Normalize all the plugin resources. + context = require.s.contexts._; + + for (moduleName in pluginCollector) { + if (hasProp(pluginCollector, moduleName)) { + parentModuleMap = context.makeModuleMap(moduleName); + resources = pluginCollector[moduleName]; + for (i = 0; i < resources.length; i++) { + resource = resources[i]; + moduleMap = context.makeModuleMap(resource, parentModuleMap); + if (falseProp(context.plugins, moduleMap.prefix)) { + //Set the value in context.plugins so it + //will be evaluated as a full plugin. + context.plugins[moduleMap.prefix] = true; + + //Do not bother if the plugin is not available. + if (!file.exists(require.toUrl(moduleMap.prefix + '.js'))) { + continue; + } + + //Rely on the require in the build environment + //to be synchronous + context.require([moduleMap.prefix]); + + //Now that the plugin is loaded, redo the moduleMap + //since the plugin will need to normalize part of the path. + moduleMap = context.makeModuleMap(resource, parentModuleMap); + } + + //Only bother with plugin resources that can be handled + //processed by the plugin, via support of the writeFile + //method. + if (falseProp(pluginProcessed, moduleMap.id)) { + //Only do the work if the plugin was really loaded. + //Using an internal access because the file may + //not really be loaded. + plugin = getOwn(context.defined, moduleMap.prefix); + if (plugin && plugin.writeFile) { + plugin.writeFile( + moduleMap.prefix, + moduleMap.name, + require, + makeWriteFile( + config.namespace + ), + context.config + ); + } + + pluginProcessed[moduleMap.id] = true; + } + } + + } + } + + //console.log('PLUGIN COLLECTOR: ' + JSON.stringify(pluginCollector, null, " ")); + + + //All module layerobjs are done, write out the build.txt file. + if (config.writeBuildTxt) { + file.saveUtf8File(config.dir + "build.txt", buildFileContents); + } + } + + //If just have one CSS file to optimize, do that here. + if (config.cssIn) { + buildFileContents += optimize.cssFile(config.cssIn, config.out, config).buildText; + } + + if (typeof config.out === 'function') { + config.out(config.modules[0]._buildText, config.modules[0]._buildSourceMap); + } + + //Print out what was built into which layerobjs. + if (buildFileContents) { + logger.info(buildFileContents); + return buildFileContents; + } + + return ''; + }); + }; + + /** + * Converts command line args like "paths.foo=../some/path" + * result.paths = { foo: '../some/path' } where prop = paths, + * name = paths.foo and value = ../some/path, so it assumes the + * name=value splitting has already happened. + */ + function stringDotToObj(result, name, value) { + var parts = name.split('.'); + + parts.forEach(function (prop, i) { + if (i === parts.length - 1) { + result[prop] = value; + } else { + if (falseProp(result, prop)) { + result[prop] = {}; + } + result = result[prop]; + } + + }); + } + + build.objProps = { + paths: true, + wrap: true, + pragmas: true, + pragmasOnSave: true, + has: true, + hasOnSave: true, + uglify: true, + uglify2: true, + closure: true, + map: true, + throwWhen: true + }; + + build.hasDotPropMatch = function (prop) { + var dotProp, + index = prop.indexOf('.'); + + if (index !== -1) { + dotProp = prop.substring(0, index); + return hasProp(build.objProps, dotProp); + } + return false; + }; + + /** + * Converts an array that has String members of "name=value" + * into an object, where the properties on the object are the names in the array. + * Also converts the strings "true" and "false" to booleans for the values. + * member name/value pairs, and converts some comma-separated lists into + * arrays. + * @param {Array} ary + */ + build.convertArrayToObject = function (ary) { + var result = {}, i, separatorIndex, prop, value, + needArray = { + "include": true, + "exclude": true, + "excludeShallow": true, + "insertRequire": true, + "stubModules": true, + "deps": true, + "mainConfigFile": true, + "wrap.startFile": true, + "wrap.endFile": true + }; + + for (i = 0; i < ary.length; i++) { + separatorIndex = ary[i].indexOf("="); + if (separatorIndex === -1) { + throw "Malformed name/value pair: [" + ary[i] + "]. Format should be name=value"; + } + + value = ary[i].substring(separatorIndex + 1, ary[i].length); + if (value === "true") { + value = true; + } else if (value === "false") { + value = false; + } + + prop = ary[i].substring(0, separatorIndex); + + //Convert to array if necessary + if (getOwn(needArray, prop)) { + value = value.split(","); + } + + if (build.hasDotPropMatch(prop)) { + stringDotToObj(result, prop, value); + } else { + result[prop] = value; + } + } + return result; //Object + }; + + build.makeAbsPath = function (path, absFilePath) { + if (!absFilePath) { + return path; + } + + //Add abspath if necessary. If path starts with a slash or has a colon, + //then already is an abolute path. + if (path.indexOf('/') !== 0 && path.indexOf(':') === -1) { + path = absFilePath + + (absFilePath.charAt(absFilePath.length - 1) === '/' ? '' : '/') + + path; + path = file.normalize(path); + } + return path.replace(lang.backSlashRegExp, '/'); + }; + + build.makeAbsObject = function (props, obj, absFilePath) { + var i, prop; + if (obj) { + for (i = 0; i < props.length; i++) { + prop = props[i]; + if (hasProp(obj, prop) && typeof obj[prop] === 'string') { + obj[prop] = build.makeAbsPath(obj[prop], absFilePath); + } + } + } + }; + + /** + * For any path in a possible config, make it absolute relative + * to the absFilePath passed in. + */ + build.makeAbsConfig = function (config, absFilePath) { + var props, prop, i; + + props = ["appDir", "dir", "baseUrl"]; + for (i = 0; i < props.length; i++) { + prop = props[i]; + + if (getOwn(config, prop)) { + //Add abspath if necessary, make sure these paths end in + //slashes + if (prop === "baseUrl") { + config.originalBaseUrl = config.baseUrl; + if (config.appDir) { + //If baseUrl with an appDir, the baseUrl is relative to + //the appDir, *not* the absFilePath. appDir and dir are + //made absolute before baseUrl, so this will work. + config.baseUrl = build.makeAbsPath(config.originalBaseUrl, config.appDir); + } else { + //The dir output baseUrl is same as regular baseUrl, both + //relative to the absFilePath. + config.baseUrl = build.makeAbsPath(config[prop], absFilePath); + } + } else { + config[prop] = build.makeAbsPath(config[prop], absFilePath); + } + + config[prop] = endsWithSlash(config[prop]); + } + } + + build.makeAbsObject((config.out === "stdout" ? ["cssIn"] : ["out", "cssIn"]), + config, absFilePath); + build.makeAbsObject(["startFile", "endFile"], config.wrap, absFilePath); + build.makeAbsObject(["externExportsPath"], config.closure, absFilePath); + }; + + /** + * Creates a relative path to targetPath from refPath. + * Only deals with file paths, not folders. If folders, + * make sure paths end in a trailing '/'. + */ + build.makeRelativeFilePath = function (refPath, targetPath) { + var i, dotLength, finalParts, length, targetParts, targetName, + refParts = refPath.split('/'), + hasEndSlash = endsWithSlashRegExp.test(targetPath), + dotParts = []; + + targetPath = file.normalize(targetPath); + if (hasEndSlash && !endsWithSlashRegExp.test(targetPath)) { + targetPath += '/'; + } + targetParts = targetPath.split('/'); + //Pull off file name + targetName = targetParts.pop(); + + //Also pop off the ref file name to make the matches against + //targetParts equivalent. + refParts.pop(); + + length = refParts.length; + + for (i = 0; i < length; i += 1) { + if (refParts[i] !== targetParts[i]) { + break; + } + } + + //Now i is the index in which they diverge. + finalParts = targetParts.slice(i); + + dotLength = length - i; + for (i = 0; i > -1 && i < dotLength; i += 1) { + dotParts.push('..'); + } + + return dotParts.join('/') + (dotParts.length ? '/' : '') + + finalParts.join('/') + (finalParts.length ? '/' : '') + + targetName; + }; + + build.nestedMix = { + paths: true, + has: true, + hasOnSave: true, + pragmas: true, + pragmasOnSave: true + }; + + /** + * Mixes additional source config into target config, and merges some + * nested config, like paths, correctly. + */ + function mixConfig(target, source, skipArrays) { + var prop, value, isArray, targetValue; + + for (prop in source) { + if (hasProp(source, prop)) { + //If the value of the property is a plain object, then + //allow a one-level-deep mixing of it. + value = source[prop]; + isArray = lang.isArray(value); + if (typeof value === 'object' && value && + !isArray && !lang.isFunction(value) && + !lang.isRegExp(value)) { + + // TODO: need to generalize this work, maybe also reuse + // the work done in requirejs configure, perhaps move to + // just a deep copy/merge overall. However, given the + // amount of observable change, wait for a dot release. + // This change is in relation to #645 + if (prop === 'map') { + if (!target.map) { + target.map = {}; + } + lang.deepMix(target.map, source.map); + } else { + target[prop] = lang.mixin({}, target[prop], value, true); + } + } else if (isArray) { + if (!skipArrays) { + // Some config, like packages, are arrays. For those, + // just merge the results. + targetValue = target[prop]; + if (lang.isArray(targetValue)) { + target[prop] = targetValue.concat(value); + } else { + target[prop] = value; + } + } + } else { + target[prop] = value; + } + } + } + + //Set up log level since it can affect if errors are thrown + //or caught and passed to errbacks while doing config setup. + if (lang.hasProp(target, 'logLevel')) { + logger.logLevel(target.logLevel); + } + } + + /** + * Converts a wrap.startFile or endFile to be start/end as a string. + * the startFile/endFile values can be arrays. + */ + function flattenWrapFile(config, keyName, absFilePath) { + var wrap = config.wrap, + keyFileName = keyName + 'File', + keyMapName = '__' + keyName + 'Map'; + + if (typeof wrap[keyName] !== 'string' && wrap[keyFileName]) { + wrap[keyName] = ''; + if (typeof wrap[keyFileName] === 'string') { + wrap[keyFileName] = [wrap[keyFileName]]; + } + wrap[keyMapName] = []; + wrap[keyFileName].forEach(function (fileName) { + var absPath = build.makeAbsPath(fileName, absFilePath), + fileText = endsWithNewLine(file.readFile(absPath)); + wrap[keyMapName].push(function (fileContents, cfg, sourceMapGenerator) { + return appendToFileContents(fileContents, fileText, absPath, cfg, null, sourceMapGenerator); + }); + wrap[keyName] += fileText; + }); + } else if (wrap[keyName] === null || wrap[keyName] === undefined) { + //Allow missing one, just set to empty string. + wrap[keyName] = ''; + } else if (typeof wrap[keyName] === 'string') { + wrap[keyName] = endsWithNewLine(wrap[keyName]); + wrap[keyMapName] = [ + function (fileContents, cfg, sourceMapGenerator) { + var absPath = build.makeAbsPath("config-wrap-" + keyName + "-default.js", absFilePath); + return appendToFileContents(fileContents, wrap[keyName], absPath, cfg, null, sourceMapGenerator); + } + ]; + } else { + throw new Error('wrap.' + keyName + ' or wrap.' + keyFileName + ' malformed'); + } + } + + function normalizeWrapConfig(config, absFilePath) { + //Get any wrap text. + try { + if (config.wrap) { + if (config.wrap === true) { + //Use default values. + config.wrap = { + start: '(function () {\n', + end: '}());', + __startMap: [ + function (fileContents, cfg, sourceMapGenerator) { + return appendToFileContents(fileContents, "(function () {\n", + build.makeAbsPath("config-wrap-start-default.js", + absFilePath), cfg, null, + sourceMapGenerator); + } + ], + __endMap: [ + function (fileContents, cfg, sourceMapGenerator) { + return appendToFileContents(fileContents, "}());", + build.makeAbsPath("config-wrap-end-default.js", absFilePath), + cfg, null, sourceMapGenerator); + } + ] + }; + } else { + flattenWrapFile(config, 'start', absFilePath); + flattenWrapFile(config, 'end', absFilePath); + } + } + } catch (wrapError) { + throw new Error('Malformed wrap config: ' + wrapError.toString()); + } + } + + /** + * Creates a config object for an optimization build. + * It will also read the build profile if it is available, to create + * the configuration. + * + * @param {Object} cfg config options that take priority + * over defaults and ones in the build file. These options could + * be from a command line, for instance. + * + * @param {Object} the created config object. + */ + build.createConfig = function (cfg) { + /*jslint evil: true */ + var buildFileContents, buildFileConfig, mainConfig, + mainConfigFile, mainConfigPath, buildFile, absFilePath, + config = {}, + buildBaseConfig = makeBuildBaseConfig(); + + //Make sure all paths are relative to current directory. + absFilePath = file.absPath('.'); + build.makeAbsConfig(cfg, absFilePath); + build.makeAbsConfig(buildBaseConfig, absFilePath); + + lang.mixin(config, buildBaseConfig); + lang.mixin(config, cfg, true); + + //Set up log level early since it can affect if errors are thrown + //or caught and passed to errbacks, even while constructing config. + if (lang.hasProp(config, 'logLevel')) { + logger.logLevel(config.logLevel); + } + + if (config.buildFile) { + //A build file exists, load it to get more config. + buildFile = file.absPath(config.buildFile); + + //Find the build file, and make sure it exists, if this is a build + //that has a build profile, and not just command line args with an in=path + if (!file.exists(buildFile)) { + throw new Error("ERROR: build file does not exist: " + buildFile); + } + + absFilePath = config.baseUrl = file.absPath(file.parent(buildFile)); + + //Load build file options. + buildFileContents = file.readFile(buildFile); + try { + //Be a bit lenient in the file ending in a ; or ending with + //a //# sourceMappingUrl comment, mostly for compiled languages + //that create a config, like typescript. + buildFileContents = buildFileContents + .replace(/\/\/\#[^\n\r]+[\n\r]*$/, '') + .trim() + .replace(/;$/, ''); + + buildFileConfig = eval("(" + buildFileContents + ")"); + build.makeAbsConfig(buildFileConfig, absFilePath); + + //Mix in the config now so that items in mainConfigFile can + //be resolved relative to them if necessary, like if appDir + //is set here, but the baseUrl is in mainConfigFile. Will + //re-mix in the same build config later after mainConfigFile + //is processed, since build config should take priority. + mixConfig(config, buildFileConfig); + } catch (e) { + throw new Error("Build file " + buildFile + " is malformed: " + e); + } + } + + mainConfigFile = config.mainConfigFile || (buildFileConfig && buildFileConfig.mainConfigFile); + if (mainConfigFile) { + if (typeof mainConfigFile === 'string') { + mainConfigFile = [mainConfigFile]; + } + + mainConfigFile.forEach(function (configFile) { + configFile = build.makeAbsPath(configFile, absFilePath); + if (!file.exists(configFile)) { + throw new Error(configFile + ' does not exist.'); + } + try { + mainConfig = parse.findConfig(file.readFile(configFile)).config; + } catch (configError) { + throw new Error('The config in mainConfigFile ' + + configFile + + ' cannot be used because it cannot be evaluated' + + ' correctly while running in the optimizer. Try only' + + ' using a config that is also valid JSON, or do not use' + + ' mainConfigFile and instead copy the config values needed' + + ' into a build file or command line arguments given to the optimizer.\n' + + 'Source error from parsing: ' + configFile + ': ' + configError); + } + if (mainConfig) { + mainConfigPath = configFile.substring(0, configFile.lastIndexOf('/')); + + //Add in some existing config, like appDir, since they can be + //used inside the configFile -- paths and baseUrl are + //relative to them. + if (config.appDir && !mainConfig.appDir) { + mainConfig.appDir = config.appDir; + } + + //If no baseUrl, then use the directory holding the main config. + if (!mainConfig.baseUrl) { + mainConfig.baseUrl = mainConfigPath; + } + + build.makeAbsConfig(mainConfig, mainConfigPath); + mixConfig(config, mainConfig); + } + }); + } + + //Mix in build file config, but only after mainConfig has been mixed in. + //Since this is a re-application, skip array merging. + if (buildFileConfig) { + mixConfig(config, buildFileConfig, true); + } + + //Re-apply the override config values. Command line + //args should take precedence over build file values. + //Since this is a re-application, skip array merging. + mixConfig(config, cfg, true); + + //Fix paths to full paths so that they can be adjusted consistently + //lately to be in the output area. + lang.eachProp(config.paths, function (value, prop) { + if (lang.isArray(value)) { + throw new Error('paths fallback not supported in optimizer. ' + + 'Please provide a build config path override ' + + 'for ' + prop); + } + config.paths[prop] = build.makeAbsPath(value, config.baseUrl); + }); + + //Set final output dir + if (hasProp(config, "baseUrl")) { + if (config.appDir) { + if (!config.originalBaseUrl) { + throw new Error('Please set a baseUrl in the build config'); + } + config.dirBaseUrl = build.makeAbsPath(config.originalBaseUrl, config.dir); + } else { + config.dirBaseUrl = config.dir || config.baseUrl; + } + //Make sure dirBaseUrl ends in a slash, since it is + //concatenated with other strings. + config.dirBaseUrl = endsWithSlash(config.dirBaseUrl); + } + + if (config.bundlesConfigOutFile) { + if (!config.dir) { + throw new Error('bundlesConfigOutFile can only be used with optimizations ' + + 'that use "dir".'); + } + config.bundlesConfigOutFile = build.makeAbsPath(config.bundlesConfigOutFile, config.dir); + } + + //If out=stdout, write output to STDOUT instead of a file. + if (config.out && config.out === 'stdout') { + config.out = function (content) { + var e = env.get(); + if (e === 'rhino') { + var out = new java.io.PrintStream(java.lang.System.out, true, 'UTF-8'); + out.println(content); + } else if (e === 'node') { + process.stdout.setEncoding('utf8'); + process.stdout.write(content); + } else { + console.log(content); + } + }; + } + + //Check for errors in config + if (config.main) { + throw new Error('"main" passed as an option, but the ' + + 'supported option is called "name".'); + } + if (config.out && !config.name && !config.modules && !config.include && + !config.cssIn) { + throw new Error('Missing either a "name", "include" or "modules" ' + + 'option'); + } + if (config.cssIn) { + if (config.dir || config.appDir) { + throw new Error('cssIn is only for the output of single file ' + + 'CSS optimizations and is not compatible with "dir" or "appDir" configuration.'); + } + if (!config.out) { + throw new Error('"out" option missing.'); + } + } + if (!config.cssIn && !config.baseUrl) { + //Just use the current directory as the baseUrl + config.baseUrl = './'; + } + if (!config.out && !config.dir) { + throw new Error('Missing either an "out" or "dir" config value. ' + + 'If using "appDir" for a full project optimization, ' + + 'use "dir". If you want to optimize to one file, ' + + 'use "out".'); + } + if (config.appDir && config.out) { + throw new Error('"appDir" is not compatible with "out". Use "dir" ' + + 'instead. appDir is used to copy whole projects, ' + + 'where "out" with "baseUrl" is used to just ' + + 'optimize to one file.'); + } + if (config.out && config.dir) { + throw new Error('The "out" and "dir" options are incompatible.' + + ' Use "out" if you are targeting a single file' + + ' for optimization, and "dir" if you want the appDir' + + ' or baseUrl directories optimized.'); + } + + + if (config.dir) { + // Make sure the output dir is not set to a parent of the + // source dir or the same dir, as it will result in source + // code deletion. + if (!config.allowSourceOverwrites && (config.dir === config.baseUrl || + config.dir === config.appDir || + (config.baseUrl && build.makeRelativeFilePath(config.dir, + config.baseUrl).indexOf('..') !== 0) || + (config.appDir && + build.makeRelativeFilePath(config.dir, config.appDir).indexOf('..') !== 0))) { + throw new Error('"dir" is set to a parent or same directory as' + + ' "appDir" or "baseUrl". This can result in' + + ' the deletion of source code. Stopping. If' + + ' you want to allow possible overwriting of' + + ' source code, set "allowSourceOverwrites"' + + ' to true in the build config, but do so at' + + ' your own risk. In that case, you may want' + + ' to also set "keepBuildDir" to true.'); + } + } + + if (config.insertRequire && !lang.isArray(config.insertRequire)) { + throw new Error('insertRequire should be a list of module IDs' + + ' to insert in to a require([]) call.'); + } + + //Support older configs with uglify2 settings, but now that uglify1 has + //been removed, just translate it to 'uglify' settings. + if (config.optimize === 'uglify2') { + config.optimize = 'uglify'; + } + if (config.uglify2) { + config.uglify = config.uglify2; + delete config.uglify2; + } + + if (config.generateSourceMaps) { + if (config.preserveLicenseComments && !(config.optimize === 'none' || config.optimize === 'uglify')) { + throw new Error('Cannot use preserveLicenseComments and ' + + 'generateSourceMaps together, unless optimize is set ' + + 'to \'uglify\'. Either explicitly set preserveLicenseComments ' + + 'to false (default is true) or turn off generateSourceMaps. ' + + 'If you want source maps with license comments, see: ' + + 'http://requirejs.org/docs/errors.html#sourcemapcomments'); + } else if (config.optimize !== 'none' && + config.optimize !== 'closure' && + config.optimize !== 'uglify') { + //Allow optimize: none to pass, since it is useful when toggling + //minification on and off to debug something, and it implicitly + //works, since it does not need a source map. + throw new Error('optimize: "' + config.optimize + + '" does not support generateSourceMaps.'); + } + } + + if ((config.name || config.include) && !config.modules) { + //Just need to build one file, but may be part of a whole appDir/ + //baseUrl copy, but specified on the command line, so cannot do + //the modules array setup. So create a modules section in that + //case. + config.modules = [ + { + name: config.name, + out: config.out, + create: config.create, + include: config.include, + exclude: config.exclude, + excludeShallow: config.excludeShallow, + insertRequire: config.insertRequire, + stubModules: config.stubModules + } + ]; + delete config.stubModules; + } else if (config.modules && config.out) { + throw new Error('If the "modules" option is used, then there ' + + 'should be a "dir" option set and "out" should ' + + 'not be used since "out" is only for single file ' + + 'optimization output.'); + } else if (config.modules && config.name) { + throw new Error('"name" and "modules" options are incompatible. ' + + 'Either use "name" if doing a single file ' + + 'optimization, or "modules" if you want to target ' + + 'more than one file for optimization.'); + } + + if (config.out && !config.cssIn) { + //Just one file to optimize. + + //Does not have a build file, so set up some defaults. + //Optimizing CSS should not be allowed, unless explicitly + //asked for on command line. In that case the only task is + //to optimize a CSS file. + if (!cfg.optimizeCss) { + config.optimizeCss = "none"; + } + } + + //Normalize cssPrefix + if (config.cssPrefix) { + //Make sure cssPrefix ends in a slash + config.cssPrefix = endsWithSlash(config.cssPrefix); + } else { + config.cssPrefix = ''; + } + + //Cycle through modules and normalize + if (config.modules && config.modules.length) { + config.modules.forEach(function (mod) { + if (lang.isArray(mod) || typeof mod === 'string' || !mod) { + throw new Error('modules config item is malformed: it should' + + ' be an object with a \'name\' property.'); + } + + //Combine any local stubModules with global values. + if (config.stubModules) { + mod.stubModules = config.stubModules.concat(mod.stubModules || []); + } + + //Create a hash lookup for the stubModules config to make lookup + //cheaper later. + if (mod.stubModules) { + mod.stubModules._byName = {}; + mod.stubModules.forEach(function (id) { + mod.stubModules._byName[id] = true; + }); + } + + // Legacy command support, which allowed a single string ID + // for include. + if (typeof mod.include === 'string') { + mod.include = [mod.include]; + } + + //Allow wrap config in overrides, but normalize it. + if (mod.override) { + normalizeWrapConfig(mod.override, absFilePath); + } + }); + } + + normalizeWrapConfig(config, absFilePath); + + //Do final input verification + if (config.context) { + throw new Error('The build argument "context" is not supported' + + ' in a build. It should only be used in web' + + ' pages.'); + } + + //Set up normalizeDirDefines. If not explicitly set, if optimize "none", + //set to "skip" otherwise set to "all". + if (!hasProp(config, 'normalizeDirDefines')) { + if (config.optimize === 'none' || config.skipDirOptimize) { + config.normalizeDirDefines = 'skip'; + } else { + config.normalizeDirDefines = 'all'; + } + } + + //Set file.fileExclusionRegExp if desired + if (hasProp(config, 'fileExclusionRegExp')) { + if (typeof config.fileExclusionRegExp === "string") { + file.exclusionRegExp = new RegExp(config.fileExclusionRegExp); + } else { + file.exclusionRegExp = config.fileExclusionRegExp; + } + } else if (hasProp(config, 'dirExclusionRegExp')) { + //Set file.dirExclusionRegExp if desired, this is the old + //name for fileExclusionRegExp before 1.0.2. Support for backwards + //compatibility + file.exclusionRegExp = config.dirExclusionRegExp; + } + + //Track the deps, but in a different key, so that they are not loaded + //as part of config seeding before all config is in play (#648). Was + //going to merge this in with "include", but include is added after + //the "name" target. To preserve what r.js has done previously, make + //sure "deps" comes before the "name". + if (config.deps) { + config._depsInclude = config.deps; + } + + + //Remove things that may cause problems in the build. + //deps already merged above + delete config.deps; + delete config.jQuery; + delete config.enforceDefine; + delete config.urlArgs; + + return config; + }; + + /** + * finds the module being built/optimized with the given moduleName, + * or returns null. + * @param {String} moduleName + * @param {Array} modules + * @returns {Object} the module object from the build profile, or null. + */ + build.findBuildModule = function (moduleName, modules) { + var i, module; + for (i = 0; i < modules.length; i++) { + module = modules[i]; + if (module.name === moduleName) { + return module; + } + } + return null; + }; + + /** + * Removes a module name and path from a layerobj, if it is supposed to be + * excluded from the layerobj. + * @param {String} moduleName the name of the module + * @param {String} path the file path for the module + * @param {Object} layerobj the layerobj to remove the module/path from + */ + build.removeModulePath = function (module, path, layerobj) { + var index = layerobj.buildFilePaths.indexOf(path); + if (index !== -1) { + layerobj.buildFilePaths.splice(index, 1); + } + }; + + /** + * Uses the module build config object to trace the dependencies for the + * given module. + * + * @param {Object} module the module object from the build config info. + * @param {Object} config the build config object. + * @param {Object} [baseLoaderConfig] the base loader config to use for env resets. + * + * @returns {Object} layerobj information about what paths and modules should + * be in the flattened module. + */ + build.traceDependencies = function (module, config, baseLoaderConfig) { + var include, override, layerobj, context, oldContext, + rawTextByIds, + syncChecks = { + rhino: true, + node: true, + xpconnect: true + }, + deferred = prim(); + + //Reset some state set up in requirePatch.js, and clean up require's + //current context. + oldContext = require._buildReset(); + + //Grab the reset layerobj and context after the reset, but keep the + //old config to reuse in the new context. + layerobj = require._layerobj; + context = layerobj.context; + + //Put back basic config, use a fresh object for it. + if (baseLoaderConfig) { + require(copyConfig(baseLoaderConfig)); + } + + logger.trace("\nTracing dependencies for: " + (module.name || + (typeof module.out === 'function' ? 'FUNCTION' : module.out))); + include = config._depsInclude || []; + include = include.concat(module.name && !module.create ? [module.name] : []); + if (module.include) { + include = include.concat(module.include); + } + + //If there are overrides to basic config, set that up now.; + if (module.override) { + if (baseLoaderConfig) { + override = build.createOverrideConfig(baseLoaderConfig, module.override); + } else { + override = copyConfig(module.override); + } + require(override); + } + + //Now, populate the rawText cache with any values explicitly passed in + //via config. + rawTextByIds = require.s.contexts._.config.rawText; + if (rawTextByIds) { + lang.eachProp(rawTextByIds, function (contents, id) { + var url = require.toUrl(id) + '.js'; + require._cachedRawText[url] = contents; + }); + } + + + //Configure the callbacks to be called. + deferred.reject.__requireJsBuild = true; + + //Use a wrapping function so can check for errors. + function includeFinished(value) { + //If a sync build environment, check for errors here, instead of + //in the then callback below, since some errors, like two IDs pointed + //to same URL but only one anon ID will leave the loader in an + //unresolved state since a setTimeout cannot be used to check for + //timeout. + var hasError = false; + if (syncChecks[env.get()]) { + try { + build.checkForErrors(context, layerobj); + } catch (e) { + hasError = true; + deferred.reject(e); + } + } + + if (!hasError) { + deferred.resolve(value); + } + } + includeFinished.__requireJsBuild = true; + + //Figure out module layerobj dependencies by calling require to do the work. + require(include, includeFinished, deferred.reject); + + // If a sync env, then with the "two IDs to same anon module path" + // issue, the require never completes, need to check for errors + // here. + if (syncChecks[env.get()]) { + build.checkForErrors(context, layerobj); + } + + return deferred.promise.then(function () { + //Reset config + if (module.override && baseLoaderConfig) { + require(copyConfig(baseLoaderConfig)); + } + + build.checkForErrors(context, layerobj); + + return layerobj; + }); + }; + + build.checkForErrors = function (context, layerobj) { + //Check to see if it all loaded. If not, then throw, and give + //a message on what is left. + var id, prop, mod, idParts, pluginId, pluginResources, + errMessage = '', + failedPluginMap = {}, + failedPluginIds = [], + errIds = [], + errUrlMap = {}, + errUrlConflicts = {}, + hasErrUrl = false, + hasUndefined = false, + defined = context.defined, + registry = context.registry; + + function populateErrUrlMap(id, errUrl, skipNew) { + // Loader plugins do not have an errUrl, so skip them. + if (!errUrl) { + return; + } + + if (!skipNew) { + errIds.push(id); + } + + if (errUrlMap[errUrl]) { + hasErrUrl = true; + //This error module has the same URL as another + //error module, could be misconfiguration. + if (!errUrlConflicts[errUrl]) { + errUrlConflicts[errUrl] = []; + //Store the original module that had the same URL. + errUrlConflicts[errUrl].push(errUrlMap[errUrl]); + } + errUrlConflicts[errUrl].push(id); + } else if (!skipNew) { + errUrlMap[errUrl] = id; + } + } + + for (id in registry) { + if (hasProp(registry, id) && id.indexOf('_@r') !== 0) { + hasUndefined = true; + mod = getOwn(registry, id); + idParts = id.split('!'); + pluginId = idParts[0]; + + if (id.indexOf('_unnormalized') === -1 && mod && mod.enabled) { + populateErrUrlMap(id, mod.map.url); + } + + //Look for plugins that did not call load() + //But skip plugin IDs that were already inlined and called + //define() with a name. + if (!hasProp(layerobj.modulesWithNames, id) && idParts.length > 1) { + if (falseProp(failedPluginMap, pluginId)) { + failedPluginIds.push(pluginId); + } + pluginResources = failedPluginMap[pluginId]; + if (!pluginResources) { + pluginResources = failedPluginMap[pluginId] = []; + } + pluginResources.push(id + (mod.error ? ': ' + mod.error : '')); + } + } + } + + // If have some modules that are not defined/stuck in the registry, + // then check defined modules for URL overlap. + if (hasUndefined) { + for (id in defined) { + if (hasProp(defined, id) && id.indexOf('!') === -1) { + populateErrUrlMap(id, require.toUrl(id) + '.js', true); + } + } + } + + if (errIds.length || failedPluginIds.length) { + if (failedPluginIds.length) { + errMessage += 'Loader plugin' + + (failedPluginIds.length === 1 ? '' : 's') + + ' did not call ' + + 'the load callback in the build:\n' + + failedPluginIds.map(function (pluginId) { + var pluginResources = failedPluginMap[pluginId]; + return pluginId + ':\n ' + pluginResources.join('\n '); + }).join('\n') + '\n'; + } + errMessage += 'Module loading did not complete for: ' + errIds.join(', '); + + if (hasErrUrl) { + errMessage += '\nThe following modules share the same URL. This ' + + 'could be a misconfiguration if that URL only has ' + + 'one anonymous module in it:'; + for (prop in errUrlConflicts) { + if (hasProp(errUrlConflicts, prop)) { + errMessage += '\n' + prop + ': ' + + errUrlConflicts[prop].join(', '); + } + } + } + throw new Error(errMessage); + } + }; + + build.createOverrideConfig = function (config, override) { + var cfg = copyConfig(config), + oride = copyConfig(override); + + lang.eachProp(oride, function (value, prop) { + if (hasProp(build.objProps, prop)) { + //An object property, merge keys. Start a new object + //so that source object in config does not get modified. + cfg[prop] = {}; + lang.mixin(cfg[prop], config[prop], true); + lang.mixin(cfg[prop], override[prop], true); + } else { + cfg[prop] = override[prop]; + } + }); + + return cfg; + }; + + /** + * Uses the module build config object to create an flattened version + * of the module, with deep dependencies included. + * + * @param {Object} module the module object from the build config info. + * + * @param {Object} layerobj the layerobj object returned from build.traceDependencies. + * + * @param {Object} the build config object. + * + * @returns {Object} with two properties: "text", the text of the flattened + * module, and "buildText", a string of text representing which files were + * included in the flattened module text. + */ + build.flattenModule = function (module, layerobj, config) { + var fileContents, sourceMapGenerator, + sourceMapBase, + buildFileContents = ''; + + return prim().start(function () { + var reqIndex, currContents, fileForSourceMap, + moduleName, shim, packageName, + parts, builder, writeApi, + namespace, namespaceWithDot, stubModulesByName, + context = layerobj.context, + onLayerEnds = [], + onLayerEndAdded = {}, + pkgsMainMap = {}; + + //Use override settings, particularly for pragmas + //Do this before the var readings since it reads config values. + if (module.override) { + config = build.createOverrideConfig(config, module.override); + } + + namespace = config.namespace || ''; + namespaceWithDot = namespace ? namespace + '.' : ''; + stubModulesByName = (module.stubModules && module.stubModules._byName) || {}; + + //Start build output for the module. + module.onCompleteData = { + name: module.name, + path: (config.dir ? module._buildPath.replace(config.dir, "") : module._buildPath), + included: [] + }; + + buildFileContents += "\n" + + module.onCompleteData.path + + "\n----------------\n"; + + //If there was an existing file with require in it, hoist to the top. + if (layerobj.existingRequireUrl) { + reqIndex = layerobj.buildFilePaths.indexOf(layerobj.existingRequireUrl); + if (reqIndex !== -1) { + layerobj.buildFilePaths.splice(reqIndex, 1); + layerobj.buildFilePaths.unshift(layerobj.existingRequireUrl); + } + } + + if (config.generateSourceMaps) { + sourceMapBase = config.dir || config.baseUrl; + if (module._buildPath === 'FUNCTION') { + fileForSourceMap = (module.name || module.include[0] || 'FUNCTION') + '.build.js'; + } else if (config.out) { + fileForSourceMap = module._buildPath.split('/').pop(); + } else { + fileForSourceMap = module._buildPath.replace(sourceMapBase, ''); + } + sourceMapGenerator = new SourceMapGenerator({ + file: fileForSourceMap + }); + } + + //Create a reverse lookup for packages main module IDs to their package + //names, useful for knowing when to write out define() package main ID + //adapters. + lang.eachProp(layerobj.context.config.pkgs, function(value, prop) { + pkgsMainMap[value] = prop; + }); + + //Write the built module to disk, and build up the build output. + fileContents = ""; + if (config.wrap && config.wrap.__startMap) { + config.wrap.__startMap.forEach(function (wrapFunction) { + fileContents = wrapFunction(fileContents, config, sourceMapGenerator); + }); + } + + return prim.serial(layerobj.buildFilePaths.map(function (path) { + return function () { + var singleContents = ''; + + moduleName = layerobj.buildFileToModule[path]; + + //If the moduleName is a package main, then hold on to the + //packageName in case an adapter needs to be written. + packageName = getOwn(pkgsMainMap, moduleName); + + return prim().start(function () { + //Figure out if the module is a result of a build plugin, and if so, + //then delegate to that plugin. + parts = context.makeModuleMap(moduleName); + builder = parts.prefix && getOwn(context.defined, parts.prefix); + if (builder) { + if (builder.onLayerEnd && falseProp(onLayerEndAdded, parts.prefix)) { + onLayerEnds.push(builder); + onLayerEndAdded[parts.prefix] = true; + } + + if (builder.write) { + writeApi = function (input) { + singleContents += "\n" + addSemiColon(input, config); + if (config.onBuildWrite) { + singleContents = config.onBuildWrite(moduleName, path, singleContents); + } + }; + writeApi.asModule = function (moduleName, input) { + singleContents += "\n" + + addSemiColon(build.toTransport(namespace, moduleName, path, input, layerobj, { + useSourceUrl: layerobj.context.config.useSourceUrl + }), config); + if (config.onBuildWrite) { + singleContents = config.onBuildWrite(moduleName, path, singleContents); + } + }; + builder.write(parts.prefix, parts.name, writeApi); + } + return; + } else { + return prim().start(function () { + if (hasProp(stubModulesByName, moduleName)) { + //Just want to insert a simple module definition instead + //of the source module. Useful for plugins that inline + //all their resources. + if (hasProp(layerobj.context.plugins, moduleName)) { + //Slightly different content for plugins, to indicate + //that dynamic loading will not work. + return 'define({load: function(id){throw new Error("Dynamic load not allowed: " + id);}});'; + } else { + return 'define({});'; + } + } else { + return require._cacheReadAsync(path); + } + }).then(function (text) { + var hasPackageName; + + currContents = text; + + if (config.cjsTranslate && + (!config.shim || !lang.hasProp(config.shim, moduleName))) { + currContents = commonJs.convert(path, currContents); + } + + if (config.onBuildRead) { + currContents = config.onBuildRead(moduleName, path, currContents); + } + + if (packageName) { + hasPackageName = (packageName === parse.getNamedDefine(currContents)); + } + + if (namespace) { + currContents = pragma.namespace(currContents, namespace); + } + + currContents = build.toTransport(namespace, moduleName, path, currContents, layerobj, { + useSourceUrl: config.useSourceUrl + }); + + if (packageName && !hasPackageName) { + currContents = addSemiColon(currContents, config) + '\n'; + currContents += namespaceWithDot + "define('" + + packageName + "', ['" + moduleName + + "'], function (main) { return main; });\n"; + } + + if (config.onBuildWrite) { + currContents = config.onBuildWrite(moduleName, path, currContents); + } + + //Semicolon is for files that are not well formed when + //concatenated with other content. + singleContents += addSemiColon(currContents, config); + }); + } + }).then(function () { + var shimDeps, shortPath = path.replace(config.dir, ""); + + module.onCompleteData.included.push(shortPath); + buildFileContents += shortPath + "\n"; + + //Some files may not have declared a require module, and if so, + //put in a placeholder call so the require does not try to load them + //after the module is processed. + //If we have a name, but no defined module, then add in the placeholder. + if (moduleName && falseProp(layerobj.modulesWithNames, moduleName) && !config.skipModuleInsertion) { + shim = config.shim && (getOwn(config.shim, moduleName) || (packageName && getOwn(config.shim, packageName))); + if (shim) { + shimDeps = lang.isArray(shim) ? shim : shim.deps; + if (config.wrapShim) { + + singleContents = '(function(root) {\n' + + namespaceWithDot + 'define("' + moduleName + '", ' + + (shimDeps && shimDeps.length ? + build.makeJsArrayString(shimDeps) + ', ' : '[], ') + + 'function() {\n' + + ' return (function() {\n' + + singleContents + + // Start with a \n in case last line is a comment + // in the singleContents, like a sourceURL comment. + '\n' + (shim.exportsFn ? shim.exportsFn() : '') + + '\n' + + ' }).apply(root, arguments);\n' + + '});\n' + + '}(this));\n'; + } else { + singleContents += '\n' + namespaceWithDot + 'define("' + moduleName + '", ' + + (shimDeps && shimDeps.length ? + build.makeJsArrayString(shimDeps) + ', ' : '') + + (shim.exportsFn ? shim.exportsFn() : 'function(){}') + + ');\n'; + } + } else { + singleContents += '\n' + namespaceWithDot + 'define("' + moduleName + '", function(){});\n'; + } + } + + //Add line break at end of file, instead of at beginning, + //so source map line numbers stay correct, but still allow + //for some space separation between files in case ASI issues + //for concatenation would cause an error otherwise. + singleContents += '\n'; + + //Add to the source map and to the final contents + fileContents = appendToFileContents(fileContents, singleContents, path, config, module, + sourceMapGenerator); + }); + }; + })).then(function () { + if (onLayerEnds.length) { + onLayerEnds.forEach(function (builder, index) { + var path; + if (typeof module.out === 'string') { + path = module.out; + } else if (typeof module._buildPath === 'string') { + path = module._buildPath; + } + builder.onLayerEnd(function (input) { + fileContents = + appendToFileContents(fileContents, "\n" + addSemiColon(input, config), + 'onLayerEnd' + index + '.js', config, module, sourceMapGenerator); + }, { + name: module.name, + path: path + }); + }); + } + + if (module.create) { + //The ID is for a created layerobj. Write out + //a module definition for it in case the + //built file is used with enforceDefine + //(#432) + fileContents = + appendToFileContents(fileContents, '\n' + namespaceWithDot + 'define("' + module.name + + '", function(){});\n', 'module-create.js', config, module, + sourceMapGenerator); + } + + //Add a require at the end to kick start module execution, if that + //was desired. Usually this is only specified when using small shim + //loaders like almond. + if (module.insertRequire) { + fileContents = + appendToFileContents(fileContents, '\n' + namespaceWithDot + 'require(["' + module.insertRequire.join('", "') + + '"]);\n', 'module-insertRequire.js', config, module, + sourceMapGenerator); + } + }); + }).then(function () { + if (config.wrap && config.wrap.__endMap) { + config.wrap.__endMap.forEach(function (wrapFunction) { + fileContents = wrapFunction(fileContents, config, sourceMapGenerator); + }); + } + return { + text: fileContents, + buildText: buildFileContents, + sourceMap: sourceMapGenerator ? + JSON.stringify(sourceMapGenerator.toJSON(), null, ' ') : + undefined + }; + }); + }; + + //Converts an JS array of strings to a string representation. + //Not using JSON.stringify() for Rhino's sake. + build.makeJsArrayString = function (ary) { + return '["' + ary.map(function (item) { + //Escape any double quotes, backslashes + return lang.jsEscape(item); + }).join('","') + '"]'; + }; + + build.toTransport = function (namespace, moduleName, path, contents, layerobj, options) { + var baseUrl = layerobj && layerobj.context.config.baseUrl; + + function onFound(info) { + //Only mark this module as having a name if not a named module, + //or if a named module and the name matches expectations. + if (layerobj && (info.needsId || info.foundId === moduleName)) { + layerobj.modulesWithNames[moduleName] = true; + } + } + + //Convert path to be a local one to the baseUrl, useful for + //useSourceUrl. + if (baseUrl) { + path = path.replace(baseUrl, ''); + } + + return transform.toTransport(namespace, moduleName, path, contents, onFound, options); + }; + + return build; +}); + + } + + + /** + * Sets the default baseUrl for requirejs to be directory of top level + * script. + */ + function setBaseUrl(fileName) { + //Use the file name's directory as the baseUrl if available. + dir = fileName.replace(/\\/g, '/'); + if (dir.indexOf('/') !== -1) { + dir = dir.split('/'); + dir.pop(); + dir = dir.join('/'); + //Make sure dir is JS-escaped, since it will be part of a JS string. + exec("require({baseUrl: '" + dir.replace(/[\\"']/g, '\\$&') + "'});"); + } + } + + function createRjsApi() { + //Create a method that will run the optimzer given an object + //config. + requirejs.optimize = function (config, callback, errback) { + if (!loadedOptimizedLib) { + loadLib(); + loadedOptimizedLib = true; + } + + //Create the function that will be called once build modules + //have been loaded. + var runBuild = function (build, logger, quit) { + //Make sure config has a log level, and if not, + //make it "silent" by default. + config.logLevel = config.hasOwnProperty('logLevel') ? + config.logLevel : logger.SILENT; + + //Reset build internals first in case this is part + //of a long-running server process that could have + //exceptioned out in a bad state. It is only defined + //after the first call though. + if (requirejs._buildReset) { + requirejs._buildReset(); + requirejs._cacheReset(); + } + + function done(result) { + //And clean up, in case something else triggers + //a build in another pathway. + if (requirejs._buildReset) { + requirejs._buildReset(); + requirejs._cacheReset(); + } + + // Ensure errors get propagated to the errback + if (result instanceof Error) { + throw result; + } + + return result; + } + + errback = errback || function (err) { + // Using console here since logger may have + // turned off error logging. Since quit is + // called want to be sure a message is printed. + console.log(err); + quit(1); + }; + + build(config).then(done, done).then(callback, errback); + }; + + requirejs({ + context: 'build' + }, ['build', 'logger', 'env!env/quit'], runBuild); + }; + + requirejs.tools = { + useLib: function (contextName, callback) { + if (!callback) { + callback = contextName; + contextName = 'uselib'; + } + + if (!useLibLoaded[contextName]) { + loadLib(); + useLibLoaded[contextName] = true; + } + + var req = requirejs({ + context: contextName + }); + + req(['build'], function () { + callback(req); + }); + } + }; + + requirejs.define = define; + } + + //If in Node, and included via a require('requirejs'), just export and + //THROW IT ON THE GROUND! + if (env === 'node' && reqMain !== module) { + setBaseUrl(path.resolve(reqMain ? reqMain.filename : '.')); + + createRjsApi(); + + module.exports = requirejs; + return; + } else if (env === 'browser') { + //Only option is to use the API. + setBaseUrl(location.href); + createRjsApi(); + return; + } else if ((env === 'rhino' || env === 'xpconnect') && + //User sets up requirejsAsLib variable to indicate it is loaded + //via load() to be used as a library. + typeof requirejsAsLib !== 'undefined' && requirejsAsLib) { + //This script is loaded via rhino's load() method, expose the + //API and get out. + setBaseUrl(fileName); + createRjsApi(); + return; + } + + if (commandOption === 'o') { + //Do the optimizer work. + loadLib(); + + /* + * Create a build.js file that has the build options you want and pass that + * build file to this file to do the build. See example.build.js for more information. + */ + +/*jslint strict: false, nomen: false */ +/*global require: false */ + +require({ + baseUrl: require.s.contexts._.config.baseUrl, + //Use a separate context than the default context so that the + //build can use the default context. + context: 'build', + catchError: { + define: true + } +}, ['env!env/args', 'env!env/quit', 'logger', 'build'], +function (args, quit, logger, build) { + build(args).then(function () {}, function (err) { + logger.error(err); + quit(1); + }); +}); + + + } else if (commandOption === 'v') { + console.log('r.js: ' + version + + ', RequireJS: ' + this.requirejsVars.require.version + + ', UglifyJS: 2.7.3'); + } else if (commandOption === 'convert') { + loadLib(); + + this.requirejsVars.require(['env!env/args', 'commonJs', 'env!env/print'], + function (args, commonJs, print) { + + var srcDir, outDir; + srcDir = args[0]; + outDir = args[1]; + + if (!srcDir || !outDir) { + print('Usage: path/to/commonjs/modules output/dir'); + return; + } + + commonJs.convertDir(args[0], args[1]); + }); + } else { + //Just run an app + + //Load the bundled libraries for use in the app. + if (commandOption === 'lib') { + loadLib(); + } + + setBaseUrl(fileName); + + if (exists(fileName)) { + exec(readFile(fileName), fileName); + } else { + showHelp(); + } + } + +}((typeof console !== 'undefined' ? console : undefined), + (typeof Packages !== 'undefined' || (typeof window === 'undefined' && + typeof Components !== 'undefined' && Components.interfaces) ? + Array.prototype.slice.call(arguments, 0) : []), + (typeof readFile !== 'undefined' ? readFile : undefined))); diff --git a/application/admin/command/Min/stubs/css.stub b/application/admin/command/Min/stubs/css.stub new file mode 100644 index 0000000..17211a1 --- /dev/null +++ b/application/admin/command/Min/stubs/css.stub @@ -0,0 +1,6 @@ +({ + cssIn: "{%cssBasePath%}{%cssBaseName%}.css", + out: "{%cssBasePath%}{%cssBaseName%}.min.css", + optimizeCss: "default", + optimize: "{%optimize%}" +}) \ No newline at end of file diff --git a/application/admin/command/Min/stubs/js.stub b/application/admin/command/Min/stubs/js.stub new file mode 100644 index 0000000..8e7a210 --- /dev/null +++ b/application/admin/command/Min/stubs/js.stub @@ -0,0 +1,11 @@ +({ + {%config%} + , + optimizeCss: "standard", + optimize: "{%optimize%}", //可使用uglify|closure|none + preserveLicenseComments: false, + removeCombined: false, + baseUrl: "{%jsBasePath%}", //JS文件所在的基础目录 + name: "{%jsBaseName%}", //来源文件,不包含后缀 + out: "{%jsBasePath%}{%jsBaseName%}.min.js" //目标文件 +}); \ No newline at end of file diff --git a/application/admin/common.php b/application/admin/common.php new file mode 100644 index 0000000..502c534 --- /dev/null +++ b/application/admin/common.php @@ -0,0 +1,197 @@ + $v) { + $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"])); + } + return '
    ' . implode(' ', $html) . '
    '; + } +} + +if (!function_exists('build_checkboxs')) { + + /** + * 生成复选按钮组 + * @param string $name + * @param array $list + * @param mixed $selected + * @return string + */ + function build_checkboxs($name, $list = [], $selected = null) + { + $html = []; + $selected = is_null($selected) ? [] : $selected; + $selected = is_array($selected) ? $selected : explode(',', $selected); + foreach ($list as $k => $v) { + $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::checkbox($name, $k, in_array($k, $selected), ['id' => "{$name}-{$k}"])); + } + return '
    ' . implode(' ', $html) . '
    '; + } +} + + +if (!function_exists('build_category_select')) { + + /** + * 生成分类下拉列表框 + * @param string $name + * @param string $type + * @param mixed $selected + * @param array $attr + * @param array $header + * @return string + */ + function build_category_select($name, $type, $selected = null, $attr = [], $header = []) + { + $tree = Tree::instance(); + $tree->init(Category::getCategoryArray($type), 'pid'); + $categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name'); + $categorydata = $header ? $header : []; + foreach ($categorylist as $k => $v) { + $categorydata[$v['id']] = $v['name']; + } + $attr = array_merge(['id' => "c-{$name}", 'class' => 'form-control selectpicker'], $attr); + return build_select($name, $categorydata, $selected, $attr); + } +} + +if (!function_exists('build_toolbar')) { + + /** + * 生成表格操作按钮栏 + * @param array $btns 按钮组 + * @param array $attr 按钮属性值 + * @return string + */ + function build_toolbar($btns = null, $attr = []) + { + $auth = \app\admin\library\Auth::instance(); + $controller = str_replace('.', '/', Loader::parseName(request()->controller())); + $btns = $btns ? $btns : ['refresh', 'add', 'edit', 'del', 'import']; + $btns = is_array($btns) ? $btns : explode(',', $btns); + $index = array_search('delete', $btns); + if ($index !== false) { + $btns[$index] = 'del'; + } + $btnAttr = [ + 'refresh' => ['javascript:;', 'btn btn-primary btn-refresh', 'fa fa-refresh', '', __('Refresh')], + 'add' => ['javascript:;', 'btn btn-success btn-add', 'fa fa-plus', __('Add'), __('Add')], + 'edit' => ['javascript:;', 'btn btn-success btn-edit btn-disabled disabled', 'fa fa-pencil', __('Edit'), __('Edit')], + 'del' => ['javascript:;', 'btn btn-danger btn-del btn-disabled disabled', 'fa fa-trash', __('Delete'), __('Delete')], + 'import' => ['javascript:;', 'btn btn-info btn-import', 'fa fa-upload', __('Import'), __('Import')], + ]; + $btnAttr = array_merge($btnAttr, $attr); + $html = []; + foreach ($btns as $k => $v) { + //如果未定义或没有权限 + if (!isset($btnAttr[$v]) || ($v !== 'refresh' && !$auth->check("{$controller}/{$v}"))) { + continue; + } + list($href, $class, $icon, $text, $title) = $btnAttr[$v]; + //$extend = $v == 'import' ? 'id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"' : ''; + //$html[] = ' ' . $text . ''; + if ($v == 'import') { + $template = str_replace('/', '_', $controller); + $download = ''; + if (file_exists("./template/{$template}.xlsx")) { + $download .= "
  • XLSX模版
  • "; + } + if (file_exists("./template/{$template}.xls")) { + $download .= "
  • XLS模版
  • "; + } + if (file_exists("./template/{$template}.csv")) { + $download .= empty($download) ? '' : "
  • "; + $download .= "
  • CSV模版
  • "; + } + $download .= empty($download) ? '' : "\n "; + if (!empty($download)) { + $html[] = << + + + + +EOT; + } else { + $html[] = ' ' . $text . ''; + } + } else { + $html[] = ' ' . $text . ''; + } + } + return implode(' ', $html); + } +} + +if (!function_exists('build_heading')) { + + /** + * 生成页面Heading + * + * @param string $path 指定的path + * @return string + */ + function build_heading($path = null, $container = true) + { + $title = $content = ''; + if (is_null($path)) { + $action = request()->action(); + $controller = str_replace('.', '/', Loader::parseName(request()->controller())); + $path = strtolower($controller . ($action && $action != 'index' ? '/' . $action : '')); + } + // 根据当前的URI自动匹配父节点的标题和备注 + $data = Db::name('auth_rule')->where('name', $path)->field('title,remark')->find(); + if ($data) { + $title = __($data['title']); + $content = __($data['remark']); + } + if (!$content) { + return ''; + } + $result = '
    ' . $title . '' . $content . '
    '; + if ($container) { + $result = '
    ' . $result . '
    '; + } + return $result; + } +} diff --git a/application/admin/config.php b/application/admin/config.php new file mode 100644 index 0000000..c650c15 --- /dev/null +++ b/application/admin/config.php @@ -0,0 +1,8 @@ + true, + 'url_html_suffix' => '', + 'controller_auto_search' => true, +]; diff --git a/application/admin/controller/Addon.php b/application/admin/controller/Addon.php new file mode 100644 index 0000000..8d11cf4 --- /dev/null +++ b/application/admin/controller/Addon.php @@ -0,0 +1,454 @@ +auth->isSuperAdmin() && in_array($this->request->action(), ['install', 'uninstall', 'local', 'upgrade', 'authorization', 'testdata'])) { + $this->error(__('Access is allowed only to the super management group')); + } + } + + /** + * 插件列表 + */ + public function index() + { + $addons = get_addon_list(); + foreach ($addons as $k => &$v) { + $config = get_addon_config($v['name']); + $v['config'] = $config ? 1 : 0; + $v['url'] = str_replace($this->request->server('SCRIPT_NAME'), '', $v['url']); + } + $this->assignconfig(['addons' => $addons, 'api_url' => config('fastadmin.api_url'), 'faversion' => config('fastadmin.version'), 'domain' => request()->host(true)]); + return $this->view->fetch(); + } + + /** + * 配置 + */ + public function config($name = null) + { + $name = $name ? $name : $this->request->get("name"); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + $info = get_addon_info($name); + $config = get_addon_fullconfig($name); + if (!$info) { + $this->error(__('Addon not exists')); + } + if ($this->request->isPost()) { + $params = $this->request->post("row/a", [], 'trim'); + if ($params) { + foreach ($config as $k => &$v) { + if (isset($params[$v['name']])) { + if ($v['type'] == 'array') { + $params[$v['name']] = is_array($params[$v['name']]) ? $params[$v['name']] : (array)json_decode($params[$v['name']], true); + $value = $params[$v['name']]; + } else { + $value = is_array($params[$v['name']]) ? implode(',', $params[$v['name']]) : $params[$v['name']]; + } + $v['value'] = $value; + } + } + try { + $addon = get_addon_instance($name); + //插件自定义配置实现逻辑 + if (method_exists($addon, 'config')) { + $addon->config($name, $config); + } else { + //更新配置文件 + set_addon_fullconfig($name, $config); + Service::refresh(); + } + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', '')); + } + $tips = []; + $groupList = []; + foreach ($config as $index => &$item) { + //如果有设置分组 + if (isset($item['group']) && $item['group']) { + if (!in_array($item['group'], $groupList)) { + $groupList["custom" . (count($groupList) + 1)] = $item['group']; + } + } + if ($item['name'] == '__tips__') { + $tips = $item; + unset($config[$index]); + } + } + $groupList['other'] = '其它'; + $this->view->assign("groupList", $groupList); + $this->view->assign("addon", ['info' => $info, 'config' => $config, 'tips' => $tips]); + $configFile = ADDON_PATH . $name . DS . 'config.html'; + $viewFile = is_file($configFile) ? $configFile : ''; + return $this->view->fetch($viewFile); + } + + /** + * 安装 + */ + public function install() + { + $name = $this->request->post("name"); + $force = (int)$this->request->post("force"); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + + $info = []; + try { + $uid = $this->request->post("uid"); + $token = $this->request->post("token"); + $version = $this->request->post("version"); + $faversion = $this->request->post("faversion"); + $extend = [ + 'uid' => $uid, + 'token' => $token, + 'version' => $version, + 'faversion' => $faversion + ]; + $info = Service::install($name, $force, $extend); + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage()), $e->getCode()); + } + $this->success(__('Install successful'), '', ['addon' => $info]); + } + + /** + * 卸载 + */ + public function uninstall() + { + $name = $this->request->post("name"); + $force = (int)$this->request->post("force"); + $droptables = (int)$this->request->post("droptables"); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + //只有开启调试且为超级管理员才允许删除相关数据库 + $tables = []; + if ($droptables && Config::get("app_debug") && $this->auth->isSuperAdmin()) { + $tables = get_addon_tables($name); + } + try { + Service::uninstall($name, $force); + if ($tables) { + $prefix = Config::get('database.prefix'); + //删除插件关联表 + foreach ($tables as $index => $table) { + //忽略非插件标识的表名 + if (!preg_match("/^{$prefix}{$name}/", $table)) { + continue; + } + Db::execute("DROP TABLE IF EXISTS `{$table}`"); + } + } + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + $this->success(__('Uninstall successful')); + } + + /** + * 禁用启用 + */ + public function state() + { + $name = $this->request->post("name"); + $action = $this->request->post("action"); + $force = (int)$this->request->post("force"); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + try { + $action = $action == 'enable' ? $action : 'disable'; + //调用启用、禁用的方法 + Service::$action($name, $force); + Cache::rm('__menu__'); + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + $this->success(__('Operate successful')); + } + + /** + * 本地上传 + */ + public function local() + { + Config::set('default_return_type', 'json'); + + $info = []; + $file = $this->request->file('file'); + try { + $uid = $this->request->post("uid"); + $token = $this->request->post("token"); + $faversion = $this->request->post("faversion"); + if (!$uid || !$token) { + throw new Exception(__('Please login and try to install')); + } + $extend = [ + 'uid' => $uid, + 'token' => $token, + 'faversion' => $faversion + ]; + $info = Service::local($file, $extend); + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + $this->success(__('Offline installed tips'), '', ['addon' => $info]); + } + + /** + * 更新插件 + */ + public function upgrade() + { + $name = $this->request->post("name"); + $addonTmpDir = RUNTIME_PATH . 'addons' . DS; + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + if (!is_dir($addonTmpDir)) { + @mkdir($addonTmpDir, 0755, true); + } + + $info = []; + try { + $info = get_addon_info($name); + $uid = $this->request->post("uid"); + $token = $this->request->post("token"); + $version = $this->request->post("version"); + $faversion = $this->request->post("faversion"); + $extend = [ + 'uid' => $uid, + 'token' => $token, + 'version' => $version, + 'oldversion' => $info['version'] ?? '', + 'faversion' => $faversion + ]; + //调用更新的方法 + $info = Service::upgrade($name, $extend); + Cache::rm('__menu__'); + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + $this->success(__('Operate successful'), '', ['addon' => $info]); + } + + /** + * 测试数据 + */ + public function testdata() + { + $name = $this->request->post("name"); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + + try { + Service::importsql($name, 'testdata.sql'); + } catch (AddonException $e) { + $this->result($e->getData(), $e->getCode(), __($e->getMessage())); + } catch (Exception $e) { + $this->error(__($e->getMessage()), $e->getCode()); + } + $this->success(__('Import successful'), ''); + } + + /** + * 已装插件 + */ + public function downloaded() + { + $offset = (int)$this->request->get("offset"); + $limit = (int)$this->request->get("limit"); + $filter = $this->request->get("filter"); + $search = $this->request->get("search"); + $search = htmlspecialchars(strip_tags($search)); + $onlineaddons = $this->getAddonList(); + $filter = (array)json_decode($filter, true); + $addons = get_addon_list(); + $list = []; + foreach ($addons as $k => $v) { + if ($search && stripos($v['name'], $search) === false && stripos($v['title'], $search) === false && stripos($v['intro'], $search) === false) { + continue; + } + + if (isset($onlineaddons[$v['name']])) { + $v = array_merge($v, $onlineaddons[$v['name']]); + $v['price'] = '-'; + } else { + $v['category_id'] = 0; + $v['flag'] = ''; + $v['banner'] = ''; + $v['image'] = ''; + $v['demourl'] = ''; + $v['price'] = __('None'); + $v['screenshots'] = []; + $v['releaselist'] = []; + $v['url'] = addon_url($v['name']); + $v['url'] = str_replace($this->request->server('SCRIPT_NAME'), '', $v['url']); + } + $v['createtime'] = filemtime(ADDON_PATH . $v['name']); + if ($filter && isset($filter['category_id']) && is_numeric($filter['category_id']) && $filter['category_id'] != $v['category_id']) { + continue; + } + $list[] = $v; + } + $total = count($list); + if ($limit) { + $list = array_slice($list, $offset, $limit); + } + $result = array("total" => $total, "rows" => $list); + + $callback = $this->request->get('callback') ? "jsonp" : "json"; + return $callback($result); + } + + /** + * 检测 + */ + public function isbuy() + { + $name = $this->request->post("name"); + $uid = $this->request->post("uid"); + $token = $this->request->post("token"); + $version = $this->request->post("version"); + $faversion = $this->request->post("faversion"); + $extend = [ + 'uid' => $uid, + 'token' => $token, + 'version' => $version, + 'faversion' => $faversion + ]; + try { + $result = Service::isBuy($name, $extend); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + return json($result); + } + + /** + * 刷新授权 + */ + public function authorization() + { + $params = [ + 'uid' => $this->request->post('uid'), + 'token' => $this->request->post('token'), + 'faversion' => $this->request->post('faversion'), + ]; + try { + Service::authorization($params); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + $this->success(__('Operate successful')); + } + + /** + * 获取插件相关表 + */ + public function get_table_list() + { + $name = $this->request->post("name"); + if (!preg_match("/^[a-zA-Z0-9]+$/", $name)) { + $this->error(__('Addon name incorrect')); + } + $tables = get_addon_tables($name); + $prefix = Config::get('database.prefix'); + foreach ($tables as $index => $table) { + //忽略非插件标识的表名 + if (!preg_match("/^{$prefix}{$name}/", $table)) { + unset($tables[$index]); + } + } + $tables = array_values($tables); + $this->success('', null, ['tables' => $tables]); + } + + protected function getAddonList() + { + $onlineaddons = Cache::get("onlineaddons"); + if (!is_array($onlineaddons) && config('fastadmin.api_url')) { + $onlineaddons = []; + $params = [ + 'uid' => $this->request->post('uid'), + 'token' => $this->request->post('token'), + 'version' => config('fastadmin.version'), + 'faversion' => config('fastadmin.version'), + ]; + $json = []; + try { + $json = Service::addons($params); + } catch (\Exception $e) { + + } + $rows = isset($json['rows']) ? $json['rows'] : []; + foreach ($rows as $index => $row) { + $onlineaddons[$row['name']] = $row; + } + Cache::set("onlineaddons", $onlineaddons, 600); + } + return $onlineaddons; + } + +} diff --git a/application/admin/controller/Ajax.php b/application/admin/controller/Ajax.php new file mode 100644 index 0000000..2651bba --- /dev/null +++ b/application/admin/controller/Ajax.php @@ -0,0 +1,313 @@ +request->filter(['trim', 'strip_tags', 'htmlspecialchars']); + } + + /** + * 加载语言包 + */ + public function lang() + { + $this->request->get(['callback' => 'define']); + $header = ['Content-Type' => 'application/javascript']; + if (!config('app_debug')) { + $offset = 30 * 60 * 60 * 24; // 缓存一个月 + $header['Cache-Control'] = 'public'; + $header['Pragma'] = 'cache'; + $header['Expires'] = gmdate("D, d M Y H:i:s", time() + $offset) . " GMT"; + } + + $controllername = input("controllername"); + //默认只加载了控制器对应的语言名,你还根据控制器名来加载额外的语言包 + $this->loadlang($controllername); + return jsonp(Lang::get(), 200, $header, ['json_encode_param' => JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE]); + } + + /** + * 上传文件 + */ + public function upload() + { + Config::set('default_return_type', 'json'); + //必须设定cdnurl为空,否则cdnurl函数计算错误 + Config::set('upload.cdnurl', ''); + $chunkid = $this->request->post("chunkid"); + if ($chunkid) { + if (!Config::get('upload.chunking')) { + $this->error(__('Chunk file disabled')); + } + $action = $this->request->post("action"); + $chunkindex = $this->request->post("chunkindex/d"); + $chunkcount = $this->request->post("chunkcount/d"); + $filename = $this->request->post("filename"); + $method = $this->request->method(true); + if ($action == 'merge') { + $attachment = null; + //合并分片文件 + try { + $upload = new Upload(); + $attachment = $upload->merge($chunkid, $chunkcount, $filename); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + $this->success(__('Uploaded successful'), '', ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]); + } elseif ($method == 'clean') { + //删除冗余的分片文件 + try { + $upload = new Upload(); + $upload->clean($chunkid); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + $this->success(); + } else { + //上传分片文件 + //默认普通上传文件 + $file = $this->request->file('file'); + try { + $upload = new Upload($file); + $upload->chunk($chunkid, $chunkindex, $chunkcount); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + $this->success(); + } + } else { + $attachment = null; + //默认普通上传文件 + $file = $this->request->file('file'); + try { + $upload = new Upload($file); + $attachment = $upload->upload(); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + + $this->success(__('Uploaded successful'), '', ['url' => $attachment->url, 'fullurl' => $attachment->url]); + } + } + + /** + * 通用排序 + */ + public function weigh() + { + //排序的数组 + $ids = $this->request->post("ids"); + //拖动的记录ID + $changeid = $this->request->post("changeid"); + //操作字段 + $field = $this->request->post("field"); + //操作的数据表 + $table = $this->request->post("table"); + if (!Validate::is($table, "alphaDash")) { + $this->error(); + } + //主键 + $pk = $this->request->post("pk"); + //排序的方式 + $orderway = strtolower($this->request->post("orderway", "")); + $orderway = $orderway == 'asc' ? 'ASC' : 'DESC'; + $sour = $weighdata = []; + $ids = explode(',', $ids); + $prikey = $pk && preg_match("/^[a-z0-9\-_]+$/i", $pk) ? $pk : (Db::name($table)->getPk() ?: 'id'); + $pid = $this->request->post("pid", ""); + //限制更新的字段 + $field = in_array($field, ['weigh']) ? $field : 'weigh'; + + // 如果设定了pid的值,此时只匹配满足条件的ID,其它忽略 + if ($pid !== '') { + $hasids = []; + $list = Db::name($table)->where($prikey, 'in', $ids)->where('pid', 'in', $pid)->field("{$prikey},pid")->select(); + foreach ($list as $k => $v) { + $hasids[] = $v[$prikey]; + } + $ids = array_values(array_intersect($ids, $hasids)); + } + + $list = Db::name($table)->field("$prikey,$field")->where($prikey, 'in', $ids)->order($field, $orderway)->select(); + foreach ($list as $k => $v) { + $sour[] = $v[$prikey]; + $weighdata[$v[$prikey]] = $v[$field]; + } + $position = array_search($changeid, $ids); + $desc_id = isset($sour[$position]) ? $sour[$position] : end($sour); //移动到目标的ID值,取出所处改变前位置的值 + $sour_id = $changeid; + $weighids = array(); + $temp = array_values(array_diff_assoc($ids, $sour)); + foreach ($temp as $m => $n) { + if ($n == $sour_id) { + $offset = $desc_id; + } else { + if ($sour_id == $temp[0]) { + $offset = isset($temp[$m + 1]) ? $temp[$m + 1] : $sour_id; + } else { + $offset = isset($temp[$m - 1]) ? $temp[$m - 1] : $sour_id; + } + } + if (!isset($weighdata[$offset])) { + continue; + } + $weighids[$n] = $weighdata[$offset]; + Db::name($table)->where($prikey, $n)->update([$field => $weighdata[$offset]]); + } + $this->success(); + } + + /** + * 清空系统缓存 + */ + public function wipecache() + { + try { + $type = $this->request->request("type"); + switch ($type) { + case 'all': + // no break + case 'content': + //内容缓存 + rmdirs(CACHE_PATH, false); + Cache::clear(); + if ($type == 'content') { + break; + } + case 'template': + // 模板缓存 + rmdirs(TEMP_PATH, false); + if ($type == 'template') { + break; + } + case 'addons': + // 插件缓存 + Service::refresh(); + if ($type == 'addons') { + break; + } + case 'browser': + // 浏览器缓存 + // 只有生产环境下才修改 + if (!config('app_debug')) { + $version = config('site.version'); + $newversion = preg_replace_callback("/(.*)\.([0-9]+)\$/", function ($match) { + return $match[1] . '.' . ($match[2] + 1); + }, $version); + if ($newversion && $newversion != $version) { + Db::startTrans(); + try { + \app\common\model\Config::where('name', 'version')->update(['value' => $newversion]); + \app\common\model\Config::refreshFile(); + Db::commit(); + } catch (\Exception $e) { + Db::rollback(); + exception($e->getMessage()); + } + } + } + if ($type == 'browser') { + break; + } + } + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + + \think\Hook::listen("wipecache_after"); + $this->success(); + } + + /** + * 读取分类数据,联动列表 + */ + public function category() + { + $type = $this->request->get('type', ''); + $pid = $this->request->get('pid', ''); + $where = ['status' => 'normal']; + $categorylist = null; + if ($pid || $pid === '0') { + $where['pid'] = $pid; + } + if ($type) { + $where['type'] = $type; + } + + $categorylist = Db::name('category')->where($where)->field('id as value,name')->order('weigh desc,id desc')->select(); + + $this->success('', '', $categorylist); + } + + /** + * 读取省市区数据,联动列表 + */ + public function area() + { + $params = $this->request->get("row/a"); + if (!empty($params)) { + $province = isset($params['province']) ? $params['province'] : null; + $city = isset($params['city']) ? $params['city'] : null; + } else { + $province = $this->request->get('province'); + $city = $this->request->get('city'); + } + $where = ['pid' => 0, 'level' => 1]; + $provincelist = null; + if ($province !== null) { + $where['pid'] = $province; + $where['level'] = 2; + if ($city !== null) { + $where['pid'] = $city; + $where['level'] = 3; + } + } + $provincelist = Db::name('area')->where($where)->field('id as value,name')->select(); + $this->success('', '', $provincelist); + } + + /** + * 生成后缀图标 + */ + public function icon() + { + $suffix = $this->request->request("suffix"); + $suffix = $suffix ? $suffix : "FILE"; + $data = build_suffix_image($suffix); + $header = ['Content-Type' => 'image/svg+xml']; + $offset = 30 * 60 * 60 * 24; // 缓存一个月 + $header['Cache-Control'] = 'public'; + $header['Pragma'] = 'cache'; + $header['Expires'] = gmdate("D, d M Y H:i:s", time() + $offset) . " GMT"; + $response = Response::create($data, '', 200, $header); + return $response; + } + +} diff --git a/application/admin/controller/Category.php b/application/admin/controller/Category.php new file mode 100644 index 0000000..c9294ed --- /dev/null +++ b/application/admin/controller/Category.php @@ -0,0 +1,158 @@ +系统配置->字典配置中添加 + */ +class Category extends Backend +{ + + /** + * @var \app\common\model\Category + */ + protected $model = null; + protected $categorylist = []; + protected $noNeedRight = ['selectpage']; + + public function _initialize() + { + parent::_initialize(); + $this->model = model('app\common\model\Category'); + + $tree = Tree::instance(); + $tree->init(collection($this->model->order('weigh desc,id desc')->select())->toArray(), 'pid'); + $this->categorylist = $tree->getTreeList($tree->getTreeArray(0), 'name'); + $categorydata = [0 => ['type' => 'all', 'name' => __('None')]]; + foreach ($this->categorylist as $k => $v) { + $categorydata[$v['id']] = $v; + } + $typeList = CategoryModel::getTypeList(); + $this->view->assign("flagList", $this->model->getFlagList()); + $this->view->assign("typeList", $typeList); + $this->view->assign("parentList", $categorydata); + $this->assignconfig('typeList', $typeList); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + $search = $this->request->request("search"); + $type = $this->request->request("type"); + + //构造父类select列表选项数据 + $list = []; + + foreach ($this->categorylist as $k => $v) { + if ($search) { + if ($v['type'] == $type && stripos($v['name'], $search) !== false || stripos($v['nickname'], $search) !== false) { + if ($type == "all" || $type == null) { + $list = $this->categorylist; + } else { + $list[] = $v; + } + } + } else { + if ($type == "all" || $type == null) { + $list = $this->categorylist; + } elseif ($v['type'] == $type) { + $list[] = $v; + } + } + } + + $total = count($list); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $this->token(); + } + return parent::add(); + } + + /** + * 编辑 + */ + 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()) { + $this->token(); + $params = $this->request->post("row/a"); + if ($params) { + $params = $this->preExcludeFields($params); + + if ($params['pid'] != $row['pid']) { + $childrenIds = Tree::instance()->init(collection(\app\common\model\Category::select())->toArray())->getChildrenIds($row['id'], true); + if (in_array($params['pid'], $childrenIds)) { + $this->error(__('Can not change the parent to child or itself')); + } + } + + 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->validate($validate); + } + $result = $row->allowField(true)->save($params); + if ($result !== false) { + $this->success(); + } else { + $this->error($row->getError()); + } + } catch (\think\exception\PDOException $e) { + $this->error($e->getMessage()); + } catch (\think\Exception $e) { + $this->error($e->getMessage()); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + $this->view->assign("row", $row); + return $this->view->fetch(); + } + + + /** + * Selectpage搜索 + * + * @internal + */ + public function selectpage() + { + return parent::selectpage(); + } +} diff --git a/application/admin/controller/Command.php b/application/admin/controller/Command.php new file mode 100644 index 0000000..00d32d4 --- /dev/null +++ b/application/admin/controller/Command.php @@ -0,0 +1,248 @@ +model = new \app\admin\model\Command; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + /** + * 添加 + */ + public function add() + { + + $tableList = []; + $list = \think\Db::query("SHOW TABLES"); + foreach ($list as $key => $row) { + $tableList[reset($row)] = reset($row); + } + + $this->view->assign("tableList", $tableList); + return $this->view->fetch(); + } + + /** + * 获取字段列表 + * @internal + */ + public function get_field_list() + { + $dbname = Config::get('database.database'); + $prefix = Config::get('database.prefix'); + $table = $this->request->request('table'); + //从数据库中获取表字段信息 + $sql = "SELECT * FROM `information_schema`.`columns` " + . "WHERE TABLE_SCHEMA = ? AND table_name = ? " + . "ORDER BY ORDINAL_POSITION"; + //加载主表的列 + $columnList = Db::query($sql, [$dbname, $table]); + $fieldlist = []; + foreach ($columnList as $index => $item) { + $fieldlist[] = $item['COLUMN_NAME']; + } + $this->success("", null, ['fieldlist' => $fieldlist]); + } + + /** + * 获取控制器列表 + * @internal + */ + public function get_controller_list() + { + //搜索关键词,客户端输入以空格分开,这里接收为数组 + $word = (array)$this->request->request("q_word/a"); + $word = implode('', $word); + + $adminPath = dirname(__DIR__) . DS; + $controllerDir = $adminPath . 'controller' . DS; + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY + ); + $list = []; + foreach ($files as $name => $file) { + if (!$file->isDir()) { + $filePath = $file->getRealPath(); + $name = str_replace($controllerDir, '', $filePath); + $name = str_replace(DS, "/", $name); + if (!preg_match("/(.*)\.php\$/", $name)) { + continue; + } + if (!$word || stripos($name, $word) !== false) { + $list[] = ['id' => $name, 'name' => $name]; + } + } + } + $pageNumber = $this->request->request("pageNumber"); + $pageSize = $this->request->request("pageSize"); + return json(['list' => array_slice($list, ($pageNumber - 1) * $pageSize, $pageSize), 'total' => count($list)]); + } + + /** + * 详情 + */ + public function detail($ids) + { + $row = $this->model->get($ids); + if (!$row) { + $this->error(__('No Results were found')); + } + $this->view->assign("row", $row); + return $this->view->fetch(); + } + + /** + * 执行 + */ + public function execute($ids) + { + $row = $this->model->get($ids); + if (!$row) { + $this->error(__('No Results were found')); + } + $result = $this->doexecute($row['type'], json_decode($row['params'], true)); + $this->success("", null, ['result' => $result]); + } + + /** + * 生成命令 + */ + public function command($action = '') + { + $commandtype = $this->request->request("commandtype"); + $params = $this->request->request(); + $allowfields = [ + 'crud' => 'table,controller,model,fields,force,local,delete,menu', + 'menu' => 'controller,delete', + 'min' => 'module,resource,optimize', + 'api' => 'url,module,output,template,force,title,author,class,language,addon', + ]; + $argv = []; + $allowfields = isset($allowfields[$commandtype]) ? explode(',', $allowfields[$commandtype]) : []; + $allowfields = array_filter(array_intersect_key($params, array_flip($allowfields))); + if (isset($params['local']) && !$params['local']) { + $allowfields['local'] = $params['local']; + } else { + unset($allowfields['local']); + } + foreach ($allowfields as $key => $param) { + $argv[] = "--{$key}=" . (is_array($param) ? implode(',', $param) : $param); + } + if ($commandtype == 'crud') { + $extend = 'setcheckboxsuffix,enumradiosuffix,imagefield,filefield,intdatesuffix,switchsuffix,citysuffix,selectpagesuffix,selectpagessuffix,ignorefields,sortfield,editorsuffix,headingfilterfield,tagsuffix,jsonsuffix,fixedcolumns'; + $extendArr = explode(',', $extend); + foreach ($params as $index => $item) { + if (in_array($index, $extendArr)) { + foreach (explode(',', $item) as $key => $value) { + if ($value) { + $argv[] = "--{$index}={$value}"; + } + } + } + } + $isrelation = (int)$this->request->request('isrelation'); + if ($isrelation && isset($params['relation'])) { + foreach ($params['relation'] as $index => $relation) { + foreach ($relation as $key => $value) { + $argv[] = "--{$key}=" . (is_array($value) ? implode(',', $value) : $value); + } + } + } + } else { + if ($commandtype == 'menu') { + if (isset($params['allcontroller']) && $params['allcontroller']) { + $argv[] = "--controller=all-controller"; + } else { + foreach (explode(',', $params['controllerfile']) as $index => $param) { + if ($param) { + $argv[] = "--controller=" . substr($param, 0, -4); + } + } + } + } else { + if ($commandtype == 'min') { + + } else { + if ($commandtype == 'api') { + + } else { + + } + } + } + } + if ($action == 'execute') { + if (stripos(implode(' ', $argv), '--controller=all-controller') !== false) { + $this->error("只允许在命令行执行该命令,执行前请做好菜单规则备份!!!"); + } + if (config('app_debug')) { + $result = $this->doexecute($commandtype, $argv); + $this->success("", null, ['result' => $result]); + } else { + $this->error("只允许在开发环境下执行命令"); + } + } else { + $this->success("", null, ['command' => "php think {$commandtype} " . implode(' ', $argv)]); + } + + return; + } + + protected function doexecute($commandtype, $argv) + { + if (!config('app_debug')) { + $this->error("只允许在开发环境下执行命令"); + } + if (preg_match("/([;\|&]+)/", implode(' ', $argv))) { + $this->error("不支持的命令参数"); + } + $commandName = "\\app\\admin\\command\\" . ucfirst($commandtype); + $input = new Input($argv); + $output = new \addons\command\library\Output(); + $command = new $commandName($commandtype); + $data = [ + 'type' => $commandtype, + 'params' => json_encode($argv), + 'command' => "php think {$commandtype} " . implode(' ', $argv), + 'executetime' => time(), + ]; + $this->model->save($data); + try { + $command->run($input, $output); + $result = implode("\n", $output->getMessage()); + $this->model->status = 'successed'; + } catch (Exception $e) { + $result = implode("\n", $output->getMessage()) . "\n"; + $result .= $e->getMessage(); + $this->model->status = 'failured'; + } + $result = trim($result); + $this->model->content = $result; + $this->model->save(); + return $result; + } + +} diff --git a/application/admin/controller/Contestants.php b/application/admin/controller/Contestants.php new file mode 100644 index 0000000..c4450d7 --- /dev/null +++ b/application/admin/controller/Contestants.php @@ -0,0 +1,329 @@ +model = new MatchContestant(); + } + + public function check_leagueuser() + { + $Club = new Club(); + $User = new User(); + $Wxmb = new Wxmb(); + $archives = new Archives(); + $ClubThirdOauth = new ClubThirdOauth(); + $type = $this->request->param('type'); + $id = $this->request->param('ids'); + $mark = $this->request->param('mark'); + $apply_res = $this->model->field('match_id,club_id')->where('id', 'eq', $id)->find(); + $club_info = $Club->where("id", $apply_res['club_id'])->find(); + $user_info = $User->where('id',$club_info['user_id'])->find(); + // $matchId = $apply_res['match_id']; + // var_dump($matchId['match_id']);exit; + // $apply_allplayer = $this->model->where(['club_id'=>$apply_res['club_id'],'match_id'=>$apply_res['match_id'],'club_status'=>1])->find(); + $apply_allplayer = $this->model->where(['club_id'=>$apply_res['club_id'],'match_id'=>$apply_res['match_id']])->select(); + + $info = $this->model->find($id); + if (empty($apply_res)){ + $this->error(__("信息出错,请刷新后重试!")); + } + + if ($type == "pass"){ + // 启动事务 + Db::startTrans(); + try{ + // var_dump($apply_allplayer);exit; + foreach ($apply_allplayer as $value){ + $data = array('status'=>2,'updated_at' => date("Y-m-d H:i:s", time())); + $this->model->where('id', $value['id'])->data($data,true)->update(); + } + // 提交事务 + Db::commit(); + } catch (\Exception $e) { + // 回滚事务 + Db::rollback(); + $this->error(__("操作失败!")); + } + $oauth_res = $ClubThirdOauth->where('user_id',$club_info['user_id'])->find(); + if(!empty($oauth_res)){ + $oauth_res = $oauth_res->toArray(); + $push_arr = array('title'=>'赛事报名审核通过','username'=> $club_info['name_short'],'type'=>'系统通知'); + $Wxmb->leaguepush($push_arr,$oauth_res['openid']); + } + #发短信 + if (!empty($user_info->mobile)) { + // $user_info->mobile = '13626832346'; + $match_info = $archives->find($info->match_id); + $alisms = new \addons\alisms\library\Alisms(); + $config = get_addon_config('alisms'); + $template = "SMS_466345112"; + // $sign = $config['sign']; + $sign = '中国无人机竞速联赛'; + $param = [ + 'name' => $club_info->name_short, + 'content' => "《" . $match_info->title . "》" + ]; + $res = $alisms->mobile($user_info->mobile) + ->template($template) + ->sign($sign) + ->param($param) + ->send(); + } + + $this->success(__("审核成功!")); + + } + + if ($type == "refuse"){ + //获取飞手信息 + + if (!empty($user_info->mobile)) { + // $user_info->mobile = '13626832346'; + // var_dump($user_info->mobile);exit; + //获取赛事信息 + $match_info = $archives->find($info->match_id); + $alisms = new \addons\alisms\library\Alisms(); + $config = get_addon_config('alisms'); + $template = "SMS_466440085"; + $sign = '中国无人机竞速联赛'; + $param = [ + 'name' => $club_info->name_short, + 'content' => "《" . $match_info->title . "》" + ]; + // var_dump($club_info->name_short);exit; + $res = $alisms->mobile($user_info->mobile) + ->template($template) + ->sign($sign) + ->param($param) + ->send(); + // var_dump($res);exit; + + Db::startTrans(); + try{ + // var_dump($apply_allplayer);exit; + foreach ($apply_allplayer as $value){ + $data = array('status'=> -1,'updated_at' => date("Y-m-d H:i:s", time())); + $this->model->where('id', $value['id'])->data($data,true)->update(); + } + // exit; + // 提交事务 + Db::commit(); + } catch (\Exception $e) { + // 回滚事务 + Db::rollback(); + $this->error(__("操作失败2!")); + } + $this->success(__("驳回成功!")); + $messageLogModel = new MessageLog(); + + $message_log_id = $messageLogModel->insertGetId( + [ + 'phone' => $user_info->mobile, + 'code' => $param['content'], + 'code2' => $mark, + 'type' => 4, + 'used' => 0, + 'template_id' => $template, + 'created_at' => date("Y-m-d H:i:s", time()) + ] + ); + } + + $info->save(['message_logs_id' => $message_log_id]); + + if ($info->save(['status' => -1, 'updated_at' => date("Y-m-d H:i:s", time()), 'mark' => $mark])){ + $this->success(__("审核成功!")); + } + + $this->error(__("审核失败!")); + } + + + } + + public function check_user() + { + $type = $this->request->param('type'); + $id = $this->request->param('ids'); + $mark = $this->request->param('mark'); + + $info = $this->model->find($id); + if (empty($info)){ + $this->error(__("信息出错,请刷新后重试!")); + } + + if ($type == "pass"){ + if ($info->save(['status' => 2, 'updated_at' => date("Y-m-d H:i:s", time())])){ + $this->success(__("审核成功!")); + } + + $this->error(__("审核失败!")); + } + + if ($type == "refuse"){ + //获取飞手信息 + $playerModel = new Players(); + $player_info = $playerModel->with('member')->find($info->player_id); + if (!empty($player_info->phone)) { + //获取赛事信息 + $archives = new Archives(); + $match_info = $archives->find($info->match_id); + + $alisms = new \addons\alisms\library\Alisms(); + + $config = get_addon_config('alisms'); + + $template = "SMS_463726010"; + + $sign = $config['sign']; + $param = [ + 'name' => $player_info->real_name, + 'content' => "《" . $match_info->title . "》" + ]; + + $alisms->mobile($player_info->phone) + ->template($template) + ->sign($sign) + ->param($param) + ->send(); + + $messageLogModel = new MessageLog(); + + $message_log_id = $messageLogModel->insertGetId( + [ + 'phone' => $player_info->phone, + 'code' => $param['content'], + 'code2' => $mark, + 'type' => 4, + 'used' => 0, + 'template_id' => $template, + 'created_at' => date("Y-m-d H:i:s", time()) + ] + ); + } + + $info->save(['message_logs_id' => $message_log_id]); + + if ($info->save(['status' => -1, 'updated_at' => date("Y-m-d H:i:s", time()), 'mark' => $mark])){ + $this->success(__("审核成功!")); + } + + $this->error(__("审核失败!")); + } + + + } + + public function sendSMS() + { + $id = $this->request->param('ids'); + //获取飞手信息 + $match_contestants_info = $this->model->find($id); + + if (empty($match_contestants_info)){ + $this->error(__("信息出错,请刷新后重试!")); + } + + if ($match_contestants_info->status != 2){ + $this->error(__("无法给审核未通过的选手发送短信!")); + } + + //获取飞手信息 + $playerModel = new Players(); + $player_info = $playerModel->with('member')->find($match_contestants_info->player_id); + + $archives = new Archives(); + $match_info = $archives->find($match_contestants_info->match_id); + + if (empty($player_info) || empty($player_info->member)){ + $this->error(__("获取用户信息出错!")); + } + $mobile = $player_info->phone ? $player_info->phone : $player_info->member->mobile; + + if (empty($mobile) || !isset($mobile)){ + $this->error(__("获取用户手机号出错!")); + } + + $alisms = new \addons\alisms\library\Alisms(); + + $config = get_addon_config('alisms'); + + $template = $config['template']['notice']; + + $sign = $config['sign']; + $param = [ + 'name' => $player_info->real_name, + 'matchTitle' => "《" . $match_info->title . "》" + ]; + + $ret = $alisms->mobile($mobile) + ->template($template) + ->sign($sign) + ->param($param) + ->send(); + + $messageLogModel = new MessageLog(); + + $message_log_id = $messageLogModel->insertGetId( + [ + 'phone' => $mobile, + 'type' => 4, + 'used' => 0, + 'template_id' => $template, + 'created_at' => date("Y-m-d H:i:s", time()) + ] + ); + + + + if ($ret && $message_log_id && $match_contestants_info->save(['message_logs_id' => $message_log_id])){ + $this->success(__("发送成功!")); + } + } + + /** + * 删除 + * @param mixed $ids + */ + public function del($ids = "") + { + $res = $this->model->where('id',intval($ids))->find(); + // var_dump($res);exit; + if($res['club_id']){ + $contes_res = $this->model->where('club_id',$res['club_id'])->where('match_id',$res['match_id'])->select(); + $str = ''; + foreach ($contes_res as $val){ + $str = $str.','.$val['id']; + } + parent::del($str); + }else{ + parent::del($ids); + } + } +} diff --git a/application/admin/controller/Countryflag.php b/application/admin/controller/Countryflag.php new file mode 100644 index 0000000..8873b12 --- /dev/null +++ b/application/admin/controller/Countryflag.php @@ -0,0 +1,75 @@ +model = new \app\admin\model\Countryflag; + + } + + public function add() + { + if (false === $this->request->isPost()) { + return $this->view->fetch(); + } + $params = $this->request->post('row/a'); + if (empty($params)) { + $this->error(__('Parameter %s can not be empty', '')); + } + // var_dump($params);exit; + $params = $this->preExcludeFields($params); + // var_dump($params);exit; + if ($this->dataLimit && $this->dataLimitFieldAutoFill) { + $params[$this->dataLimitField] = $this->auth->id; + } + $country_res = $this->model->where('country',$params['country'])->find(); + if($country_res){ + $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()->validate($validate); + } + $result = $this->model->allowField(true)->save($params); + Db::commit(); + } catch (ValidateException|PDOException|Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($result === false) { + $this->error(__('No rows were inserted')); + } + $this->success(); + } + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + + +} diff --git a/application/admin/controller/Dashboard.php b/application/admin/controller/Dashboard.php new file mode 100644 index 0000000..2dd2e06 --- /dev/null +++ b/application/admin/controller/Dashboard.php @@ -0,0 +1,84 @@ +where('jointime', 'between time', [$starttime, $endtime]) + ->field('jointime, status, COUNT(*) AS nums, DATE_FORMAT(FROM_UNIXTIME(jointime), "%Y-%m-%d") AS join_date') + ->group('join_date') + ->select(); + for ($time = $starttime; $time <= $endtime;) { + $column[] = date("Y-m-d", $time); + $time += 86400; + } + $userlist = array_fill_keys($column, 0); + foreach ($joinlist as $k => $v) { + $userlist[$v['join_date']] = $v['nums']; + } + + $dbTableList = Db::query("SHOW TABLE STATUS"); + $addonList = get_addon_list(); + $totalworkingaddon = 0; + $totaladdon = count($addonList); + foreach ($addonList as $index => $item) { + if ($item['state']) { + $totalworkingaddon += 1; + } + } + $this->view->assign([ + 'totaluser' => User::count(), + 'totaladdon' => $totaladdon, + 'totaladmin' => Admin::count(), + 'totalcategory' => \app\common\model\Category::count(), + 'todayusersignup' => User::whereTime('jointime', 'today')->count(), + 'todayuserlogin' => User::whereTime('logintime', 'today')->count(), + 'sevendau' => User::whereTime('jointime|logintime|prevtime', '-7 days')->count(), + 'thirtydau' => User::whereTime('jointime|logintime|prevtime', '-30 days')->count(), + 'threednu' => User::whereTime('jointime', '-3 days')->count(), + 'sevendnu' => User::whereTime('jointime', '-7 days')->count(), + 'dbtablenums' => count($dbTableList), + 'dbsize' => array_sum(array_map(function ($item) { + return $item['Data_length'] + $item['Index_length']; + }, $dbTableList)), + 'totalworkingaddon' => $totalworkingaddon, + 'attachmentnums' => Attachment::count(), + 'attachmentsize' => Attachment::sum('filesize'), + 'picturenums' => Attachment::where('mimetype', 'like', 'image/%')->count(), + 'picturesize' => Attachment::where('mimetype', 'like', 'image/%')->sum('filesize'), + ]); + + $this->assignconfig('column', array_keys($userlist)); + $this->assignconfig('userdata', array_values($userlist)); + + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/Docs.php b/application/admin/controller/Docs.php new file mode 100644 index 0000000..d87364a --- /dev/null +++ b/application/admin/controller/Docs.php @@ -0,0 +1,432 @@ +docsdata = Service::getDocsData(false); + + $config = get_addon_config('docs'); + $this->typeList = $this->typeColor = []; + $langs = $config['lang']; + $colors = ["primary", "success", "danger", "warning", "info"]; + foreach ($this->docsdata as $type => $pages) { + $this->typeList[$type] = $langs[$type] ?? $type; + $this->typeColor[$type] = array_pop($colors); + } + $this->view->assign('typeList', $this->typeList); + $this->assignconfig('typeList', $this->typeList); + + $this->view->assign('typeColor', $this->typeColor); + $this->assignconfig('typeColor', $this->typeColor); + $this->assignconfig('htmlDetect', (bool)($config['htmldetect'] ?? false)); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + $filter = $this->request->request('filter', ''); + $filter = (array)json_decode($filter, true); + $filter['type'] = isset($filter['type']) && $filter['type'] ? $filter['type'] : ''; + $search = $this->request->request("search"); + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $list = []; + $docsdata = $this->docsdata; + if ($filter['type']) { + $docsdata = isset($docsdata[$filter['type']]) ? [$filter['type'] => $docsdata[$filter['type']]] : []; + } + foreach ($docsdata as $index => $docsdatum) { + $tree = Tree::instance(); + $data = $tree->init($docsdatum, 'parent')->getTreeArray(''); + $list = array_merge($list, $tree->getTreeList($data, 'title')); + } + $ids = []; + foreach ($list as $index => $item) { + $ids[] = $item['id']; + } + foreach ($docsdata as $index => $docsdatum) { + foreach ($docsdatum as $key => $item) { + if (!in_array($item['id'], $ids)) { + $list[] = $item; + } + } + } + //foreach ($this->docsdata as $type => $pages) { + // if ($filter['type'] && $type != $filter['type']) { + // continue; + // } + // foreach ($pages as $index => $page) { + // if (!$search || stripos($page['relative'], $search) !== false || stripos($page['title'], $search) !== false) { + // $list[] = $page; + // } + // } + //} + $total = count($list); + $result = array("total" => $total, "rows" => array_slice($list, $offset, $limit)); + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $params = $this->request->post("row/a"); + if ($params) { + try { + if (!$params['md']) { + throw new Exception("MD文件不能为空"); + } + $config = get_addon_config('docs'); + $params['md'] = trim($params['md']); + //未设置后缀 + if (stripos($params['md'], ".{$config['suffix']}") === false) { + $params['md'] .= ".{$config['suffix']}"; + } + if (mb_substr_count($params['md'], "/") > 1) { + throw new Exception("MD文件中只能包含一个/字符"); + } + if (mb_substr_count($params['md'], ".") > 1) { + throw new Exception("MD文件中只能包含一个.字符"); + } + if (!Validate::is($params['md'], "/^[A-Za-z0-9\-\_\.\/]+$/")) { + throw new Exception("MD文件只能是字母数字下划线"); + } + $exist = Service::getMarkdownData($params['md']); + if ($exist) { + throw new Exception("对应的MD文件已经存在"); + } + $parent = $params['parent'] ?? ''; + $type = $params['type'] ?? ''; + if ($parent) { + if ($parent == str_replace(".md", "", $params['md'])) { + $this->error("父级不能为当前文件"); + } + if (!isset($this->docsdata[$type])) { + $this->error("未找到指定分类"); + } + $nameArr = []; + foreach ($this->docsdata[$type] as $index => $docsdatum) { + $nameArr[] = $docsdatum['name']; + } + if (!in_array($parent, $nameArr)) { + $this->error("未找到指定父级数据"); + } + } + Service::setMarkdownData($params['md'], $params); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', '')); + } + return $this->view->fetch(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + $ids = $ids ? $ids : $this->request->request('ids'); + $row = Service::getMarkdownData($ids, true, false); + if (!$row) { + $this->error(__('No Results were found')); + } + if ($this->request->isPost()) { + $params = $this->request->post("row/a"); + if ($params) { + $config = get_addon_config('docs'); + $params['md'] = trim(isset($params['md']) && $params['md'] ? $params['md'] : $row['relative']); + $md = $params['md']; + $parent = $params['parent'] ?? ''; + $type = $params['type'] ?? ''; + if ($parent) { + if ($parent == str_replace(".md", "", $ids)) { + $this->error("父级不能为当前文件"); + } + if (!isset($this->docsdata[$type])) { + $this->error("未找到指定分类"); + } + $nameArr = []; + foreach ($this->docsdata[$type] as $index => $docsdatum) { + $nameArr[] = $docsdatum['name']; + } + if (!in_array($parent, $nameArr)) { + $this->error("未找到指定父级数据"); + } + } + //如果文件名有变更,删除原文件 + if ($md != $row['relative']) { + //未设置后缀 + if (stripos($params['md'], ".{$config['suffix']}") === false) { + $params['md'] .= ".{$config['suffix']}"; + } + if (mb_substr_count($params['md'], "/") > 1) { + throw new Exception("MD文件中只能包含一个/字符"); + } + if (mb_substr_count($params['md'], ".") > 1) { + throw new Exception("MD文件中只能包含一个.字符"); + } + if (!Validate::is($params['md'], "/^[A-Za-z0-9\-\_\.\/]+$/")) { + throw new Exception("MD文件只能是字母数字下划线"); + } + $exist = Service::getMarkdownData($params['md']); + if ($exist) { + throw new Exception("对应的MD文件已经存在"); + } + } + try { + //如果变更了分类,则所有子文档分类都需要变更 + if ($type != $row['type']) { + $docs = $this->docsdata[$row['type']] ?? []; + $childrenList = Tree::instance()->init($docs, 'parent')->getChildren($row['id']); + foreach ($childrenList as $index => $item) { + $data = Service::getMarkdownData($item['relative'], true); + $data['type'] = $type; + Service::setMarkdownData($item['relative'], $data); + } + } + Service::setMarkdownData($md, $params); + //如果文件名有变更,删除原文件 + if ($md != $row['relative']) { + unlink(Service::getDocsSourceDir() . $row['relative']); + } + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', '')); + } + $allowEditName = true; + $parentList = isset($this->docsdata[$row['type']]) ? $this->docsdata[$row['type']] : []; + foreach ($parentList as $index => $item) { + if ($item['parent'] == $row['name']) { + $allowEditName = false; + break; + } + } + $parentOptions = $this->getParentList($row['type'], $row['id'], $row['parent']); + $this->view->assign("parentOptions", $parentOptions); + $this->view->assign("allowEditName", $allowEditName); + $this->view->assign("row", $row); + return $this->view->fetch(); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + if (stripos($ids, ',') !== false) { + $this->error("文档不支持批量删除"); + } + $row = Service::getMarkdownData($ids, false, false); + if (!$row) { + $this->error(__('No Results were found')); + } + try { + unlink(Service::getDocsSourceDir() . $row['relative']); + Service::refreshDocsData(); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + /** + * 刷新 + */ + public function refresh() + { + Service::refreshDocsData(); + $this->success(); + } + + /** + * 导出 + */ + public function export() + { + try { + //重新生成一次文档 + Service::build(); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + $distDir = Service::getDocsDistDir(); + $zipFile = RUNTIME_PATH . 'addons' . DS . 'export-docs-' . date('YmdHis') . '.zip'; + + $rootPath = realpath($distDir); + + $zip = new ZipArchive(); + $zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE); + + $files = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($rootPath), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $name => $file) { + if (!$file->isDir()) { + $filePath = $file->getRealPath(); + $relativePath = substr($filePath, strlen($rootPath) + 1); + $relativePath = str_replace('\\', '/', $relativePath); + + $zip->addFile($filePath, $relativePath); + } + } + + $zip->close(); + Http::sendToBrowser($zipFile); + } + + /** + * 批量更新 + * @internal + */ + public function status($ids = "") + { + $ids = $ids ? $ids : $this->request->param("ids"); + if ($ids) { + if ($this->request->has('params')) { + parse_str($this->request->post("params"), $values); + if ($values && isset($values['isnew'])) { + $row = Service::getMarkdownData($ids, true, false); + $row = array_merge($row, $values); + try { + Service::setMarkdownData($ids, $row); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } else { + $this->error(__('You have no permission')); + } + } + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + /** + * 批量更新 + * @internal + */ + public function multi($ids = "") + { + return; + } + + /** + * 动态下拉选择类型 + * @internal + */ + public function selectpage_type() + { + $list = []; + $word = (array)$this->request->request("q_word/a"); + $field = $this->request->request('showField'); + $keyValue = $this->request->request('keyValue'); + $config = get_addon_config('docs'); + $typeList = []; + $langs = $config['lang']; + foreach ($this->docsdata as $type => $pages) { + $typeList[$type] = isset($langs[$type]) ? $langs[$type] : $type; + } + $typeArr = $typeList; + if (!$keyValue) { + if (array_filter($word)) { + foreach ($word as $k => $v) { + $list[] = ['id' => $v, $field => $v]; + } + } + foreach ($typeArr as $index => $item) { + $list[] = ['id' => $index, $field => $item]; + } + } else { + $list[] = ['id' => $keyValue, $field => $typeArr[$keyValue] ?? $keyValue]; + } + return json(['total' => count($list), 'list' => $list]); + } + + /** + * 获取父级options + * @internal + */ + public function get_parent_options() + { + $type = $this->request->request('type'); + $origintype = $this->request->request('origintype'); + $id = $parent = null; + if ($type == $origintype) { + $id = $this->request->request('id'); + $parent = $this->request->request('parent'); + } + $parentOptions = ''; + if ($type) { + $parentOptions .= $this->getParentList($type, $id, $parent); + } + $this->success("", '', ['options' => $parentOptions]); + } + + protected function getParentList($type, $id = null, $parent = null) + { + $parentList = []; + $parentOptions = ''; + if (!isset($this->docsdata[$type])) { + return $parentOptions; + } + foreach ($this->docsdata[$type] as $index => $item) { + $parentList[] = ['id' => $item['id'], 'pid' => $item['parent'], 'name' => $item['title']]; + } + if ($parentList) { + $tree = Tree::instance()->init($parentList); + $disableIds = $id ? $tree->getChildrenIds($id, true) : []; + $selectIds = $parent ? [$parent] : []; + $parentOptions = $tree->getTree('', '', $selectIds, $disableIds); + } + return $parentOptions; + } +} diff --git a/application/admin/controller/Epay.php b/application/admin/controller/Epay.php new file mode 100644 index 0000000..5fe0a5b --- /dev/null +++ b/application/admin/controller/Epay.php @@ -0,0 +1,39 @@ +request->post('certname', ''); + $certPathArr = [ + 'cert_client' => '/addons/epay/certs/apiclient_cert.pem', //微信支付api + 'cert_key' => '/addons/epay/certs/apiclient_key.pem', //微信支付api + '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', //支付宝公钥证书路径 + ]; + if (!isset($certPathArr[$certname])) { + $this->error("证书错误"); + } + $url = $certPathArr[$certname]; + $file = $this->request->file('file'); + if (!$file) { + $this->error("未上传文件"); + } + $file->move(dirname(ROOT_PATH . $url), basename(ROOT_PATH . $url), true); + $this->success(__('上传成功'), '', ['url' => $url]); + } +} diff --git a/application/admin/controller/Index.php b/application/admin/controller/Index.php new file mode 100644 index 0000000..862c224 --- /dev/null +++ b/application/admin/controller/Index.php @@ -0,0 +1,138 @@ +request->filter('trim,strip_tags,htmlspecialchars'); + } + + /** + * 后台首页 + */ + public function index() + { + $cookieArr = ['adminskin' => "/^skin\-([a-z\-]+)\$/i", 'multiplenav' => "/^(0|1)\$/", 'multipletab' => "/^(0|1)\$/", 'show_submenu' => "/^(0|1)\$/"]; + foreach ($cookieArr as $key => $regex) { + $cookieValue = $this->request->cookie($key); + if (!is_null($cookieValue) && preg_match($regex, $cookieValue)) { + config('fastadmin.' . $key, $cookieValue); + } + } + //左侧菜单 + list($menulist, $navlist, $fixedmenu, $referermenu) = $this->auth->getSidebar([ + 'dashboard' => 'hot', + 'addon' => ['new', 'red', 'badge'], + 'auth/rule' => __('Menu'), + 'general' => ['new', 'purple'], + ], $this->view->site['fixedpage']); + $action = $this->request->request('action'); + if ($this->request->isPost()) { + if ($action == 'refreshmenu') { + $this->success('', null, ['menulist' => $menulist, 'navlist' => $navlist]); + } + } + $this->assignconfig('cookie', ['prefix' => config('cookie.prefix')]); + $this->view->assign('menulist', $menulist); + $this->view->assign('navlist', $navlist); + $this->view->assign('fixedmenu', $fixedmenu); + $this->view->assign('referermenu', $referermenu); + $this->view->assign('title', __('Home')); + return $this->view->fetch(); + } + + /** + * 管理员登录 + */ + public function login() + { + $url = $this->request->get('url', 'index/index'); + if ($this->auth->isLogin()) { + $this->success(__("You've logged in, do not login again"), $url); + } + if ($this->request->isPost()) { + $username = $this->request->post('username'); + $password = $this->request->post('password'); + $keeplogin = $this->request->post('keeplogin'); + $token = $this->request->post('__token__'); + $rule = [ + 'username' => 'require|length:3,30', + 'password' => 'require|length:3,30', + '__token__' => 'require|token', + ]; + $data = [ + 'username' => $username, + 'password' => $password, + '__token__' => $token, + ]; + if (Config::get('fastadmin.login_captcha')) { + $rule['captcha'] = 'require|captcha'; + $data['captcha'] = $this->request->post('captcha'); + } + $validate = new Validate($rule, [], ['username' => __('Username'), 'password' => __('Password'), 'captcha' => __('Captcha')]); + $result = $validate->check($data); + if (!$result) { + $this->error($validate->getError(), $url, ['token' => $this->request->token()]); + } + AdminLog::setTitle(__('Login')); + $result = $this->auth->login($username, $password, $keeplogin ? 86400 : 0); + if ($result === true) { + Hook::listen("admin_login_after", $this->request); + $this->success(__('Login successful'), $url, ['url' => $url, 'id' => $this->auth->id, 'username' => $username, 'avatar' => $this->auth->avatar]); + } else { + $msg = $this->auth->getError(); + $msg = $msg ? $msg : __('Username or password is incorrect'); + $this->error($msg, $url, ['token' => $this->request->token()]); + } + } + + // 根据客户端的cookie,判断是否可以自动登录 + if ($this->auth->autologin()) { + Session::delete("referer"); + $this->redirect($url); + } + $background = Config::get('fastadmin.login_background'); + $background = $background ? (stripos($background, 'http') === 0 ? $background : config('site.cdnurl') . $background) : ''; + $this->view->assign('background', $background); + $this->view->assign('title', __('Login')); + Hook::listen("admin_login_init", $this->request); + return $this->view->fetch(); + } + + /** + * 退出登录 + */ + public function logout() + { + if ($this->request->isPost()) { + $this->auth->logout(); + Hook::listen("admin_logout_after", $this->request); + $this->success(__('Logout successful'), 'index/login'); + } + $html = "
    " . token() . "
    "; + $html .= ""; + + return $html; + } + +} diff --git a/application/admin/controller/Match.php b/application/admin/controller/Match.php new file mode 100644 index 0000000..32bb889 --- /dev/null +++ b/application/admin/controller/Match.php @@ -0,0 +1,28 @@ +model = model('Archives'); + } + + public function chechkUser() + { + + } +} diff --git a/application/admin/controller/Renzheng.php b/application/admin/controller/Renzheng.php new file mode 100644 index 0000000..aee82dd --- /dev/null +++ b/application/admin/controller/Renzheng.php @@ -0,0 +1,37 @@ +model = new \app\admin\model\Renzheng; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + + +} diff --git a/application/admin/controller/Renzheng/Php.php b/application/admin/controller/Renzheng/Php.php new file mode 100644 index 0000000..8f60921 --- /dev/null +++ b/application/admin/controller/Renzheng/Php.php @@ -0,0 +1,37 @@ +model = new \app\admin\model\Renzheng\Php; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + + +} diff --git a/application/admin/controller/Schedule b/application/admin/controller/Schedule new file mode 100644 index 0000000..e69de29 diff --git a/application/admin/controller/Schedule.php b/application/admin/controller/Schedule.php new file mode 100644 index 0000000..df95897 --- /dev/null +++ b/application/admin/controller/Schedule.php @@ -0,0 +1,215 @@ +scheduleService = new ScheduleService(); + } + + + public function getSchedule() + { + $param['match_id'] = $this->request->param("match_id"); + + $rule = [ + 'match_id' => 'require', + ]; + + $msg = [ + 'match_id.require' => '赛事ID不可为空', + ]; + + $validate = new Validate($rule, $msg); + $result = $validate->check($param); + + if (!$result) { + $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); + } + + $res = $this->scheduleService->getByWhere($param); + + if ($res) { + $resule['data'] = $res; + $resule['code'] = 1; + $resule['msg'] = "获取成功"; + } else { + $resule['data'] = []; + $resule['code'] = 1; + $resule['msg'] = "获取失败"; + } + + return json($resule); + } + + public function getOneSchedule() + { + $param['id'] = $this->request->param("id"); + + $rule = [ + 'id' => 'require', + ]; + + $msg = [ + 'id.require' => 'id不可为空', + ]; + + $validate = new Validate($rule, $msg); + $result = $validate->check($param); + + if (!$result) { + $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); + } + + $res = $this->scheduleService->getOne($param['id']); + + if ($res) { + $resule['data'] = $res; + $resule['code'] = 1; + $resule['msg'] = "获取成功"; + } else { + $resule['data'] = []; + $resule['code'] = 1; + $resule['msg'] = "获取失败"; + } + + return json($resule); + } + + public function delete() + { + $param['id'] = $this->request->param("id"); + $rule = [ + 'id' => 'require', + ]; + + $msg = [ + 'id.require' => 'id不可为空', + ]; + + $validate = new Validate($rule, $msg); + $result = $validate->check($param); + + if (!$result) { + $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); + } + $res = $this->scheduleService->delSchedule($param['id']); + + if ($res) { + $resule['data'] = []; + $resule['code'] = 1; + $resule['msg'] = "删除成功"; + } else { + $resule['data'] = []; + $resule['code'] = 1; + $resule['msg'] = "删除失败"; + } + + return json($resule); + } + + public function add() + { + $param['match_id'] = $this->request->param("match_id"); + $param['title'] = $this->request->param("title"); + $param['content'] = $this->request->param("content"); + $param['date'] = $this->request->param("date"); + $param['weigh'] = $this->request->param("weigh"); + + $rule = [ + 'title' => 'require', + 'match_id' => 'require', + 'content' => 'require', + 'date' => 'require', + ]; + + $msg = [ + 'title.require' => '标题不可为空', + 'match_id.require' => '赛程数据不可为空', + 'date.require' => '日期不可为空', + 'content.require' => '日程内容不可为空', + ]; + + $validate = new Validate($rule, $msg); + $result = $validate->check($param); + + if (!$result) { + $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); + } + + $res = $this->scheduleService->addSchedule($param); + + if ($res) { + $resule['data'] = []; + $resule['code'] = 1; + $resule['msg'] = "添加成功"; + } else { + $resule['data'] = []; + $resule['code'] = 1; + $resule['msg'] = "添加失败"; + } + + return json($resule); + } + + public function editSchedule() + { + $param['match_id'] = $this->request->param("match_id"); + $param['title'] = $this->request->param("title"); + $param['content'] = $this->request->param("content"); + $param['date'] = $this->request->param("date"); + $param['weigh'] = $this->request->param("weigh"); + $param['id'] = $this->request->param("id"); + + $rule = [ + 'title' => 'require', + 'match_id' => 'require', + 'content' => 'require', + 'date' => 'require', + 'id' => 'require', + ]; + + $msg = [ + 'title.require' => '标题不可为空', + 'match_id.require' => '赛程数据不可为空', + 'date.require' => '日期不可为空', + 'content.require' => '日程内容不可为空', + 'id.require' => '修改日程ID不可为空', + ]; + + $validate = new Validate($rule, $msg); + $result = $validate->check($param); + + if (!$result) { + $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); + } + + $res = $this->scheduleService->editSchedule($param); + + if ($res) { + $resule['data'] = []; + $resule['code'] = 1; + $resule['msg'] = "修改成功"; + } else { + $resule['data'] = []; + $resule['code'] = 1; + $resule['msg'] = "修改失败"; + } + + return json($resule); + } +} \ No newline at end of file diff --git a/application/admin/controller/Screen.php b/application/admin/controller/Screen.php new file mode 100644 index 0000000..fc10f62 --- /dev/null +++ b/application/admin/controller/Screen.php @@ -0,0 +1,88 @@ +screenService = new ScreenService(); + } + + public function addScreen() + { + $param['title'] = $this->request->param("title"); + $param['look_pwd'] = $this->request->param("password"); + $param['match_id'] = $this->request->param("match_id"); + $param['backound_img'] = $this->request->param("backound_img", ""); + $param['nopass'] = $this->request->param("nopass"); + $param['font_rgb'] = $this->request->param("font_rgb"); + $param['type'] = $this->request->param("type"); + $rule = [ + 'title' => 'require', + 'match_id' => 'require', + ]; + + $msg = [ + 'title.require' => '标题不可为空', + 'match_id.require' => '赛程数据不可为空', + ]; + + $validate = new Validate($rule, $msg); + $result = $validate->check($param); + + if (!$result) { + $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); + } + $res = $this->screenService->addScreen($param); + + if (!empty($res)) { + $row['code'] = 1; + $row['data'] = []; + $row['msg'] = "保存成功"; + } else { + $row['code'] = 0; + $row['data'] = []; + $row['msg'] = "保存失败"; + } + + return json($row); + } + + public function getScreen() + { + $param['match_id'] = $this->request->param("match_id"); + $param['type'] = $this->request->param("type"); + + $res = $this->screenService->getScreen($param); + + if (!empty($res)) { + $row['code'] = 1; + $row['data'] = $res; + $row['msg'] = "获取成功"; + } else { + $row['code'] = 0; + $row['data'] = []; + $row['msg'] = "获取失败"; + } + + return json($row); + } + +} \ No newline at end of file diff --git a/application/admin/controller/Third.php b/application/admin/controller/Third.php new file mode 100644 index 0000000..ac7dc77 --- /dev/null +++ b/application/admin/controller/Third.php @@ -0,0 +1,66 @@ +model = new \app\admin\model\Third; + } + + /** + * 查看 + */ + public function index() + { + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $total = $this->model + ->with(['user']) + ->where($where) + ->order($sort, $order) + ->count(); + + $list = $this->model + ->with(['user']) + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + foreach ($list as $index => $item) { + if ($item->user) { + $item->user->visible(['nickname']); + } + } + $list = collection($list)->toArray(); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/auth/Admin.php b/application/admin/controller/auth/Admin.php new file mode 100644 index 0000000..a7dc6da --- /dev/null +++ b/application/admin/controller/auth/Admin.php @@ -0,0 +1,297 @@ +model = model('Admin'); + + $this->childrenAdminIds = $this->auth->getChildrenAdminIds($this->auth->isSuperAdmin()); + $this->childrenGroupIds = $this->auth->getChildrenGroupIds($this->auth->isSuperAdmin()); + + $groupList = collection(AuthGroup::where('id', 'in', $this->childrenGroupIds)->select())->toArray(); + + Tree::instance()->init($groupList); + $groupdata = []; + if ($this->auth->isSuperAdmin()) { + $result = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0)); + foreach ($result as $k => $v) { + $groupdata[$v['id']] = $v['name']; + } + } else { + $result = []; + $groups = $this->auth->getGroups(); + foreach ($groups as $m => $n) { + $childlist = Tree::instance()->getTreeList(Tree::instance()->getTreeArray($n['id'])); + $temp = []; + foreach ($childlist as $k => $v) { + $temp[$v['id']] = $v['name']; + } + $result[__($n['name'])] = $temp; + } + $groupdata = $result; + } + + $this->view->assign('groupdata', $groupdata); + $this->assignconfig("admin", ['id' => $this->auth->id]); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + $childrenGroupIds = $this->childrenGroupIds; + $groupName = AuthGroup::where('id', 'in', $childrenGroupIds) + ->column('id,name'); + $authGroupList = AuthGroupAccess::where('group_id', 'in', $childrenGroupIds) + ->field('uid,group_id') + ->select(); + + $adminGroupName = []; + foreach ($authGroupList as $k => $v) { + if (isset($groupName[$v['group_id']])) { + $adminGroupName[$v['uid']][$v['group_id']] = $groupName[$v['group_id']]; + } + } + $groups = $this->auth->getGroups(); + foreach ($groups as $m => $n) { + $adminGroupName[$this->auth->id][$n['id']] = $n['name']; + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + ->where($where) + ->where('id', 'in', $this->childrenAdminIds) + ->field(['password', 'salt', 'token'], true) + ->order($sort, $order) + ->paginate($limit); + + foreach ($list as $k => &$v) { + $groups = isset($adminGroupName[$v['id']]) ? $adminGroupName[$v['id']] : []; + $v['groups'] = implode(',', array_keys($groups)); + $v['groups_text'] = implode(',', array_values($groups)); + } + unset($v); + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a"); + if ($params) { + Db::startTrans(); + try { + if (!Validate::is($params['password'], '\S{6,30}')) { + exception(__("Please input correct password")); + } + $params['salt'] = Random::alnum(); + $params['password'] = md5(md5($params['password']) . $params['salt']); + $params['avatar'] = '/assets/img/avatar.png'; //设置新管理员默认头像。 + $result = $this->model->validate('Admin.add')->save($params); + if ($result === false) { + exception($this->model->getError()); + } + $group = $this->request->post("group/a"); + + //过滤不允许的组别,避免越权 + $group = array_intersect($this->childrenGroupIds, $group); + if (!$group) { + exception(__('The parent group exceeds permission limit')); + } + + $dataset = []; + foreach ($group as $value) { + $dataset[] = ['uid' => $this->model->id, 'group_id' => $value]; + } + model('AuthGroupAccess')->saveAll($dataset); + Db::commit(); + } catch (\Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', '')); + } + return $this->view->fetch(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + $row = $this->model->get(['id' => $ids]); + if (!$row) { + $this->error(__('No Results were found')); + } + if (!in_array($row->id, $this->childrenAdminIds)) { + $this->error(__('You have no permission')); + } + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a"); + if ($params) { + Db::startTrans(); + try { + if ($params['password']) { + if (!Validate::is($params['password'], '\S{6,30}')) { + exception(__("Please input correct password")); + } + $params['salt'] = Random::alnum(); + $params['password'] = md5(md5($params['password']) . $params['salt']); + } else { + unset($params['password'], $params['salt']); + } + //这里需要针对username和email做唯一验证 + $adminValidate = \think\Loader::validate('Admin'); + $adminValidate->rule([ + 'username' => 'require|regex:\w{3,30}|unique:admin,username,' . $row->id, + 'email' => 'require|email|unique:admin,email,' . $row->id, + 'mobile' => 'regex:1[3-9]\d{9}|unique:admin,mobile,' . $row->id, + 'password' => 'regex:\S{32}', + ]); + $result = $row->validate('Admin.edit')->save($params); + if ($result === false) { + exception($row->getError()); + } + + // 先移除所有权限 + model('AuthGroupAccess')->where('uid', $row->id)->delete(); + + $group = $this->request->post("group/a"); + + // 过滤不允许的组别,避免越权 + $group = array_intersect($this->childrenGroupIds, $group); + if (!$group) { + exception(__('The parent group exceeds permission limit')); + } + + $dataset = []; + foreach ($group as $value) { + $dataset[] = ['uid' => $row->id, 'group_id' => $value]; + } + model('AuthGroupAccess')->saveAll($dataset); + Db::commit(); + } catch (\Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', '')); + } + $grouplist = $this->auth->getGroups($row['id']); + $groupids = []; + foreach ($grouplist as $k => $v) { + $groupids[] = $v['id']; + } + $this->view->assign("row", $row); + $this->view->assign("groupids", $groupids); + return $this->view->fetch(); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + $ids = array_intersect($this->childrenAdminIds, array_filter(explode(',', $ids))); + // 避免越权删除管理员 + $childrenGroupIds = $this->childrenGroupIds; + $adminList = $this->model->where('id', 'in', $ids)->where('id', 'in', function ($query) use ($childrenGroupIds) { + $query->name('auth_group_access')->where('group_id', 'in', $childrenGroupIds)->field('uid'); + })->select(); + if ($adminList) { + $deleteIds = []; + foreach ($adminList as $k => $v) { + $deleteIds[] = $v->id; + } + $deleteIds = array_values(array_diff($deleteIds, [$this->auth->id])); + if ($deleteIds) { + Db::startTrans(); + try { + $this->model->destroy($deleteIds); + model('AuthGroupAccess')->where('uid', 'in', $deleteIds)->delete(); + Db::commit(); + } catch (\Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + $this->success(); + } + $this->error(__('No rows were deleted')); + } + } + $this->error(__('You have no permission')); + } + + /** + * 批量更新 + * @internal + */ + public function multi($ids = "") + { + // 管理员禁止批量操作 + $this->error(); + } + + /** + * 下拉搜索 + */ + public function selectpage() + { + $this->dataLimit = 'auth'; + $this->dataLimitField = 'id'; + return parent::selectpage(); + } +} diff --git a/application/admin/controller/auth/Adminlog.php b/application/admin/controller/auth/Adminlog.php new file mode 100644 index 0000000..c4895c1 --- /dev/null +++ b/application/admin/controller/auth/Adminlog.php @@ -0,0 +1,133 @@ +model = model('AdminLog'); + + $this->childrenAdminIds = $this->auth->getChildrenAdminIds(true); + $this->childrenGroupIds = $this->auth->getChildrenGroupIds(true); + + $groupName = AuthGroup::where('id', 'in', $this->childrenGroupIds) + ->column('id,name'); + + $this->view->assign('groupdata', $groupName); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $list = $this->model + ->where($where) + ->where('admin_id', 'in', $this->childrenAdminIds) + ->order($sort, $order) + ->paginate($limit); + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 详情 + */ + public function detail($ids) + { + $row = $this->model->get(['id' => $ids]); + if (!$row) { + $this->error(__('No Results were found')); + } + if (!$row['admin_id'] || !in_array($row['admin_id'], $this->childrenAdminIds)) { + $this->error(__('You have no permission')); + } + $this->view->assign("row", $row->toArray()); + return $this->view->fetch(); + } + + /** + * 添加 + * @internal + */ + public function add() + { + $this->error(); + } + + /** + * 编辑 + * @internal + */ + public function edit($ids = null) + { + $this->error(); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + $adminList = $this->model->where('id', 'in', $ids)->where('admin_id', 'in', $this->childrenAdminIds)->select(); + if ($adminList) { + $deleteIds = []; + foreach ($adminList as $k => $v) { + $deleteIds[] = $v->id; + } + if ($deleteIds) { + $this->model->destroy($deleteIds); + $this->success(); + } + } + } + $this->error(); + } + + /** + * 批量更新 + * @internal + */ + public function multi($ids = "") + { + // 管理员禁止批量操作 + $this->error(); + } + + public function selectpage() + { + return parent::selectpage(); + } +} diff --git a/application/admin/controller/auth/Group.php b/application/admin/controller/auth/Group.php new file mode 100644 index 0000000..29ae01d --- /dev/null +++ b/application/admin/controller/auth/Group.php @@ -0,0 +1,317 @@ +model = model('AuthGroup'); + + $this->childrenGroupIds = $this->auth->getChildrenGroupIds(true); + + $groupList = collection(AuthGroup::where('id', 'in', $this->childrenGroupIds)->select())->toArray(); + + Tree::instance()->init($groupList); + $groupList = []; + if ($this->auth->isSuperAdmin()) { + $groupList = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0)); + } else { + $groups = $this->auth->getGroups(); + $groupIds = []; + foreach ($groups as $m => $n) { + if (in_array($n['id'], $groupIds) || in_array($n['pid'], $groupIds)) { + continue; + } + $groupList = array_merge($groupList, Tree::instance()->getTreeList(Tree::instance()->getTreeArray($n['pid']))); + foreach ($groupList as $index => $item) { + $groupIds[] = $item['id']; + } + } + } + $groupName = []; + foreach ($groupList as $k => $v) { + $groupName[$v['id']] = $v['name']; + } + + $this->grouplist = $groupList; + $this->groupdata = $groupName; + $this->assignconfig("admin", ['id' => $this->auth->id, 'group_ids' => $this->auth->getGroupIds()]); + + $this->view->assign('groupdata', $this->groupdata); + } + + /** + * 查看 + */ + public function index() + { + if ($this->request->isAjax()) { + $list = $this->grouplist; + $total = count($list); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a", [], 'strip_tags'); + $params['rules'] = explode(',', $params['rules']); + if (!in_array($params['pid'], $this->childrenGroupIds)) { + $this->error(__('The parent group exceeds permission limit')); + } + $parentmodel = model("AuthGroup")->get($params['pid']); + if (!$parentmodel) { + $this->error(__('The parent group can not found')); + } + // 父级别的规则节点 + $parentrules = explode(',', $parentmodel->rules); + // 当前组别的规则节点 + $currentrules = $this->auth->getRuleIds(); + $rules = $params['rules']; + // 如果父组不是超级管理员则需要过滤规则节点,不能超过父组别的权限 + $rules = in_array('*', $parentrules) ? $rules : array_intersect($parentrules, $rules); + // 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限 + $rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules); + $params['rules'] = implode(',', $rules); + if ($params) { + $this->model->create($params); + $this->success(); + } + $this->error(); + } + return $this->view->fetch(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + if (!in_array($ids, $this->childrenGroupIds)) { + $this->error(__('You have no permission')); + } + $row = $this->model->get(['id' => $ids]); + if (!$row) { + $this->error(__('No Results were found')); + } + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a", [], 'strip_tags'); + //父节点不能是非权限内节点 + if (!in_array($params['pid'], $this->childrenGroupIds)) { + $this->error(__('The parent group exceeds permission limit')); + } + // 父节点不能是它自身的子节点或自己本身 + if (in_array($params['pid'], Tree::instance()->getChildrenIds($row->id, true))) { + $this->error(__('The parent group can not be its own child or itself')); + } + $params['rules'] = explode(',', $params['rules']); + + $parentmodel = model("AuthGroup")->get($params['pid']); + if (!$parentmodel) { + $this->error(__('The parent group can not found')); + } + // 父级别的规则节点 + $parentrules = explode(',', $parentmodel->rules); + // 当前组别的规则节点 + $currentrules = $this->auth->getRuleIds(); + $rules = $params['rules']; + // 如果父组不是超级管理员则需要过滤规则节点,不能超过父组别的权限 + $rules = in_array('*', $parentrules) ? $rules : array_intersect($parentrules, $rules); + // 如果当前组别不是超级管理员则需要过滤规则节点,不能超当前组别的权限 + $rules = in_array('*', $currentrules) ? $rules : array_intersect($currentrules, $rules); + $params['rules'] = implode(',', $rules); + if ($params) { + Db::startTrans(); + try { + $row->save($params); + $children_auth_groups = model("AuthGroup")->all(['id' => ['in', implode(',', (Tree::instance()->getChildrenIds($row->id)))]]); + $childparams = []; + foreach ($children_auth_groups as $key => $children_auth_group) { + $childparams[$key]['id'] = $children_auth_group->id; + $childparams[$key]['rules'] = implode(',', array_intersect(explode(',', $children_auth_group->rules), $rules)); + } + model("AuthGroup")->saveAll($childparams); + Db::commit(); + $this->success(); + } catch (Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + } + $this->error(); + return; + } + $this->view->assign("row", $row); + return $this->view->fetch(); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + $ids = explode(',', $ids); + $grouplist = $this->auth->getGroups(); + $group_ids = array_map(function ($group) { + return $group['id']; + }, $grouplist); + // 移除掉当前管理员所在组别 + $ids = array_diff($ids, $group_ids); + + // 循环判断每一个组别是否可删除 + $grouplist = $this->model->where('id', 'in', $ids)->select(); + $groupaccessmodel = model('AuthGroupAccess'); + foreach ($grouplist as $k => $v) { + // 当前组别下有管理员 + $groupone = $groupaccessmodel->get(['group_id' => $v['id']]); + if ($groupone) { + $ids = array_diff($ids, [$v['id']]); + continue; + } + // 当前组别下有子组别 + $groupone = $this->model->get(['pid' => $v['id']]); + if ($groupone) { + $ids = array_diff($ids, [$v['id']]); + continue; + } + } + if (!$ids) { + $this->error(__('You can not delete group that contain child group and administrators')); + } + $count = $this->model->where('id', 'in', $ids)->delete(); + if ($count) { + $this->success(); + } + } + $this->error(); + } + + /** + * 批量更新 + * @internal + */ + public function multi($ids = "") + { + // 组别禁止批量操作 + $this->error(); + } + + /** + * 读取角色权限树 + * + * @internal + */ + public function roletree() + { + $this->loadlang('auth/group'); + + $model = model('AuthGroup'); + $id = $this->request->post("id"); + $pid = $this->request->post("pid"); + $parentGroupModel = $model->get($pid); + $currentGroupModel = null; + if ($id) { + $currentGroupModel = $model->get($id); + } + if (($pid || $parentGroupModel) && (!$id || $currentGroupModel)) { + $id = $id ? $id : null; + $ruleList = collection(model('AuthRule')->order('weigh', 'desc')->order('id', 'asc')->select())->toArray(); + //读取父类角色所有节点列表 + $parentRuleList = []; + if (in_array('*', explode(',', $parentGroupModel->rules))) { + $parentRuleList = $ruleList; + } else { + $parentRuleIds = explode(',', $parentGroupModel->rules); + foreach ($ruleList as $k => $v) { + if (in_array($v['id'], $parentRuleIds)) { + $parentRuleList[] = $v; + } + } + } + + $ruleTree = new Tree(); + $groupTree = new Tree(); + //当前所有正常规则列表 + $ruleTree->init($parentRuleList); + //角色组列表 + $groupTree->init(collection(model('AuthGroup')->where('id', 'in', $this->childrenGroupIds)->select())->toArray()); + + //读取当前角色下规则ID集合 + $adminRuleIds = $this->auth->getRuleIds(); + //是否是超级管理员 + $superadmin = $this->auth->isSuperAdmin(); + //当前拥有的规则ID集合 + $currentRuleIds = $id ? explode(',', $currentGroupModel->rules) : []; + + if (!$id || !in_array($pid, $this->childrenGroupIds) || !in_array($pid, $groupTree->getChildrenIds($id, true))) { + $parentRuleList = $ruleTree->getTreeList($ruleTree->getTreeArray(0), 'name'); + $hasChildrens = []; + foreach ($parentRuleList as $k => $v) { + if ($v['haschild']) { + $hasChildrens[] = $v['id']; + } + } + $parentRuleIds = array_map(function ($item) { + return $item['id']; + }, $parentRuleList); + $nodeList = []; + foreach ($parentRuleList as $k => $v) { + if (!$superadmin && !in_array($v['id'], $adminRuleIds)) { + continue; + } + if ($v['pid'] && !in_array($v['pid'], $parentRuleIds)) { + continue; + } + $state = array('selected' => in_array($v['id'], $currentRuleIds) && !in_array($v['id'], $hasChildrens)); + $nodeList[] = array('id' => $v['id'], 'parent' => $v['pid'] ? $v['pid'] : '#', 'text' => __($v['title']), 'type' => 'menu', 'state' => $state); + } + $this->success('', null, $nodeList); + } else { + $this->error(__('Can not change the parent to child')); + } + } else { + $this->error(__('Group not found')); + } + } +} diff --git a/application/admin/controller/auth/Rule.php b/application/admin/controller/auth/Rule.php new file mode 100644 index 0000000..8ae23cb --- /dev/null +++ b/application/admin/controller/auth/Rule.php @@ -0,0 +1,159 @@ +auth->isSuperAdmin()) { + $this->error(__('Access is allowed only to the super management group')); + } + $this->model = model('AuthRule'); + // 必须将结果集转换为数组 + $ruleList = \think\Db::name("auth_rule")->field('type,condition,remark,createtime,updatetime', true)->order('weigh DESC,id ASC')->select(); + foreach ($ruleList as $k => &$v) { + $v['title'] = __($v['title']); + } + unset($v); + Tree::instance()->init($ruleList); + $this->rulelist = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0), 'title'); + $ruledata = [0 => __('None')]; + foreach ($this->rulelist as $k => &$v) { + if (!$v['ismenu']) { + continue; + } + $ruledata[$v['id']] = $v['title']; + unset($v['spacer']); + } + unset($v); + $this->view->assign('ruledata', $ruledata); + $this->view->assign("menutypeList", $this->model->getMenutypeList()); + } + + /** + * 查看 + */ + public function index() + { + if ($this->request->isAjax()) { + $list = $this->rulelist; + $total = count($this->rulelist); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a", [], 'strip_tags'); + if ($params) { + if (!$params['ismenu'] && !$params['pid']) { + $this->error(__('The non-menu rule must have parent')); + } + $result = $this->model->validate()->save($params); + if ($result === false) { + $this->error($this->model->getError()); + } + Cache::rm('__menu__'); + $this->success(); + } + $this->error(); + } + return $this->view->fetch(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + $row = $this->model->get(['id' => $ids]); + if (!$row) { + $this->error(__('No Results were found')); + } + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a", [], 'strip_tags'); + if ($params) { + if (!$params['ismenu'] && !$params['pid']) { + $this->error(__('The non-menu rule must have parent')); + } + if ($params['pid'] == $row['id']) { + $this->error(__('Can not change the parent to self')); + } + if ($params['pid'] != $row['pid']) { + $childrenIds = Tree::instance()->init(collection(AuthRule::select())->toArray())->getChildrenIds($row['id']); + if (in_array($params['pid'], $childrenIds)) { + $this->error(__('Can not change the parent to child')); + } + } + //这里需要针对name做唯一验证 + $ruleValidate = \think\Loader::validate('AuthRule'); + $ruleValidate->rule([ + 'name' => 'require|unique:AuthRule,name,' . $row->id, + ]); + $result = $row->validate()->save($params); + if ($result === false) { + $this->error($row->getError()); + } + Cache::rm('__menu__'); + $this->success(); + } + $this->error(); + } + $this->view->assign("row", $row); + return $this->view->fetch(); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + $delIds = []; + foreach (explode(',', $ids) as $k => $v) { + $delIds = array_merge($delIds, Tree::instance()->getChildrenIds($v, true)); + } + $delIds = array_unique($delIds); + $count = $this->model->where('id', 'in', $delIds)->delete(); + if ($count) { + Cache::rm('__menu__'); + $this->success(); + } + } + $this->error(); + } +} diff --git a/application/admin/controller/club/Complain.php b/application/admin/controller/club/Complain.php new file mode 100644 index 0000000..1a38953 --- /dev/null +++ b/application/admin/controller/club/Complain.php @@ -0,0 +1,37 @@ +model = new \app\admin\model\club\Complain; + + } + + + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + + +} diff --git a/application/admin/controller/club/Invate.php b/application/admin/controller/club/Invate.php new file mode 100644 index 0000000..89e747d --- /dev/null +++ b/application/admin/controller/club/Invate.php @@ -0,0 +1,189 @@ +model = new \app\admin\model\club\Invate; + $this->view->assign("statusList", $this->model->getStatusList()); + } + //更新旧数据 + public function updatename(){ + $res = $this->model->where(['deletetime' =>null,'status'=>1,'name_short'=>null])->select(); + var_dump($res);exit; + // 启动事务 + Db::startTrans(); + try{ + foreach ($res as $val){ + $User = new User(); + $players = new Players(); + $club = new Club(); + $player_info = $players->where('id',$val['player_id'])->find(); + $user_info = $User->where('id',$player_info['member_id'])->find(); + $club_info = $club->where('id',$val['club_id'])->find(); + $this->model->where('id',$val['id'])->update([ + 'name_short' => $club_info['name_short'], + 'member_number' => $user_info['member_number'], + ]); + } + // 提交事务 + Db::commit(); + } catch (\Exception $e) { + // 回滚事务 + Db::rollback(); + var_dump($e);exit; + } + + // var_dump($res);exit; + } + + + public function index() + { + $user = new User(); + $players = new Players(); + //设置过滤方法 + $club = new Club(); + $this->request->filter(['strip_tags', 'trim']); + if (false === $this->request->isAjax()) { + return $this->view->fetch(); + } + //如果发送的来源是 Selectpage,则转发到 Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + [$where, $sort, $order, $offset, $limit] = $this->buildparams(); + $sort = 'a.status,a.id desc'; + // var_dump($order);exit; + $list = $this->model + ->alias('a') + // ->join('peewee_players b','a.player_id = b.id','RIGHT') + ->where($where) + ->where('a.status in (5,6,7,8,9)') + ->order($sort, $order) + + ->paginate($limit); + // var_dump($list->items());exit; + $i = 0; + foreach ($list->items() as &$val){ + $player_res = $players->where('id',$val['player_id'])->find(); + $club_info = $club->where('id',$val['club_id'])->find(); + // if(empty($player_res)){var_dump($val);exit;} + if(!empty($player_res)){ + $user_res = $user->where('id',$player_res['member_id'])->find(); + // if(empty($user_res)){var_dump($val);exit;} + //用户信息不存在 + // if(!empty($user_res)){ + $val['phone'] = $player_res['phone']; + $val['player_name'] = $player_res['real_name']; + $val['gender'] = $player_res['gender']; + $val['province'] = $player_res['province']; + $val['name_short'] = !empty($club_info['name_short']) ? $club_info['name_short'] : ''; + $val['member_number'] = $val['member_number']; + // } + } + } + $result = ['total' => $list->total(), 'rows' => $list->items()]; + return json($result); + } + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + 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) && !in_array($row[$this->dataLimitField], $adminIds)) { + $this->error(__('You have no permission')); + } + if (false === $this->request->isPost()) { + $this->view->assign('row', $row); + return $this->view->fetch(); + } + $params = $this->request->post('row/a'); + if (empty($params)) { + $this->error(__('Parameter %s can not be empty', '')); + } + $params = $this->preExcludeFields($params); + // var_dump($params);exit; + // if($params['status'] == 7){ + // $params['deletetime'] = time(); + // } + // elseif($params['check_status'] == 2){ + // $params['deletetime'] = time(); + // } + $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()->validate($validate); + } + $result = $row->allowField(true)->save($params); + Db::commit(); + } catch (ValidateException|PDOException|Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if (false === $result) { + $this->error(__('No rows were updated')); + } + + if($params['status'] == 6){ + $players = new Players(); + $club = new Club(); + $clubThirdOauth = new ClubThirdOauth(); + $Wxmb = new Wxmb(); + $player_info = $players->where('id',$row['player_id'])->find(); + $club_info = $club->where('id',$row['club_id'])->find(); + $player_oauth = $clubThirdOauth->where('user_id',$player_info['member_id'])->find(); + $club_oauth = $clubThirdOauth->where('user_id',$club_info['user_id'])->find(); + // var_dump($player_oauth);exit; + if(!empty($player_oauth)){ + $player_oauth = $player_oauth->toArray(); + $push_arr = array('title'=>$club_info['name_short'].'队伍与飞手'.$player_info['real_name'].'关联已通过','username'=> '系统','type'=>'系统通知'); + $Wxmb->leaguepush($push_arr,$player_oauth['openid']); + } + if(!empty($club_oauth)){ + // var_dump($club_oauth);exit; + $club_oauth = $club_oauth->toArray(); + $push_arr = array('title'=>$club_info['name_short'].'队伍与飞手'.$player_info['real_name'].'关联已通过','username'=> '系统','type'=>'系统通知'); + $Wxmb->leaguepush($push_arr,$club_oauth['openid']); + } + // $params['deletetime'] = time(); + } + $this->success(); + } + +} diff --git a/application/admin/controller/cms/Ajax.php b/application/admin/controller/cms/Ajax.php new file mode 100644 index 0000000..8e30951 --- /dev/null +++ b/application/admin/controller/cms/Ajax.php @@ -0,0 +1,300 @@ +request->request("keyValue"); + if (!$keyValue) { + $type = $this->request->request("type"); + $name = $this->request->request("name"); + if ($name) { + //$files[] = ['name' => $name . '.html']; + } + //设置过滤方法 + $this->request->filter(['strip_tags']); + $config = get_addon_config('cms'); + $themeDir = ADDON_PATH . 'cms' . DS . 'view' . DS . $config['theme'] . DS; + $dh = opendir($themeDir); + while (false !== ($filename = readdir($dh))) { + if ($filename == '.' || $filename == '..') { + continue; + } + if ($type) { + $rule = $type == 'channel' ? '(channel|list)' : $type; + if (!preg_match("/^{$rule}(.*)/i", $filename)) { + continue; + } + } + $files[] = ['name' => $filename]; + } + } else { + $files[] = ['name' => $keyValue]; + } + return $result = ['total' => count($files), 'list' => $files]; + } + + /** + * 检查内容是否包含违禁词 + * @throws \Exception + */ + public function check_content_islegal() + { + $config = get_addon_config('cms'); + $content = $this->request->post('content'); + if (!$content) { + $this->error(__('Please input your content')); + } + if ($config['audittype'] == 'local') { + // 敏感词过滤 + $handle = SensitiveHelper::init()->setTreeByFile(ADDON_PATH . 'cms/data/words.dic'); + //首先检测是否合法 + $arr = $handle->getBadWord($content); + if ($arr) { + $this->error(__('The content is not legal'), null, $arr); + } else { + $this->success(__('The content is legal')); + } + } else { + $client = new AipContentCensor($config['aip_appid'], $config['aip_apikey'], $config['aip_secretkey']); + $result = $client->textCensorUserDefined($content); + if (isset($result['conclusionType']) && $result['conclusionType'] > 1) { + $msg = []; + foreach ($result['data'] as $index => $datum) { + $msg[] = $datum['msg']; + } + $this->error(implode("
    ", $msg), null, []); + } else { + $this->success(__('The content is legal')); + } + } + } + + /** + * 获取关键字 + * @throws \Exception + */ + public function get_content_keywords() + { + $config = get_addon_config('cms'); + $title = $this->request->post('title'); + $tags = $this->request->post('tags', ''); + $content = $this->request->post('content'); + if (!$content) { + $this->error(__('Please input your content')); + } + $keywords = Service::getContentTags($title); + $keywords = in_array($title, $keywords) ? [] : $keywords; + $keywords = array_filter(array_merge([$tags], $keywords)); + $description = mb_substr(strip_tags($content), 0, 200); + $data = [ + "keywords" => implode(',', $keywords), + "description" => $description + ]; + $this->success("提取成功", null, $data); + } + + /** + * 获取标题拼音 + */ + public function get_title_pinyin() + { + $config = get_addon_config('cms'); + $title = $this->request->post("title"); + //分隔符 + $delimiter = $this->request->post("delimiter", ""); + $pinyin = new \Overtrue\Pinyin\Pinyin('Overtrue\Pinyin\MemoryFileDictLoader'); + if ($title) { + if ($config['autopinyin']) { + $result = $pinyin->permalink($title, $delimiter); + $this->success("", null, ['pinyin' => $result]); + } else { + $this->error(); + } + } else { + $this->error(__('Parameter %s can not be empty', 'name')); + } + } + + /** + * 获取表字段列表 + * @internal + */ + public function get_fields_list() + { + $table = $this->request->request('table'); + $fieldList = Service::getTableFields($table); + $this->success("", null, ['fieldList' => $fieldList]); + } + + /** + * 获取自定义字段列表HTML + * @internal + */ + public function get_fields_html() + { + $this->view->engine->layout(false); + $source = $this->request->post('source'); + $id = $this->request->post('id/d'); + if (in_array($source, ['channel', 'page', 'special'])) { + $values = \think\Db::name("cms_{$source}")->where('id', $id)->find(); + $values = $values ? $values : []; + + $fields = \addons\cms\library\Service::getCustomFields($source, 0, $values); + + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + $this->success('', null, ['html' => $this->view->fetch('cms/common/fields')]); + } else { + $this->error(__('Please select type')); + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + public function get_link_list() + { + if ($this->request->isAjax()) { + $filter = $this->request->request("filter", '', 'trim'); + $filter = (array)json_decode($filter, true); + $pageList = \app\admin\model\cms\Page::all(); + $specialList = \app\admin\model\cms\Special::all(); + $diyformList = \app\admin\model\cms\Diyform::all(); + $rows = []; + if (!isset($filter['type']) || $filter['type'] == 'page') { + foreach ($pageList as $index => $item) { + $rows[] = ['type' => 'page', 'url' => $item['url'], 'title' => $item['title'], 'source_id' => $item['id']]; + } + } + if (!isset($filter['type']) || $filter['type'] == 'special') { + foreach ($specialList as $index => $item) { + $rows[] = ['type' => 'special', 'url' => $item['url'], 'title' => $item['title'], 'source_id' => $item['id']]; + } + } + if (!isset($filter['type']) || $filter['type'] == 'diyform') { + foreach ($diyformList as $index => $item) { + $rows[] = ['type' => 'diyform', 'url' => $item['url'], 'title' => $item['name'] . ' - 列表页', 'source_id' => $item['id']]; + $rows[] = ['type' => 'diyform', 'url' => $item['post_url'], 'title' => $item['name'] . " - 投稿页", 'source_id' => $item['id']]; + } + } + foreach ($rows as $index => $row) { + if (isset($filter['url']) && stripos($row['url'], $filter['url']) === false) { + unset($rows[$index]); + continue; + } + if (isset($filter['title']) && stripos($row['title'], $filter['title']) === false) { + unset($rows[$index]); + continue; + } + } + return [ + 'rows' => array_values($rows), + 'total' => count($rows) + ]; + } + $typeList = [ + 'special' => '专题', + 'page' => '单页', + 'diyform' => '自定义表单', + ]; + $this->view->assign('typeList', $typeList); + return $this->view->fetch('cms/common/links'); + } + + public function config() + { + $name = $this->request->get('name'); + if ($name == 'sms') { + $config = config('addons.hooks'); + if (isset($config['sms_send']) && $config['sms_send']) { + $name = reset($config['sms_send']); + } else { + $this->error("请在插件管理中安装一款短信验证插件", ""); + } + } elseif ($name == 'oss') { + $config = config('addons.hooks'); + if (isset($config['upload_config_init']) && $config['upload_config_init']) { + $availableArr = array_intersect($config['upload_config_init'], ['alioss', 'bos', 'cos', 'upyun', 'ucloud', 'hwobs', 'qiniu']); + if ($availableArr) { + $name = reset($availableArr); + } + } + if (!$name || $name == 'oss') { + $this->error("请在插件管理中安装一款云存储插件", ""); + } + } else { + $info = get_addon_info($name); + $addonArr = [ + 'third' => '第三方登录', + 'signin' => '会员签到', + 'epay' => '微信支付宝整合', + ]; + if (!$info) { + $this->error('请检查对应插件' . (isset($addonArr[$name]) ? "《{$addonArr[$name]}》" : "") . '是否安装且启动', ""); + } + } + $this->redirect('addon/config?name=' . $name . '&dialog=1'); + } + + /** + * 检测元素是否可用 + * @internal + */ + public function check_config_available() + { + $name = $this->request->request('name'); + $value = $this->request->request('value'); + $name = substr($name, 4, -1); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if ($name == 'theme') { + if (!preg_match("/^([a-zA-Z0-9\-_]+)$/i", $value)) { + $this->error("只支持字母数字下划线"); + } + if (!is_dir(ADDON_PATH . 'cms' . DS . 'view' . DS . $value . DS)) { + $this->error("皮肤目录不存在"); + } + } elseif ($name == 'archivesratio') { + $valueArr = explode(':', $value); + if (!isset($valueArr[1])) { + $this->error('格式不正确'); + } + if (bcadd($valueArr[0], $valueArr[1], 2) != 1) { + $this->error('分成占比相加必须等于1'); + } + } elseif ($name == 'cachelifetime') { + if (!is_numeric($value) && !in_array($value, ['true', 'false'])) { + $this->error("格式不正确,只支持 数字/true"); + } + } + $this->success(); + } + +} diff --git a/application/admin/controller/cms/Archives.php b/application/admin/controller/cms/Archives.php new file mode 100644 index 0000000..f11c606 --- /dev/null +++ b/application/admin/controller/cms/Archives.php @@ -0,0 +1,993 @@ +model = new \app\admin\model\cms\Archives; + $this->MatchContestantModel = model('MatchContestant'); + $this->UserModel = model('User'); + $config = get_addon_config('cms'); + if ($config['archivesdatalimit'] != 'all') { + $this->dataLimit = $config['archivesdatalimit']; + } + + //复制/加入专题/修改标签均检测编辑权限 + if (in_array($this->request->action(), ['copy', 'special', 'tags', 'move', 'flag']) && !$this->auth->check('cms/archives/edit')) { + Hook::listen('admin_nopermission', $this); + $this->error(__('You have no permission'), ''); + } + + //是否超级管理员 + $this->isSuperAdmin = $this->auth->isSuperAdmin(); + $channelList = []; + $disabledIds = []; + $all = collection(Channel::order("weigh desc,id desc")->select())->toArray(); + + //允许的栏目 + $this->channelIds = $this->isSuperAdmin || !$config['channelallocate'] ? Channel::column('id') : ChannelAdmin::getAdminChanneIds(); + $parentChannelIds = Channel::where('id', 'in', $this->channelIds)->column('parent_id'); + $parentChannelIds = array_unique($parentChannelIds); + $parentChannelList = \think\Db::name('cms_channel')->where('id', 'in', $parentChannelIds)->where('parent_id', '<>', 0)->field('id,parent_id,name')->select(); + $tree = Tree::instance()->init($all, 'parent_id'); + foreach ($parentChannelList as $index => $channel) { + $parentChannelIds = array_merge($parentChannelIds, $tree->getParentsIds($channel['parent_id'], true)); + } + $this->channelIds = array_merge($parentChannelIds, $this->channelIds); + foreach ($all as $k => $v) { + $state = ['opened' => true]; + if ($v['type'] == 'link') { + $disabledIds[] = $v['id']; + } + if ($v['type'] == 'link') { + $state['checkbox_disabled'] = true; + } + if (!$this->isSuperAdmin) { + if (!in_array($v['id'], $parentChannelIds) && !in_array($v['id'], $this->channelIds)) { + unset($all[$k]); + continue; + } + } + $channelList[] = [ + 'id' => $v['id'], + 'parent' => $v['parent_id'] ? $v['parent_id'] : '#', + 'text' => __($v['name']), + 'type' => $v['type'], + 'state' => $state + ]; + } + $tree = Tree::instance()->init($all, 'parent_id'); + $channelOptions = $tree->getTree(0, "", '', $disabledIds); + $secondChannelOptions = $tree->getTree(0, "", '', $disabledIds); + $this->view->assign('channelOptions', $channelOptions); + $this->view->assign('secondChannelOptions', $secondChannelOptions); + $this->assignconfig('channelList', $channelList); + $this->assignconfig('spiderRecord', intval($config['spiderrecord'] ?? 0)); + + $this->assignconfig("flagList", $this->model->getFlagList()); + $this->view->assign("flagList", $this->model->getFlagList()); + $this->view->assign("statusList", $this->model->getStatusList()); + + $this->assignconfig('cms', ['archiveseditmode' => $config['archiveseditmode']]); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + $this->relationSearch = true; + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + if (!$this->auth->isSuperAdmin()) { + $this->model->where('channel_id', 'in', $this->channelIds); + } + $total = $this->model + ->with('Channel') + ->where($where) + ->order($sort, $order) + ->count(); + if (!$this->auth->isSuperAdmin()) { + $this->model->where('channel_id', 'in', $this->channelIds); + } + $list = $this->model + ->with(['Channel']) + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + + addtion($list, [ + [ + 'field' => 'channel_ids', + 'display' => 'channel_ids', + 'model' => Channel::class, + ], + ]); + \app\admin\model\cms\SpiderLog::render($list, 'archives'); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + + $modelList = \app\admin\model\cms\Modelx::all(); + $specialList = \app\admin\model\cms\Special::where('status', 'normal')->select(); + $this->view->assign('modelList', $modelList); + $this->view->assign('specialList', $specialList); + return $this->view->fetch(); + } + + /** + * 副表内容 + */ + public function content($model_id = null) + { + $model = \app\admin\model\cms\Modelx::get($model_id); + if (!$model) { + $this->error('未找到对应模型'); + } + $fieldsList = \app\admin\model\cms\Fields::where('source', 'model')->where('source_id', $model['id'])->where('type', '<>', 'text')->select(); + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + $fields = []; + foreach ($fieldsList as $index => $item) { + $fields[] = "addon." . $item['name']; + } + $filter = $this->request->request('filter'); + $op = $this->request->request('op'); + if ($filter && $op) { + $filterArr = json_decode($filter, true); + $opArr = json_decode($op, true); + foreach ($filterArr as $index => $item) { + if (in_array("addon." . $index, $fields)) { + $filterArr["addon." . $index] = $item; + $opArr["addon." . $index] = $opArr[$index]; + unset($filterArr[$index], $opArr[$index]); + } + } + $this->request->get(['filter' => json_encode($filterArr), 'op' => json_encode($opArr)]); + } + + $this->searchFields = "archives.id,archives.title"; + $this->relationSearch = true; + $table = $this->model->getTable(); + list($where, $sort, $order, $offset, $limit, $page, $alias) = $this->buildparams(); + $sort = 'archives.id'; + $isSuperAdmin = $this->isSuperAdmin; + $channelIds = $this->channelIds; + // var_dump(['0'=>36,'1'=>80]);exit; + $customWhere = function ($query) use ($isSuperAdmin, $channelIds, $model_id) { + if (!$isSuperAdmin) { + $query->where('archives.channel_id', 'in', $channelIds); + } + if ($model_id) { + $query->where('archives.model_id', $model_id); + if ($model_id == 2){ + $res = $query->where('archives.channel_id', 36)->whereOr('archives.channel_id', 80); + // var_dump($res);exit; + } + // if ($model_id == 6){ + + // $res = $query->where('archives.channel_id', 73); + // // var_dump($res);exit; + // } + } + }; + + + + $list = $this->model + ->alias($alias) + ->alias('archives') + ->join('cms_channel channel', 'channel.id=archives.channel_id', 'LEFT') + ->join($model['table'] . ' addon', 'addon.id=archives.id', 'LEFT') + ->field('archives.*,channel.name as channel_name,addon.id as aid' . ($fields ? ',' . implode(',', $fields) : '')) + ->where($customWhere) + ->whereNull('deletetime') + ->where($where) + ->order($sort, $order) + ->paginate($limit); + // var_dump($list);exit; + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + $fields = []; + foreach ($fieldsList as $index => $item) { + $fields[] = ['field' => $item['name'], 'title' => $item['title'], 'type' => $item['type'], 'content' => $item['content_list']]; + } + + $this->assignconfig('fields', $fields); + $this->view->assign('fieldsList', $fieldsList); + $this->view->assign('model', $model); + $this->assignconfig('model_id', $model_id); + $modelList = \app\admin\model\cms\Modelx::all(); + $this->view->assign('modelList', $modelList); + return $this->view->fetch(); + } + + /** + * 编辑 + * + * @param mixed $ids + * @return string + */ + 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->isSuperAdmin && !in_array($row['channel_id'], $this->channelIds)) { + $this->error(__('You have no permission')); + } + if ($this->request->isPost()) { + return parent::edit($ids); + } + $channel = Channel::get($row['channel_id']); + if (!$channel) { + $this->error(__('No specified channel found')); + } + $model = \app\admin\model\cms\Modelx::get($channel['model_id']); + if (!$model) { + $this->error(__('No specified model found')); + } + $addon = db($model['table'])->where('id', $row['id'])->find(); + if ($addon) { + $row->setData($addon); + } + + $disabledIds = []; + $all = collection(Channel::order("weigh desc,id desc")->select())->toArray(); + foreach ($all as $k => $v) { + if ($v['type'] == 'link' || $v['model_id'] != $channel['model_id']) { + $disabledIds[] = $v['id']; + } + } + $disabledIds = array_diff($disabledIds, [$row['channel_id']]); + $tree = Tree::instance()->init($all, 'parent_id'); + $channelOptions = $tree->getTree(0, "", $row['channel_id'], $disabledIds); + $secondChannelOptions = $tree->getTree(0, "", explode(',', $row['channel_ids']), $disabledIds); + $this->view->assign('channelOptions', $channelOptions); + $this->view->assign('secondChannelOptions', $secondChannelOptions); + $this->view->assign("row", $row); + return $this->view->fetch(); + } + + /** + * 删除 + * @param mixed $ids + */ + public function del($ids = "") + { + parent::del($ids); + } + + /** + * 销毁 + * @param string $ids + */ + public function destroy($ids = "") + { + \app\admin\model\cms\Archives::event('after_delete', function ($row) { + //删除副表 + $channel = Channel::get($row->channel_id); + if ($channel) { + $model = Modelx::get($channel['model_id']); + if ($model) { + db($model['table'])->where("id", $row['id'])->delete(); + } + } + }); + parent::destroy($ids); + } + + /** + * 还原 + * @param mixed $ids + */ + public function restore($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + if ($ids) { + $this->model->where($pk, 'in', $ids); + } + $config = get_addon_config('cms'); + $list = $this->model->onlyTrashed()->select(); + if ($list) { + $ids = []; + $refreshIds = []; + foreach ($list as $index => $item) { + if ($item['status'] == 'normal') { + User::score($config['score']['postarchives'], $item['user_id'], '发布文章'); + } + $ids[] = $item['id']; + $refreshIds = array_merge([$item['channel_id']], explode(',', $item['channel_ids'])); + $refreshIds = array_filter(array_unique($refreshIds)); + } + $this->model->where('id', 'in', $ids); + $this->model->restore('1=1'); + Channel::refreshItems($refreshIds); + $this->success(); + } + $this->error(__('No rows were updated')); + } + + /** + * 移动 + * @param string $ids + */ + public function move($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($ids) { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $channel_id = $this->request->post('channel_id'); + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $this->model->where($pk, 'in', $ids); + $channel = Channel::get($channel_id); + if ($channel && $channel['type'] === 'list') { + $channelNums = \app\admin\model\cms\Archives:: + with('channel') + ->where('archives.' . $pk, 'in', $ids) + ->where('channel_id', '<>', $channel['id']) + ->field('channel_id,COUNT(*) AS nums') + ->group('channel_id') + ->select(); + $result = $this->model + ->where('model_id', '=', $channel['model_id']) + ->where('channel_id', '<>', $channel['id']) + ->update(['channel_id' => $channel_id]); + if ($result) { + $this->success(); + } else { + $this->error(__('No rows were updated')); + } + } else { + $this->error(__('No rows were updated')); + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + } + + /** + * 复制选择行 + * @param string $ids + */ + public function copy($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($ids) { + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $archivesList = $this->model->where('id', 'in', $ids)->select(); + foreach ($archivesList as $index => $item) { + try { + $model = Modelx::get($item['model_id']); + $addon = \think\Db::name($model['table'])->find($item['id']); + $data = $item->toArray(); + $data = array_merge($data, $addon ?? []); + $data['title'] = $data['title'] . "_copy"; + $data['status'] = 'hidden'; + unset($data['id']); + \app\admin\model\cms\Archives::create($data, true); + } catch (\Exception $e) { + // + } + } + $this->success(); + $this->error(__('Parameter %s can not be empty', 'ids')); + } + } + + /** + * 加入专题 + * @param string $ids + */ + public function special($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($ids) { + $special_id = $this->request->post('special_id'); + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $special = \app\admin\model\cms\Special::get($special_id); + if ($special) { + $archivesList = $this->model->where($pk, 'in', $ids)->select(); + foreach ($archivesList as $index => $item) { + $special_ids = explode(',', $item['special_ids']); + if (!in_array($special['id'], $special_ids)) { + $special_ids[] = $special['id']; + $item->save(['special_ids' => implode(',', array_unique(array_filter($special_ids)))]); + } + } + $this->success(); + } else { + $this->error(__('No rows were updated')); + } + } + $this->error(__('Please select at least one row')); + } + + /** + * 加入标签 + * @param string $ids + */ + public function tags($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($ids) { + $tags = $this->request->post('tags'); + $newTagsArr = array_filter(explode(',', $tags)); + if ($newTagsArr) { + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $archivesList = $this->model->where($pk, 'in', $ids)->select(); + foreach ($archivesList as $index => $item) { + $tagsArr = explode(',', $item['tags']); + $tagsArr = array_merge($tagsArr, $newTagsArr); + $item->save(['tags' => implode(',', array_unique(array_filter($tagsArr)))]); + } + $this->success(); + } else { + $this->error(__('标签数据不能为空')); + } + } + $this->error(__('Please select at least one row')); + } + + /** + * 修改标志 + * @param string $ids + */ + public function flag($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($ids) { + $type = $this->request->post('type'); + $flag = $this->request->post('flag'); + $changeFlagArr = array_filter(explode(',', $flag)); + if ($changeFlagArr) { + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $archivesList = $this->model->where($pk, 'in', $ids)->select(); + foreach ($archivesList as $index => $item) { + $flagArr = explode(',', $item['flag']); + if ($type == 'add') { + $flagArr = array_merge($flagArr, $changeFlagArr); + } else { + $flagArr = array_diff($flagArr, $changeFlagArr); + } + $item->save(['flag' => implode(',', array_unique(array_filter($flagArr)))]); + } + $this->success(); + } else { + $this->error(__('标志数据不能为空')); + } + } + $this->error(__('Please select at least one row')); + } + + /** + * 获取栏目列表 + * @internal + */ + public function get_fields_html() + { + $this->view->engine->layout(false); + $channel_id = $this->request->post('channel_id'); + $archives_id = $this->request->post('archives_id'); + $channel = Channel::get($channel_id, 'model'); + if ($channel) { + $model_id = $channel['model_id']; + $values = []; + if ($archives_id) { + $values = db($channel['model']['table'])->where('id', $archives_id)->find(); + + //优先从栏目获取模型ID,再从文档获取 + $archives = \app\admin\model\cms\Archives::get($archives_id); + $model_id = $archives ? $archives['model_id'] : $model_id; + } + + $fields = \addons\cms\library\Service::getCustomFields('model', $model_id, $values); + + $model = Modelx::get($model_id); + + $setting = $model['setting']; + $publishfields = isset($setting['publishfields']) ? $setting['publishfields'] : []; + $titlelist = isset($setting['titlelist']) ? $setting['titlelist'] : []; + + $this->view->assign('channel', $channel); + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + $this->success('', null, ['html' => $this->view->fetch('cms/common/fields'), 'publishfields' => $publishfields, 'titlelist' => $titlelist]); + } else { + $this->error(__('Please select channel')); + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + /** + * 检测元素是否可用 + * @internal + */ + public function check_element_available() + { + $id = $this->request->request('id'); + $name = $this->request->request('name'); + $value = $this->request->request('value'); + $name = substr($name, 4, -1); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if ($id) { + $this->model->where('id', '<>', $id); + } + $exist = $this->model->where($name, $value)->find(); + if ($exist) { + $this->error(__('The data already exist')); + } else { + $this->success(); + } + } + + /** + * 搜索建议 + * @internal + */ + public function suggestion() + { + $config = get_addon_config('cms'); + $q = trim($this->request->request("q")); + $id = trim($this->request->request("id/d")); + $list = []; + if ($config['searchtype'] == 'xunsearch') { + $result = FulltextSearch::search($q, 1, 10); + } else { + $result = $this->model->where("title|keywords|description", "like", "%{$q}%")->where('id', '<>', $id)->limit(10)->order("id", "desc")->select(); + foreach ($result as $index => $item) { + $item['image'] = $item['image'] ? $item['image'] : '/assets/addons/cms/img/noimage.png'; + $list[] = ['id' => $item['id'], 'url' => $item['fullurl'], 'image' => cdnurl($item['image']), 'title' => $item['title'], 'create_date' => datetime($item['createtime']), 'status' => $item['status'], 'status_text' => $item['status_text'], 'deletetime' => $item['deletetime']]; + } + } + return json($list); + } + + /** + * Created by PhpStorm. + * Author:Soar + * Time:2023/7/12 11:54 + * @return string + * @throws \think\Exception + * @DESC 赛事页面飞手列表 + */ + public function league_allplayer() + { + return $this->view->fetch(); + } + public function league_wholeplayer() + { + return $this->view->fetch(); + } + public function leaguecontestant() + { + return $this->view->fetch(); + } + public function contestant() + { + return $this->view->fetch(); + } + + public function club_match_list() + { + return $this->view->fetch(); + } + + public function uploadfile() + { + return $this->view->fetch(); + } + + public function test() + { + return $this->view->fetch(); + } + + public function course() + { + return $this->view->fetch(); + } + + public function visualization() + { + return $this->view->fetch(); + } + + public function league(){ + return $this->view->fetch(); + } + + public function league_course(){ + return $this->view->fetch(); + } + + public function getleagueList() + { + $ClubMatchApply = new ClubMatchApply(); + $club = new Club(); + // $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + // 拦截部分信息修改 + if (!empty($this->request->get("op", '', 'trim'))) { + $op = json_decode($this->request->get("op", '', 'trim'), true); + $filter = json_decode($this->request->get("filter", '', 'trim'), true); + $matchId = trim($this->request->request("ids/d")); + // var_dump($op);exit; + if (!empty($filter['club.name'])) { + $club_list = $this->MatchContestantModel + // ->where($where) + ->field('club_id') + ->where('match_id', 'eq', $matchId) + ->group('club_id') + // ->fetchSql(true) + ->select(); + foreach ($club_list as $v){ + $new_arr[] = $v['club_id']; + } + $res = $club->field('id')->where('id','in',$new_arr)->where('name','like','%'.$filter['club.name'].'%')->select(); + // var_dump($res);exit; + foreach ($res as $v){ + $new_arrs[] = $v['id']; + } + unset($op['club.name']); + unset($filter['club.name']); + $filter['club_id'] = empty($new_arrs) ? 1: $new_arrs; + $op['club_id'] = "in"; + $this->request->get(["op" => json_encode($op)])["op"] = json_encode($op); + $this->request->get(["filter" => json_encode($filter)]); + + } + } + + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + // $res = $this->buildparams(); + // var_dump($res);exit; + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $matchId = trim($this->request->request("ids/d")); + + // $list = $ClubMatchApply->where($where) + // ->where('match_id', 'eq', $matchId) + // ->order($sort, $order) + // ->paginate($limit); + // var_dump($where);exit; + $sort = 'status,id desc'; + $club_list = $this->MatchContestantModel + ->with('club') + // ->alias('a') + // ->join('club club','a.club_id = club.id') + ->where($where) + ->where('match_id', 'eq', $matchId) + ->order($sort, $order) + ->group('club_id') + // ->fetchSql(true) + ->paginate($limit); + // var_dump($club_list);exit; + // var_dump($list->toArray()['data']);exit; + // $lists = $list->toArray()['data']; + foreach ($club_list as &$val){ + + + $val->club = $club->where('id',$val['club_id'])->find(); + // if(!empty($res)) $res = $res->toArray(); + // $val['clubs'] = $res; + } + // $a = $club_list->items(); + // foreach ($a as &$val){ + // $val = $val->toArray(); + // } + // var_dump($a);exit; + $result = array("total" => $club_list->total(), "rows" => $club_list->items()); + + return json($result); + } + + } + + public function getmatch_wholeList() + { + $ClubMatchApply = new ClubMatchApply(); + $club = new Club(); + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + // 拦截部分信息修改 + if (!empty($this->request->get("op", '', 'trim'))) { + $op = json_decode($this->request->get("op", '', 'trim'), true); + $filter = json_decode($this->request->get("filter", '', 'trim'), true); + // var_dump($filter);exit; + if (!empty($filter['user.member_number'])) { + // 获取用户信息 + $players = $this->UserModel->where('member_number', $filter['user.member_number'])->find(); + // var_dump($players);exit; + if (!empty($players)) { + unset($op['user.member_number']); + unset($filter['user.member_number']); + $filter['players.member_id'] = $players->id; + $op['players.member_id'] = "LIKE"; + $this->request->get(["op" => json_encode($op)])["op"] = json_encode($op); + $this->request->get(["filter" => json_encode($filter)]); + + } + } + } + + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $ids = trim($this->request->request("ids/d")); + // var_dump($ids);exit; + $matchId = $this->MatchContestantModel + ->field('match_id')->where('match_id', 'eq', $ids)->where('club_status',null)->find(); + // var_dump($matchId);exit; + // $list = $ClubMatchApply->where($where) + // ->where('match_id', 'eq', $matchId) + // ->order($sort, $order) + // ->paginate($limit); + $list = $this->MatchContestantModel + ->with('players') + ->where($where) + ->where('match_id', 'eq', $matchId['match_id']) + ->where('club_status',null) + ->order($sort, $order) + // ->distinct(true) + ->paginate($limit); + // var_dump($list->toArray()['data']);exit; + // $lists = $list->toArray()['data']; + foreach ($list as &$val){ + + if(empty($val->players->member_id)){ + + continue; + } + if(strpos($val->players->player_pic, 'aliyuncs') === false){ + // $val->players->player_pic = 'https://www.fpvone.cn'.$val->players->player_pic; + } + $val->user = $this->UserModel->find($val->players->member_id); + $val->club = $club->where('id',$val['club_id'])->find(); + } + $result = array("total" => $list->total(), "rows" => $list->items()); + // var_dump($result);exit; + return json($result); + } + + } + + public function getmatch_idsList() + { + $ClubMatchApply = new ClubMatchApply(); + $club = new Club(); + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + // 拦截部分信息修改 + if (!empty($this->request->get("op", '', 'trim'))) { + $op = json_decode($this->request->get("op", '', 'trim'), true); + $filter = json_decode($this->request->get("filter", '', 'trim'), true); + // var_dump($filter);exit; + if (!empty($filter['user.member_number'])) { + // 获取用户信息 + $players = $this->UserModel->where('member_number', $filter['user.member_number'])->find(); + // var_dump($players);exit; + if (!empty($players)) { + unset($op['user.member_number']); + unset($filter['user.member_number']); + $filter['players.member_id'] = $players->id; + $op['players.member_id'] = "LIKE"; + $this->request->get(["op" => json_encode($op)])["op"] = json_encode($op); + $this->request->get(["filter" => json_encode($filter)]); + + } + } + } + + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $ids = trim($this->request->request("ids/d")); + // var_dump($ids);exit; + $matchId = $this->MatchContestantModel + ->field('match_id,club_id')->where('id', 'eq', $ids)->find(); + // var_dump($matchId['club_id']);exit; + // $list = $ClubMatchApply->where($where) + // ->where('match_id', 'eq', $matchId) + // ->order($sort, $order) + // ->paginate($limit); + // var_dump($where);exit; + $list = $this->MatchContestantModel + ->with('player') + ->where($where) + ->where('match_id', 'eq', $matchId['match_id']) + ->where('club_id','eq',$matchId['club_id']) + ->where('club_status',null) + ->order($sort, $order) + // ->distinct(true) + ->paginate($limit); + // var_dump($list->toArray()['data']);exit; + // $lists = $list->toArray()['data']; + foreach ($list as &$val){ + + if(empty($val->players->member_id)){ + + continue; + } + $val->user = $this->UserModel->find($val->players->member_id); + $val->club = $club->where('id',$val['club_id'])->find(); + } + $result = array("total" => $list->total(), "rows" => $list->items()); + // var_dump($result);exit; + return json($result); + } + + } + + public function getplayersList() + { + $ClubMatchApply = new ClubMatchApply(); + $club = new Club(); + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + // 拦截部分信息修改 + if (!empty($this->request->get("op", '', 'trim'))) { + $op = json_decode($this->request->get("op", '', 'trim'), true); + $filter = json_decode($this->request->get("filter", '', 'trim'), true); + // var_dump($filter);exit; + if (!empty($filter['user.member_number'])) { + // 获取用户信息 + $players = $this->UserModel->where('member_number', $filter['user.member_number'])->find(); + // var_dump($players);exit; + if (!empty($players)) { + unset($op['user.member_number']); + unset($filter['user.member_number']); + $filter['players.member_id'] = $players->id; + $op['players.member_id'] = "LIKE"; + $this->request->get(["op" => json_encode($op)])["op"] = json_encode($op); + $this->request->get(["filter" => json_encode($filter)]); + + } + } + } + + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + // var_dump($where);exit; + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $matchId = trim($this->request->request("ids/d")); + // var_dump($where);exit; + // $list = $ClubMatchApply->where($where) + // ->where('match_id', 'eq', $matchId) + // ->order($sort, $order) + // ->paginate($limit); + $list = $this->MatchContestantModel + ->with('players') + ->where($where) + ->where('match_id', 'eq', $matchId) + ->order($sort, $order) + // ->distinct(true) + ->paginate($limit); + // var_dump($list->toArray()['data']);exit; + // $lists = $list->toArray()['data']; + foreach ($list as &$val){ + + if(empty($val->players->member_id)){ + + continue; + } + // var_dump($val['club_id']);exit; + // $club->where('id',$val->club_id); + $val->user = $this->UserModel->find($val->players->member_id); + + $val->club = $club->where('id',$val['club_id'])->find(); + // if(!empty($res)) $res = $res->toArray(); + // $val['clubs'] = $res; + } + $result = array("total" => $list->total(), "rows" => $list->items()); + // var_dump($result);exit; + return json($result); + } + + } + +} diff --git a/application/admin/controller/cms/Autolink.php b/application/admin/controller/cms/Autolink.php new file mode 100644 index 0000000..d0eb05b --- /dev/null +++ b/application/admin/controller/cms/Autolink.php @@ -0,0 +1,35 @@ +model = new \app\admin\model\cms\Autolink; + $this->view->assign("targetList", $this->model->getTargetList()); + $this->view->assign("statusList", $this->model->getStatusList()); + } + + public function import() + { + parent::import(); + } + +} diff --git a/application/admin/controller/cms/Block.php b/application/admin/controller/cms/Block.php new file mode 100644 index 0000000..e737fe6 --- /dev/null +++ b/application/admin/controller/cms/Block.php @@ -0,0 +1,104 @@ +model = new \app\admin\model\cms\Block; + $this->view->assign("statusList", $this->model->getStatusList()); + $this->view->assign("nameList", $this->model->getNameList()); + } + + public function index() + { + $typeArr = \app\admin\model\cms\Block::distinct('type')->column('type'); + $this->view->assign('typeList', $typeArr); + $this->assignconfig('typeList', $typeArr); + return parent::index(); + } + + public function selectpage_type() + { + $list = []; + $word = (array)$this->request->request("q_word/a"); + $field = $this->request->request('showField'); + $keyValue = $this->request->request('keyValue'); + if (!$keyValue) { + if (array_filter($word)) { + foreach ($word as $k => $v) { + $list[] = ['id' => $v, $field => $v]; + } + } + $typeArr = \app\admin\model\cms\Block::column('type'); + $typeArr = array_unique($typeArr); + foreach ($typeArr as $index => $item) { + $list[] = ['id' => $item, $field => $item]; + } + } else { + $list[] = ['id' => $keyValue, $field => $keyValue]; + } + return json(['total' => count($list), 'list' => $list]); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $row = $this->request->post("row/a", []); + if (isset($row['parsetpl']) && $row['parsetpl']) { + $this->token(); + } + } + $values = []; + $fields = \addons\cms\library\Service::getCustomFields('block', 0, $values); + + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + return parent::add(); + } + + public function edit($ids = null) + { + if ($this->request->isPost()) { + $row = $this->request->post("row/a", []); + if (isset($row['parsetpl']) && $row['parsetpl']) { + $this->token(); + } + } + $values = \app\admin\model\cms\Block::get($ids); + if (!$values) { + $this->error(__('No Results were found')); + } + $values = $values->toArray(); + $fields = \addons\cms\library\Service::getCustomFields('block', 0, $values); + + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + return parent::edit($ids); + } + + public function import() + { + return parent::import(); + } +} diff --git a/application/admin/controller/cms/Builder.php b/application/admin/controller/cms/Builder.php new file mode 100644 index 0000000..fc13b75 --- /dev/null +++ b/application/admin/controller/cms/Builder.php @@ -0,0 +1,111 @@ +init(collection(Channel::where('status', 'normal')->order('weigh desc,id desc')->select())->toArray(), 'parent_id'); + $channelList = $tree->getTreeList($tree->getTreeArray(0), 'name'); + $modelList = \app\admin\model\cms\Modelx::order('id asc')->select(); + + $prefix = \think\Config::get('database.prefix'); + $fieldList = Service::getTableFields("{$prefix}cms_archives"); + $channelFieldList = Service::getTableFields("{$prefix}cms_channel"); + $userFieldList = Service::getTableFields("{$prefix}user"); + $specialFieldList = Service::getTableFields("{$prefix}cms_special"); + $pageFieldList = Service::getTableFields("{$prefix}cms_page"); + $pageTypeList = \app\admin\model\cms\Page::distinct('type')->column("type"); + $blockTypeList = \app\admin\model\cms\Block::distinct('type')->column("type"); + $blockNameList = \app\admin\model\cms\Block::distinct('name')->column("name"); + + $blockFieldList = Service::getTableFields("{$prefix}cms_block"); + $diyformList = \app\admin\model\cms\Diyform::all(); + $diyformFieldList = []; + foreach ($diyformList as $index => $item) { + $diyformFieldList[$item['id']] = Service::getTableFields($prefix . $item['table']); + } + + $this->view->assign("configList", get_addon_fullconfig("cms")); + $this->view->assign("fieldList", $fieldList); + $this->view->assign("channelFieldList", $channelFieldList); + $this->view->assign("pageFieldList", $pageFieldList); + $this->view->assign("pageTypeList", $pageTypeList); + $this->view->assign("specialFieldList", $specialFieldList); + $this->view->assign("blockFieldList", $blockFieldList); + $this->view->assign("blockTypeList", $blockTypeList); + $this->view->assign("blockNameList", $blockNameList); + $this->view->assign("userFieldList", $userFieldList); + $this->view->assign("diyformList", $diyformList); + $this->view->assign("diyformFieldList", $diyformFieldList); + $this->view->assign("channelList", $channelList); + $this->view->assign("modelList", $modelList); + return $this->view->fetch(); + } + + /** + * 解析模板标签 + * @return string + */ + public function parse() + { + $this->view->engine->layout(false); + $tag = $this->request->post('tag'); + if (!config('app_debug')) { + $this->error("只在开发模式下才可渲染"); + } + $html = ''; + try { + $html = $this->view->display($tag); + } catch (\Exception $e) { + $this->error("模板标签解析错误:" . $e->getMessage()); + } + $this->success("", null, $html); + return $this->view->fetch(); + } + + /** + * 获取自定义字段列表HTML + * @internal + */ + public function get_model_fields() + { + $this->view->engine->layout(false); + $id = $this->request->post('id/d'); + $model = Modelx::get($id); + if ($model) { + $fields = \app\admin\model\cms\Fields::where('source', 'model')->where('source_id', $model['id'])->column("id,name,title"); + $this->success('', null, ['fields' => array_values($fields)]); + } else { + $this->error(__('Please select model')); + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + +} diff --git a/application/admin/controller/cms/Channel.php b/application/admin/controller/cms/Channel.php new file mode 100644 index 0000000..f30f9d8 --- /dev/null +++ b/application/admin/controller/cms/Channel.php @@ -0,0 +1,316 @@ +model = new \app\admin\model\cms\Channel; + + $this->tree = Tree::instance(); + $this->tree->init(collection($this->model->order('weigh desc,id desc')->select())->toArray(), 'parent_id'); + $this->channelList = $this->tree->getTreeList($this->tree->getTreeArray(0), 'name'); + $this->modelList = \app\admin\model\cms\Modelx::order('id asc')->select(); + + $config = get_addon_config('cms'); + $this->assignconfig('spiderRecord', intval($config['spiderrecord']?? 0)); + + $this->view->assign("modelList", $this->modelList); + $this->view->assign("channelList", $this->channelList); + $this->view->assign("typeList", ChannelModel::getTypeList()); + $this->assignconfig("flagList", $this->model->getFlagList()); + $this->view->assign("flagList", $this->model->getFlagList()); + $this->view->assign("statusList", ChannelModel::getStatusList()); + $this->view->assign("listtypeList", ChannelModel::getListtypeList()); + $this->view->assign("vipList", get_addon_info('vip') ? Service::getVipList() : []); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + $search = $this->request->request("search"); + $model_id = $this->request->request("model_id"); + //构造父类select列表选项数据 + $list = []; + if ($search) { + foreach ($this->channelList as $k => $v) { + if (stripos($v['name'], $search) !== false || stripos($v['nickname'], $search) !== false) { + $list[] = $v; + } + } + } else { + $list = $this->channelList; + } + foreach ($list as $index => $item) { + if ($model_id && $model_id != $item['model_id']) { + unset($list[$index]); + } + } + $list = array_values($list); + $modelNameArr = []; + foreach ($this->modelList as $k => $v) { + $modelNameArr[$v['id']] = $v['name']; + } + foreach ($list as $k => &$v) { + $v['pid'] = $v['parent_id']; + $v['model_name'] = $v['model_id'] && isset($modelNameArr[$v['model_id']]) ? $modelNameArr[$v['model_id']] : __('None'); + } + $total = count($list); + + \app\admin\model\cms\SpiderLog::render($list, 'channel'); + + $result = array("total" => $total, "rows" => $list); + + 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; + } + try { + //是否采用模型验证 + if ($this->modelValidate) { + $name = basename(str_replace('\\', '/', get_class($this->model))); + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : true) : $this->modelValidate; + $this->model->validate($validate); + } + $nameArr = array_filter(explode("\n", str_replace("\r\n", "\n", $params['name']))); + if (count($nameArr) > 1) { + foreach ($nameArr as $index => $item) { + $itemArr = array_filter(explode('|', $item)); + $params['name'] = $itemArr[0]; + $params['diyname'] = isset($itemArr[1]) ? $itemArr[1] : ''; + $result = $this->model->allowField(true)->isUpdate(false)->data($params)->save(); + } + } else { + $result = $this->model->allowField(true)->save($params); + } + if ($result !== false) { + $this->success(); + } else { + $this->error($this->model->getError()); + } + } catch (\think\exception\PDOException $e) { + $this->error($e->getMessage()); + } catch (\think\Exception $e) { + $this->error($e->getMessage()); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + + $values = []; + $fields = \addons\cms\library\Service::getCustomFields('channel', 0, $values); + + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + return $this->view->fetch(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + $channel = \app\admin\model\cms\Channel::get($ids); + if (!$channel) { + $this->error(__('No Results were found')); + } + $linkdata = $channel['linkdata']; + $channel = $channel->toArray(); + $fields = \addons\cms\library\Service::getCustomFields('channel', 0, $channel); + + $childrenIds = $this->tree->getChildrenIds($channel['id'], true); + $hasArchives = \app\admin\model\cms\Archives::withTrashed()->where('channel_id', $channel['id'])->whereOr('FIND_IN_SET(:id, `channel_ids`)', ['id' => $channel['id']])->count(); + $this->view->assign('hasArchives', $hasArchives); + $this->view->assign('fields', $fields); + $this->view->assign('values', $channel); + $this->view->assign('childrenIds', $childrenIds); + $this->assignconfig('linkdata', $linkdata); + return parent::edit($ids); + } + + /** + * 栏目授权 + */ + public function admin() + { + $act = $this->request->param('act'); + $ids = $this->request->param('ids'); + if ($act && $ids) { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($act == 'remove') { + ChannelAdmin::where('admin_id', $ids)->delete(); + $this->success('删除成功!'); + } elseif ($act == 'authorization') { + $selected = ChannelAdmin::getAdminChanneIds($ids); + $all = collection(ChannelModel::order("weigh desc,id desc")->select())->toArray(); + foreach ($all as $k => $v) { + $state = ['opened' => true]; + if ($v['type'] == 'link') { + $disabledIds[] = $v['id']; + } + if ($v['type'] == 'link') { + $state['checkbox_disabled'] = true; + } + $state['selected'] = in_array($v['id'], $selected); + $channelList[] = [ + 'id' => $v['id'], + 'parent' => $v['parent_id'] ? $v['parent_id'] : '#', + 'text' => __($v['name']), + 'type' => $v['type'], + 'state' => $state + ]; + } + $this->success('成功', '', $channelList); + } elseif ($act == 'save') { + \think\Db::startTrans(); + try { + ChannelAdmin::where('admin_id', $ids)->delete(); + $channelIds = explode(",", $this->request->post("ids")); + if ($channelIds) { + $listChannelIds = ChannelModel::where('type', 'list')->column('id'); + $channelIds = array_intersect($channelIds, $listChannelIds); + $data = []; + foreach ($channelIds as $key => $item) { + $data[] = ['admin_id' => $ids, 'channel_id' => $item]; + } + $model = new ChannelAdmin(); + $model->saveAll($data, true); + } + \think\Db::commit(); + } catch (Exception $e) { + \think\Db::rollback(); + $this->error($e->getMessage()); + } + $this->success("保存成功!"); + } + } + + if ($this->request->isAjax()) { + $list = \think\Db::name("cms_channel_admin") + ->group("admin_id") + ->field("COUNT(*) as channels,admin_id") + ->select(); + $adminChannelList = []; + foreach ($list as $index => $item) { + $adminChannelList[$item['admin_id']] = $item['channels']; + } + + $superAdminIds = AuthGroupAccess::where('group_id', 1)->column('uid'); + + $adminList = Admin::order('id', 'desc')->field('id,username,nickname')->select(); + foreach ($adminList as $index => $item) { + $item->channels = isset($adminChannelList[$item['id']]) ? $adminChannelList[$item['id']] : 0; + $item->superadmin = in_array($item['id'], $superAdminIds); + } + $total = count($adminList); + $result = array("total" => $total, "rows" => $adminList); + + return json($result); + } + $config = get_addon_config('cms'); + $this->view->assign("isChannelAllocate", $config['channelallocate']); + return $this->view->fetch(); + } + + /** + * Selectpage搜索 + * + * @internal + */ + public function selectpage() + { + return parent::selectpage(); + } + + /** + * 检测元素是否可用 + * @internal + */ + public function check_element_available() + { + $id = $this->request->request('id'); + $name = $this->request->request('name'); + $value = $this->request->request('value'); + $name = substr($name, 4, -1); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if ($name == 'diyname') { + if ($id) { + $this->model->where('id', '<>', $id); + } + $exist = $this->model->where($name, $value)->find(); + if ($exist) { + $this->error(__('The data already exist')); + } else { + $this->success(); + } + } elseif ($name == 'name') { + $nameArr = array_filter(explode("\n", str_replace("\r\n", "\n", $value))); + if (count($nameArr) > 1) { + foreach ($nameArr as $index => $item) { + $itemArr = array_filter(explode('|', $item)); + if (!isset($itemArr[1])) { + $this->error('格式:分类名称|自定义名称'); + } + $exist = \app\admin\model\cms\Channel::getByDiyname($itemArr[1]); + if ($exist) { + $this->error('自定义名称[' . $itemArr[1] . ']已经存在'); + } + } + $this->success(); + } else { + $this->success(); + } + } + } +} diff --git a/application/admin/controller/cms/Comment.php b/application/admin/controller/cms/Comment.php new file mode 100644 index 0000000..a4b036f --- /dev/null +++ b/application/admin/controller/cms/Comment.php @@ -0,0 +1,138 @@ +model = new \app\admin\model\cms\Comment; + $this->view->assign("typeList", $this->model->getTypeList()); + $this->view->assign("statusList", $this->model->getStatusList()); + } + + /** + * 查看 + */ + public function index() + { + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $total = $this->model + ->where($where) + ->order($sort, $order) + ->count(); + + $list = $this->model + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + + foreach ($list as $index => $item) { + $item->user->visible(['id', 'username', 'nickname', 'avatar']); + $item->source = $item->source; + } + $list = collection($list)->toArray(); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + $this->assignconfig("typeList", $this->model->getTypeList()); + return $this->view->fetch(); + } + + public function recyclebin() + { + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + $this->relationSearch = true; + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $total = $this->model + ->onlyTrashed() + ->with(['archives', 'spage', 'user']) + ->where($where) + ->order($sort, $order) + ->count(); + + $list = $this->model + ->onlyTrashed() + ->with(['archives', 'spage', 'user']) + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + + foreach ($list as $index => $item) { + $item->user->visible(['id', 'username', 'nickname', 'avatar']); + $type = $item['type'] == 'page' ? 'spage' : $item['type']; + $item->url = $item->{$type} ? $item->{$type}->url : 'javascript:'; + } + $list = collection($list)->toArray(); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + + public function restore($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + if ($ids) { + $this->model->where($pk, 'in', $ids); + } + $config = get_addon_config('cms'); + $list = $this->model->onlyTrashed()->select(); + if ($list) { + $ids = []; + foreach ($list as $index => $item) { + if ($item['status'] == 'normal') { + User::score($config['score']['postcomment'], $item['user_id'], '发表评论'); + } + $ids[] = $item['id']; + } + $this->model->where('id', 'in', $ids); + $this->model->restore('1=1'); + foreach ($list as $index => $item) { + \app\admin\model\cms\Comment::refreshSourceComments($item['id']); + } + $this->success(); + } + $this->error(__('No rows were updated')); + } + +} diff --git a/application/admin/controller/cms/Config.php b/application/admin/controller/cms/Config.php new file mode 100644 index 0000000..41797e8 --- /dev/null +++ b/application/admin/controller/cms/Config.php @@ -0,0 +1,70 @@ +error(__('No Results were found')); + } + if ($this->request->isPost()) { + $params = $this->request->post("row/a", [], 'trim'); + if ($params) { + foreach ($config as $k => &$v) { + if (isset($params[$v['name']])) { + if ($v['type'] == 'array') { + $params[$v['name']] = is_array($params[$v['name']]) ? $params[$v['name']] : (array)json_decode($params[$v['name']], true); + $value = $params[$v['name']]; + } else { + $value = is_array($params[$v['name']]) ? implode(',', $params[$v['name']]) : $params[$v['name']]; + } + $v['value'] = $value; + } + } + try { + //更新配置文件 + set_addon_fullconfig($name, $config); + Service::refresh(); + $this->success(); + } catch (Exception $e) { + $this->error(__($e->getMessage())); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + $tips = []; + foreach ($config as $index => &$item) { + if ($item['name'] == '__tips__') { + $tips = $item; + unset($config[$index]); + } + } + $this->view->assign("addon", ['info' => $info, 'config' => $config, 'tips' => $tips]); + $configFile = ADDON_PATH . $name . DS . 'config.html'; + $viewFile = is_file($configFile) ? $configFile : ''; + return $this->view->fetch($viewFile); + } +} diff --git a/application/admin/controller/cms/Diydata.php b/application/admin/controller/cms/Diydata.php new file mode 100644 index 0000000..28ea349 --- /dev/null +++ b/application/admin/controller/cms/Diydata.php @@ -0,0 +1,174 @@ +request->param('diyform_id'); + $this->diyform = \app\admin\model\cms\Diyform::get($diyform_id); + if (!$this->diyform) { + $this->error('未找到对应自定义表单'); + } + $this->model = new \addons\cms\model\Diydata([], $this->diyform); + $this->assignconfig('diyform_id', $diyform_id); + } + + /** + * 查看 + */ + public function index() + { + $fieldsList = \app\admin\model\cms\Fields::where('source', 'diyform')->where('source_id', $this->diyform['id'])->where('type', '<>', 'text')->select(); + $fields = []; + foreach ($fieldsList as $index => $item) { + $fields[] = ['field' => $item['name'], 'title' => $item['title'], 'type' => $item['type'], 'content' => $item['content_list']]; + } + $this->assignconfig('fields', $fields); + $where = []; + + + $config = get_addon_config('cms'); + if ($config['diyformdatalimit'] != 'all') { + $this->dataLimit = $config['diyformdatalimit']; + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $where[$this->dataLimitField] = ['in', $adminIds]; + } + $this->dataLimit = false; + } + + $diyformList = \app\admin\model\cms\Diyform::where($where)->select(); + $this->view->assign('diyform', $this->diyform); + $this->view->assign('diyformList', $diyformList); + return parent::index(); + } + + /** + * 添加 + */ + public function add() + { + $this->assignFields(); + if ($this->request->isPost()) { + $params = $this->request->post("row/a"); + if ($params) { + if ($this->dataLimit && $this->dataLimitFieldAutoFill) { + $params[$this->dataLimitField] = $this->auth->id; + } + try { + $result = $this->model->save($params); + if ($result !== false) { + $this->success(); + } else { + $this->error($this->model->getError()); + } + } catch (\think\exception\PDOException $e) { + $this->error($e->getMessage()); + } catch (\think\Exception $e) { + $this->error($e->getMessage()); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + return $this->view->fetch(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + $row = $this->model->where('id', $ids)->find(); + 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) { + try { + $result = $this->model->update($params, ['id' => $ids]); + if ($result !== false) { + $this->success(); + } else { + $this->error($row->getError()); + } + } catch (\think\exception\PDOException $e) { + $this->error($e->getMessage()); + } catch (\think\Exception $e) { + $this->error($e->getMessage()); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + + $this->assignFields($ids); + $this->view->assign("row", $row); + return $this->view->fetch(); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $count = $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $count = $this->model->where($pk, 'in', $ids)->delete(); + if ($count) { + $this->success(); + } else { + $this->error(__('No rows were deleted')); + } + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + private function assignFields($diydata_id = 0) + { + $values = []; + if ($diydata_id) { + $values = db($this->diyform['table'])->where('id', $diydata_id)->find(); + } + $fields = Service::getCustomFields('diyform', $this->diyform['id'], $values); + + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + } + +} diff --git a/application/admin/controller/cms/Diyform.php b/application/admin/controller/cms/Diyform.php new file mode 100644 index 0000000..6e2d34d --- /dev/null +++ b/application/admin/controller/cms/Diyform.php @@ -0,0 +1,117 @@ +dataLimit = $config['diyformdatalimit']; + } + + $this->assignconfig('spiderRecord', intval($config['spiderrecord']?? 0)); + + $this->model = new \app\admin\model\cms\Diyform; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + 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); + + + \app\admin\model\cms\SpiderLog::render($list, 'diyform'); + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + public function add() + { + if ($this->request->isAjax()){ + $params = $this->request->post('row/a'); + if ($params['notice'] == 1 && empty($params['notice_mobile'])){ + $this->error("通知人手机号不能为空!"); + } + + parent::add(); + } + + return $this->view->fetch(); + + } + + public function edit($ids = null) + { + if ($this->request->isAjax()){ + $params = $this->request->post('row/a'); + if ($params['notice'] == 1 && empty($params['notice_mobile'])){ + $this->error("通知人手机号不能为空!"); + } + } + parent::edit($ids); + return $this->view->fetch(); + + } + + + /** + * 检测元素是否可用 + * @internal + */ + public function check_element_available() + { + $id = $this->request->request('id'); + $name = $this->request->request('name'); + $value = $this->request->request('value'); + $name = substr($name, 4, -1); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if ($id) { + $this->model->where('id', '<>', $id); + } + $exist = $this->model->where($name, $value)->find(); + if ($exist) { + $this->error(__('The data already exist')); + } else { + $this->success(); + } + } +} diff --git a/application/admin/controller/cms/Fields.php b/application/admin/controller/cms/Fields.php new file mode 100644 index 0000000..2f6f7c8 --- /dev/null +++ b/application/admin/controller/cms/Fields.php @@ -0,0 +1,403 @@ +model = new \app\admin\model\cms\Fields; + $this->view->assign("statusList", $this->model->getStatusList()); + $this->view->assign('typeList', Config::getTypeList()); + $this->view->assign('regexList', Config::getRegexList()); + $this->assignconfig('withoutModelList', ['channel', 'page', 'special', 'block']); + $this->assignconfig('contributeFields', \app\admin\model\cms\Fields::getContributeFields()); + $this->assignconfig('publishFields', \app\admin\model\cms\Fields::getPublishFields()); + } + + /** + * 查看 + */ + public function index() + { + $source = $this->request->param('source', ''); + $source_id = $this->request->param('source_id', 0); + + $condition = ['source' => $source, 'source_id' => $source_id]; + $prefix = \think\Config::get('database.prefix'); + + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $total = $this->model + ->where($condition) + ->where($where) + ->order($sort, $order) + ->count(); + + $list = $this->model + ->where($condition) + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + + + if ($source == 'model') { + $fieldList = Service::getTableFields("{$prefix}cms_archives"); + $model = \app\admin\model\cms\Modelx::get($source_id); + if (!$model) { + $this->error("模型未找到"); + } + $setting = $model->setting; + $titles = isset($setting['titlelist']) ? $setting['titlelist'] : []; + $list = collection($list)->toArray(); + + array_unshift($fieldList, ['name' => 'content', 'title' => __('Content'), 'type' => 'text']); + foreach ($fieldList as $field) { + $item = [ + 'id' => $field['name'], + 'state' => false, + 'source_id' => $source_id, + 'source' => '-', + 'name' => $field['name'], + 'title' => $field['title'] . (isset($titles[$field['name']]) && $titles[$field['name']] != $field['title'] ? "({$titles[$field['name']]})" : ''), + 'type' => $field['type'], + 'issystem' => true, + 'isfilter' => isset($setting['filterfields']) && is_array($setting['filterfields']) && in_array($field['name'], $setting['filterfields']) ? 1 : 0, + 'iscontribute' => isset($setting['contributefields']) && is_array($setting['contributefields']) && in_array($field['name'], $setting['contributefields']) ? 1 : 0, + 'ispublish' => isset($setting['publishfields']) && is_array($setting['publishfields']) && in_array($field['name'], $setting['publishfields']) ? 1 : 0, + 'isorder' => isset($setting['orderfields']) && is_array($setting['orderfields']) && in_array($field['name'], $setting['orderfields']) ? 1 : 0, + 'status' => 'normal', + 'createtime' => 0, + 'updatetime' => 0 + ]; + $list[] = $item; + } + } elseif (in_array($source, ['channel', 'page', 'special', 'block'])) { + $fieldList = Service::getTableFields("{$prefix}cms_" . $source); + $fields = []; + foreach ($list as $index => $item) { + $fields[] = $item['name']; + } + foreach ($fieldList as $index => $field) { + if (in_array($field['name'], $fields)) { + continue; + } + $item = [ + 'id' => $field['name'], + 'state' => false, + 'source_id' => $source_id, + 'source' => '-', + 'name' => $field['name'], + 'title' => $field['title'], + 'type' => $field['type'], + 'issystem' => true, + 'isfilter' => 0, + 'iscontribute' => 0, + 'ispublish' => 0, + 'isorder' => 0, + 'status' => 'normal', + 'createtime' => 0, + 'updatetime' => 0 + ]; + $list[] = $item; + } + } elseif ($source == 'diyform') { + $diyform = \app\admin\model\cms\Diyform::get($source_id); + if (!$diyform) { + $this->error("表单未找到"); + } + $setting = $diyform->setting; + $titles = isset($setting['titlelist']) ? $setting['titlelist'] : []; + $fieldList = [ + ['name' => 'id', 'title' => 'ID', 'type' => 'int'], + ['name' => 'memo', 'title' => __('Memo'), 'type' => 'string'], + ['name' => 'createtime', 'title' => __('Createtime'), 'type' => 'int'], + ['name' => 'updatetime', 'title' => __('Updatetime'), 'type' => 'int'], + ['name' => 'status', 'title' => __('Status'), 'type' => 'enum'], + ]; + foreach ($fieldList as $index => $field) { + $item = [ + 'id' => $field['name'], + 'state' => false, + 'source_id' => $source_id, + 'source' => '-', + 'name' => $field['name'], + 'title' => $field['title'] . (isset($titles[$field['name']]) && $titles[$field['name']] != $field['title'] ? "({$titles[$field['name']]})" : ''), + 'type' => $field['type'], + 'issystem' => true, + 'isfilter' => isset($setting['filterfields']) && is_array($setting['filterfields']) && in_array($field['name'], $setting['filterfields']) ? 1 : 0, + 'iscontribute' => 0, + 'ispublish' => 0, + 'isorder' => isset($setting['orderfields']) && is_array($setting['orderfields']) && in_array($field['name'], $setting['orderfields']) ? 1 : 0, + 'status' => 'normal', + 'createtime' => 0, + 'updatetime' => 0 + ]; + $list[] = $item; + } + } + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + $this->assignconfig('params', "/source/{$source}/source_id/{$source_id}"); + $this->assignconfig('source', $source); + $this->view->assign('source', $source); + $this->view->assign('source_id', $source_id); + + if (in_array($source, ['model', 'diyform'])) { + $model = $source == 'model' ? \app\admin\model\cms\Modelx::get($source_id) : \app\admin\model\cms\Diyform::get($source_id); + $this->view->assign('model', $model); + $modelList = $source == 'model' ? \app\admin\model\cms\Modelx::all() : \app\admin\model\cms\Diyform::all(); + $this->view->assign('modelList', $modelList); + } + + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + $source = $this->request->param('source', ''); + $source_id = $this->request->param('source_id', 0); + $this->view->assign('source', $source); + $this->view->assign('source_id', $source_id); + + $this->renderTable(); + return parent::add(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + if (is_numeric($ids)) { + $this->renderTable(); + return parent::edit($ids); + } else { + $source = $this->request->param('source'); + $source_id = $this->request->param('source_id'); + $prefix = \think\Config::get('database.prefix'); + $fieldList = Service::getTableFields("{$prefix}cms_archives"); + $model = $source == 'model' ? \app\admin\model\cms\Modelx::get($source_id) : \app\admin\model\cms\Diyform::get($source_id); + if (!$model) { + $this->error("模型未找到"); + } + $setting = $model->setting; + $name = $ids; + $title = ''; + foreach ($fieldList as $index => $item) { + if ($item['name'] == $name) { + $title = $item['title']; + break; + } + } + $setting['filterlist'] = isset($setting['filterlist']) ? $setting['filterlist'] : []; + $setting['titlelist'] = isset($setting['titlelist']) ? $setting['titlelist'] : []; + $title = isset($setting['titlelist'][$name]) ? $setting['titlelist'][$name] : $title; + + if ($this->request->isPost()) { + $row = $this->request->post("row/a"); + + foreach (['filter', 'contribute', 'order'] as $index => $item) { + if ($source == 'diyform' && $item == 'contribute') { + continue; + } + $field = $item . 'fields'; + $setting[$field] = isset($setting[$field]) ? $setting[$field] : []; + $setting[$field] = array_diff($setting[$field], [$name]); + if (isset($row['is' . $item]) && $row['is' . $item]) { + $setting[$field] = array_merge($setting[$field], [$name]); + } + if ($item == 'filter') { + if (isset($row['is' . $item]) && $row['is' . $item]) { + $setting['filterlist'][$name] = $row['filterlist']; + } + } + } + $setting['titlelist'][$name] = $row['title']; + $model->save(['setting' => $setting]); + $this->success(); + } + $row = [ + 'source' => $this->request->param('source'), + 'source_id' => $this->request->param('source_id'), + 'name' => $name, + 'title' => $title, + 'isfilter' => isset($setting['filterfields']) && in_array($name, $setting['filterfields']), + 'iscontribute' => isset($setting['contributefields']) && in_array($name, $setting['contributefields']), + 'isorder' => isset($setting['orderfields']) && in_array($name, $setting['orderfields']), + 'filterlist' => isset($setting['filterlist']) && isset($setting['filterlist'][$name]) ? $setting['filterlist'][$name] : '', + ]; + $this->view->assign("row", $row); + return $this->view->fetch('cms/fields/archives'); + } + } + + /** + * 渲染表 + */ + protected function renderTable() + { + $tableList = []; + $dbname = \think\Config::get('database.database'); + $list = \think\Db::query("SELECT `TABLE_NAME`,`TABLE_COMMENT` FROM `information_schema`.`TABLES` where `TABLE_SCHEMA` = '{$dbname}';"); + foreach ($list as $key => $row) { + $tableList[$row['TABLE_NAME']] = $row['TABLE_COMMENT']; + } + $this->view->assign("tableList", $tableList); + } + + /** + * 批量操作 + * @param string $ids + */ + public function multi($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $params = $this->request->request('params'); + parse_str($params, $paramsArr); + if (isset($paramsArr['iscontribute']) && !is_numeric($ids)) { + if (!$ids || !in_array($ids, \app\admin\model\cms\Fields::getContributeFields())) { + $this->error('参数错误'); + } + $source_id = $this->request->param('source_id', 0); + $model = \app\admin\model\cms\Modelx::get($source_id); + if (!$model) { + $this->error("模型未找到"); + } + $setting = $model['setting']; + $contributefields = isset($setting['contributefields']) ? $setting['contributefields'] : []; + if ($paramsArr['iscontribute']) { + $contributefields[] = $ids; + } else { + $contributefields = array_values(array_diff($contributefields, [$ids])); + } + $setting['contributefields'] = $contributefields; + $model->setting = $setting; + $model->save(); + $this->success(""); + } + if (isset($paramsArr['ispublish']) && !is_numeric($ids)) { + if (!$ids || !in_array($ids, \app\admin\model\cms\Fields::getPublishFields())) { + $this->error('参数错误'); + } + $source_id = $this->request->param('source_id', 0); + $model = \app\admin\model\cms\Modelx::get($source_id); + if (!$model) { + $this->error("模型未找到"); + } + $setting = $model['setting']; + $publishfields = isset($setting['publishfields']) ? $setting['publishfields'] : []; + if ($paramsArr['ispublish']) { + $publishfields[] = $ids; + } else { + $publishfields = array_values(array_diff($publishfields, [$ids])); + } + $setting['publishfields'] = $publishfields; + $model->setting = $setting; + $model->save(); + $this->success(""); + } + if (isset($paramsArr['isorder']) && !is_numeric($ids)) { + if (!$ids) { + $this->error('参数错误'); + } + $source = $this->request->param('source', ''); + $source_id = $this->request->param('source_id', 0); + $model = $source == 'model' ? \app\admin\model\cms\Modelx::get($source_id) : \app\admin\model\cms\Diyform::get($source_id); + if (!$model) { + $this->error("模型未找到"); + } + $setting = $model['setting']; + $orderfields = isset($setting['orderfields']) ? $setting['orderfields'] : []; + if ($paramsArr['isorder']) { + $orderfields[] = $ids; + } else { + $orderfields = array_values(array_diff($orderfields, [$ids])); + } + $setting['orderfields'] = $orderfields; + $model->setting = $setting; + $model->save(); + $this->success(""); + } + if (isset($paramsArr['isfilter']) && !is_numeric($ids)) { + if (!$ids) { + $this->error('参数错误'); + } + $source = $this->request->param('source', ''); + $source_id = $this->request->param('source_id', 0); + $model = $source == 'model' ? \app\admin\model\cms\Modelx::get($source_id) : \app\admin\model\cms\Diyform::get($source_id); + if (!$model) { + $this->error("模型未找到"); + } + $setting = $model['setting']; + $filterfields = isset($setting['filterfields']) ? $setting['filterfields'] : []; + if ($paramsArr['isfilter']) { + $filterfields[] = $ids; + } else { + $filterfields = array_values(array_diff($filterfields, [$ids])); + } + $setting['filterfields'] = $filterfields; + $model->setting = $setting; + $model->save(); + $this->success(""); + } + return parent::multi($ids); + } + + /** + * 规则列表 + * @internal + */ + public function rulelist() + { + //主键 + $primarykey = $this->request->request("keyField"); + //主键值 + $keyValue = $this->request->request("keyValue", ""); + + $keyValueArr = array_filter(explode(',', $keyValue)); + $regexList = Config::getRegexList(); + $list = []; + foreach ($regexList as $k => $v) { + if ($keyValueArr) { + if (in_array($k, $keyValueArr)) { + $list[] = ['id' => $k, 'name' => $v]; + } + } else { + $list[] = ['id' => $k, 'name' => $v]; + } + } + return json(['list' => $list]); + } +} diff --git a/application/admin/controller/cms/League.php b/application/admin/controller/cms/League.php new file mode 100644 index 0000000..b85fec1 --- /dev/null +++ b/application/admin/controller/cms/League.php @@ -0,0 +1,690 @@ +leagueService = new LeagueService(); + $this->leagueRound = new Leagueround(); + } + + public function export() + { + $matchranking = new MatchRanking(); + $user = new User(); + $players = new Players(); + $this->exportmatch_id = $this->request->param("match_id"); + $this->exportcourse = $this->request->param("course"); // 赛程 + $this->exportround = $this->request->param("other_round"); + $this->exportsorting = $this->request->param("sorting"); + $this->birthday = $this->request->param("birthday"); + // if ($this->exportsorting == "false") { + // $this->exportsorting = false; + // } + + if (empty($this->exportmatch_id)) { + $this->error("赛事id不能为空!"); + } else if (empty($this->exportcourse)) { + $this->error("赛程不可为空!"); + } + + // $row = $this->rankingService->getRankingForCourse($this->exportmatch_id, $this->exportcourse, $this->exportsorting, "最佳成绩"); + if($this->exportcourse == 101 || $this->exportcourse == 102 ){ + $row = $this->leagueService->bestplayerqua($this->exportmatch_id, $this->exportcourse ,true); + if($this->exportcourse == 101 && $this->exportround != 'best'){ + $row = $this->leagueService->getrank_course($this->exportmatch_id, $this->exportcourse,$this->exportround); + // var_dump($row);exit; + } + // $round = $matchranking->where("match_id", "eq", $this->exportmatch_id) + // ->where("course", "eq", $this->exportcourse) + // ->max("other_round"); + // var_dump($this->exportround);exit; + } + if($this->exportcourse == 103){ + $Leaguexport = new Leaguexport(); + $row = $this->leagueRound->group_round2($this->exportmatch_id, $this->exportcourse,$this->exportround,true); + // var_dump($row);exit; + } + if($this->exportcourse == 104){ + $Leaguexport = new Leaguexport(); + $row = $this->leagueRound->final_rank($this->exportmatch_id); + } + if($this->exportcourse == 'all'){ + $LeagueIntegral = new LeagueIntegral(); + $row = $LeagueIntegral->where('match_id',$this->exportmatch_id)->select(); + if(empty($row)) $this->error('请先生成积分'); + } + $archives = new \app\admin\model\cms\Archives(); + $PDOStatement = $archives->find($this->exportmatch_id); + if (empty($PDOStatement)) { + $this->error('导出数据为空'); + } + $cellTitles = [ + 'player_id' => '编号', + 'player_name' => '姓名', + 'gender' => '性别', + // 'position' => '号位', + 'name_short' => '所属队伍', + 'first_round_time1' => '第一圈', + 'first_round_time2' => '第二圈', + 'first_round_time3' => '第三圈', + 'second_round_time1' => '第一圈', + 'second_round_time2' => '第二圈', + 'second_round_time3' => '第三圈', + 'third_round_time1' => '第一圈', + 'third_round_time2' => '第二圈', + 'third_round_time3' => '第三圈', + 'fourth_round_time1' => '第一圈', + 'fourth_round_time2' => '第二圈', + 'fourth_round_time3' => '第三圈', + 'best_time1' => '第一圈', + 'best_time2' => '第二圈', + 'best_time3' => '第三圈', + // 'fly_num' => '圈数', + 'times' => '平均用时', + 'num' => '排名', + 'channel' => '频道', + 'led_color' => 'LED颜色', + 'grouping' => '组别', + 'is_young' => '是否满足青少年组', + + ]; + if($this->exportcourse == 101 && $this->exportround != 'best'){ + $cellTitles = [ + 'grouping' => '组别', + 'player_id' => '编号', + 'player_name' => '姓名', + 'gender' => '性别', + 'position' => '号位', + 'name_short' => '所属队伍', + 'first_round_time1' => '第一圈', + 'first_round_time2' => '第二圈', + 'first_round_time3' => '第三圈', + 'second_round_time1' => '第一圈', + 'second_round_time2' => '第二圈', + 'second_round_time3' => '第三圈', + 'third_round_time1' => '第一圈', + 'third_round_time2' => '第二圈', + 'third_round_time3' => '第三圈', + 'fourth_round_time1' => '第一圈', + 'fourth_round_time2' => '第二圈', + 'fourth_round_time3' => '第三圈', + // 'best_time1' => '第一圈', + // 'best_time2' => '第二圈', + // 'best_time3' => '第三圈', + // 'fly_num' => '圈数', + // 'times' => '平均用时', + // 'num' => '排名', + 'channel' => '频道', + 'led_color' => 'LED颜色', + 'is_young' => '是否满足青少年组', + ]; + } + if($this->exportcourse == 102){ + $cellTitles = [ + 'num' => '序号', + 'player_id' => '编号', + 'player_name' => '姓名', + 'first_round_time1' => '第一圈', + 'first_round_time2' => '第二圈', + 'first_round_time3' => '第三圈', + 'second_round_time1' => '第一圈', + 'second_round_time2' => '第二圈', + 'second_round_time3' => '第三圈', + 'fly_num' => '总圈数', + 'times' => '总时间', + 'grouping' => '组别', + 'channel' => '频道', + 'led_color' => 'LED颜色', + ]; + } + if($this->exportcourse == 103 || $this->exportcourse == 104){ + $cellTitles = [ + 'grouping' => '组别', + 'player_id' => '编号', + 'player_name' => '姓名', + 'gender' => '性别', + 'fly_num' => '总圈数', + 'times' => '总时间', + 'group_sort' => '小组排名', + 'channel' => '频道', + 'led_color' => 'LED颜色', + 'is_young' => '是否满足青少年组', + ]; + } + if($this->exportcourse == 'all'){ + $cellTitles = [ + 'num' => '排名', + 'player_id' => '编号', + 'player_name' => '姓名', + 'gender' => '性别', + 'name_short' => '所属队伍', + 'is_young' => '是否满足青少年组', + ]; + } + // var_dump($cellTitles);exit; + // if($round == 3){ + // $cellTitles['third_round_time1'] = '第一圈'; + // $cellTitles['third_round_time2'] = '第二圈'; + // $cellTitles['third_round_time3'] = '第三圈'; + // } + + // if($round == 4){ + // $cellTitles['third_round_time1'] = '第一圈'; + // $cellTitles['third_round_time2'] = '第二圈'; + // $cellTitles['third_round_time3'] = '第三圈'; + // $cellTitles['fourth_round_time1'] = '第一圈'; + // $cellTitles['fourth_round_time2'] = '第二圈'; + // $cellTitles['fourth_round_time3'] = '第三圈'; + // } + // $cellTitles['fly_num'] = '圈数'; + // $cellTitles['times'] = '时间'; + // $cellTitles['grouping'] = '组别'; + // $cellTitles['channel'] = '频道'; + // $cellTitles['led_color'] = 'LED颜色'; + // var_dump($cellTitles);exit; + // var_dump($row);exit; + // 数据总条数 + // var_dump($row);exit; + $total = count($row); + if ($total <= 0) { + $this->error('导出数据为空'); + } + + + switch ($this->exportcourse) { + case 101: + $PDOStatement->title = $PDOStatement->title."资格赛成绩"; + break; + case 102: + $PDOStatement->title = $PDOStatement->title."排位赛"; + break; + case 103: + $which_round = $this->win_or_lose_group($this->exportround); + $PDOStatement->title = $PDOStatement->title."淘汰赛【第".$this->exportround.'轮'.$which_round['which_round'].'组】'.$which_round['status'].'-(成绩公布)'; + break; + case 104: + $PDOStatement->title = $PDOStatement->title."决赛成绩"; + break; + case 'all': + $PDOStatement->title = $PDOStatement->title."最终排名"; + break; + default: + if ($this->exportsorting == false) { + $PDOStatement->title = $PDOStatement->title."【决赛】-未排序"; + } else { + $PDOStatement->title = $PDOStatement->title."【决赛】-已排序"; + } + break; + + } + //计算资格赛每组人数 + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => $PDOStatement->title, + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => false, + 'group_people' =>4, + ]; + if( $this->exportcourse != 'all'){ + $group_people = $this->leagueService->group_people($this->exportmatch_id, $this->exportcourse); + $params['group_people'] = $group_people; + } + + $total_commission = 0; + $config = $export->getConfig(); + if($this->exportcourse == 101 && $this->exportround == 'best'){ + $result = $export->exportRank_bestcourse($params, function ($pages) use (&$total_commission, $total) { + $datas = $this->leagueService->bestplayerqua($this->exportmatch_id, $this->exportcourse ,true); + // var_dump($datas);exit; + $user = new User(); + $players = new Players(); + // var_dump($datas);exit; + $newDatas = []; + $nums = 0; + foreach ($datas as &$reward) { + // var_dump($reward);exit; + // var_dump(json_encode($reward['round1']['first_fly_time']));exit; + // if(!array_key_exists($reward['round1']['first_fly_time'],$reward)) var_dump($reward['round1']);exit; + $user_info = $user->where('member_number',$reward['info']['player_id'])->find(); + $player_info = $players->where('member_id',$user_info['id'])->find(); + $data = [ + 'name_short' => $reward['info']['name_short'], + 'is_young' => $player_info['birthday'] >= $this->birthday ? '是' : '否', + 'gender' => $player_info['gender'], + 'num' => ++$nums, + 'player_id' => $reward['info']['player_id'] ? $reward['info']['player_id'] : 0, + 'player_name' => $reward['info']['player_name'], + // 'fly_num' => $reward['fly_num'], + 'times' => $reward['avg'], + 'grouping' => $reward['info']['grouping'], + 'channel' => $reward['info']['channel'], + 'led_color' => $reward['info']['led_color'], + 'first_round_time1' => $reward['round1']['first_fly_time'] ? $reward['round1']['first_fly_time'] : '', + 'first_round_time2' => $reward['round1']['second_fly_time'] ? $reward['round1']['second_fly_time'] : '', + 'first_round_time3' => $reward['round1']['third_fly_time'] ? $reward['round1']['third_fly_time']:'', + 'second_round_time1' => $reward['round2']['first_fly_time'] ? $reward['round2']['first_fly_time'] : '', + 'second_round_time2' => $reward['round2']['second_fly_time']? $reward['round2']['second_fly_time'] : '', + 'second_round_time3' => $reward['round2']['third_fly_time']? $reward['round2']['third_fly_time']:'', + 'third_round_time1' => array_key_exists('round3',$reward) ? $reward['round3']['first_fly_time'] : '', + 'third_round_time2' => array_key_exists('round3',$reward) ? $reward['round3']['second_fly_time'] : '', + 'third_round_time3' => array_key_exists('round3',$reward) ? $reward['round3']['third_fly_time'] : '', + 'fourth_round_time1' => array_key_exists('round4',$reward) ? $reward['round4']['first_fly_time'] : '', + 'fourth_round_time2' => array_key_exists('round4',$reward) ? $reward['round4']['second_fly_time'] : '', + 'fourth_round_time3' => array_key_exists('round4',$reward) ? $reward['round4']['third_fly_time'] : '', + 'best_time1' => $this->getAllSeconds($reward['first']), + 'best_time2' => $this->getAllSeconds($reward['second']), + 'best_time3' => $this->getAllSeconds($reward['third']), + ]; + + $newDatas[] = $data; + } + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + return $newDatas; + }); + + } + + if($this->exportcourse == 101 && $this->exportround != 'best'){ + $result = $export->exportRank_course($params, function ($pages) use (&$total_commission, $total) { + // var_dump(123);exit; + // $datas = $this->leagueService->bestplayerqua($this->exportmatch_id, $this->exportcourse ,true); + $datas = $this->leagueService->qqq($this->exportmatch_id, $this->exportcourse); + $group_people = $this->leagueService->group_people($this->exportmatch_id, $this->exportcourse); + // var_dump($group_people);exit; + $user = new User(); + $players = new Players(); + // var_dump($datas);exit; + $newDatas = []; + $nums = 0;$j = 0; + foreach ($datas as &$reward) { + // var_dump($reward);exit; + if($j == $group_people) {$j = 0;} + $user_info = $user->where('member_number',$reward['info']['player_id'])->find(); + $player_info = $players->where('member_id',$user_info['id'])->find(); + $data = [ + 'position' => $j= $j + 1, + 'name_short' => $reward['info']['name_short'], + 'is_young' => $player_info['birthday'] >= $this->birthday ? '是' : '否', + 'gender' => $player_info['gender'], + 'player_id' => $reward['info']['player_id'] ? $reward['info']['player_id'] : 0, + 'player_name' => $reward['info']['player_name'], + // 'fly_num' => $reward['fly_num'], + // 'times' => $reward['avg'], + 'grouping' => $reward['info']['grouping'], + 'channel' => $reward['info']['channel'], + 'led_color' => $reward['info']['led_color'], + 'first_round_time1' => $reward['round1']['first_fly_time'] ? $reward['round1']['first_fly_time'] : '', + 'first_round_time2' => $reward['round1']['second_fly_time'] ? $reward['round1']['second_fly_time'] : '', + 'first_round_time3' => $reward['round1']['third_fly_time'] ? $reward['round1']['third_fly_time']:'', + 'second_round_time1' => $reward['round2']['first_fly_time']? $reward['round2']['first_fly_time'] : '', + 'second_round_time2' => $reward['round2']['second_fly_time']? $reward['round2']['second_fly_time'] : '', + 'second_round_time3' => $reward['round2']['third_fly_time']? $reward['round2']['third_fly_time']:'', + 'third_round_time1' => array_key_exists('round3',$reward) ? $reward['round3']['first_fly_time'] : '', + 'third_round_time2' => array_key_exists('round3',$reward) ? $reward['round3']['second_fly_time'] : '', + 'third_round_time3' => array_key_exists('round3',$reward) ? $reward['round3']['third_fly_time'] : '', + 'fourth_round_time1' => array_key_exists('round4',$reward) ? $reward['round4']['first_fly_time'] : '', + 'fourth_round_time2' => array_key_exists('round4',$reward) ? $reward['round4']['second_fly_time'] : '', + 'fourth_round_time3' => array_key_exists('round4',$reward) ? $reward['round4']['third_fly_time'] : '', + ]; + + $newDatas[] = $data; + } + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + return $newDatas; + }); + + } + + if($this->exportcourse == 102){ + $result = $export->exportRank_sen($params, function ($pages) use (&$total_commission, $total) { + $datas = $this->leagueService->bestplayersen($this->exportmatch_id, $this->exportcourse ,true); + $newDatas = []; + $nums = 0; + foreach ($datas as &$reward) { + // var_dump(json_encode($reward['round1']['first_fly_time']));exit; + // if(!array_key_exists($reward['round1']['first_fly_time'],$reward)) var_dump($reward['round1']);exit; + $data = [ + 'num' => ++$nums, + 'player_id' => $reward['info']['player_id'] ? $reward['info']['player_id'] : 0, + 'player_name' => $reward['info']['player_name'], + 'fly_num' => $reward['fly_num'], + 'times' => $reward['sum'], + 'grouping' => $reward['info']['grouping'], + 'channel' => $reward['info']['channel'], + 'led_color' => $reward['info']['led_color'], + 'first_round_time1' => $reward['round1']['first_fly_time'] ? $reward['round1']['first_fly_time'] : 0, + 'first_round_time2' => $reward['round1']['second_fly_time'] ? $reward['round1']['second_fly_time'] : 0, + 'first_round_time3' => $reward['round1']['third_fly_time'] ? $reward['round1']['third_fly_time']:0, + 'second_round_time1' => $reward['round2']['first_fly_time'], + 'second_round_time2' => $reward['round2']['second_fly_time'], + 'second_round_time3' => $reward['round2']['third_fly_time'], + ]; + + $newDatas[] = $data; + } + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + return $newDatas; + }); + + } + + if($this->exportcourse == 103){ + + $result = $export->exportRank_tao($params, function ($pages) use (&$total_commission, $total) { + $Leaguexport = new Leaguexport(); + $Leagueround = new Leagueround(); + $user = new User(); + $players = new Players(); + if($this->exportround == 1){ + $datas = $Leagueround->group_round2($this->exportmatch_id, $this->exportcourse,$this->exportround,true); + }elseif(in_array($this->exportround,array(2,3,5,6,8,9,11,12))){ + $datas = $Leagueround->group_round4($this->exportmatch_id, $this->exportcourse,$this->exportround,true); + }elseif(in_array($this->exportround,array(4,7,10,13))){ + // var_dump(1234);exit; + $datas = $Leagueround->group_round5($this->exportmatch_id, $this->exportcourse,$this->exportround,true); + // var_dump($datas);exit; + } + + // var_dump($datas);exit; + $newDatas = []; + $nums = 0; + foreach ($datas as &$reward) { + $user_info = $user->where('member_number',$reward['player_id'])->find(); + $player_info = $players->where('member_id',$user_info['id'])->find(); + + $data = [ + 'is_young' => $player_info['birthday'] >= $this->birthday ? '是' : '否', + 'gender' => $player_info['gender'], + // 'num' => ++$nums, + 'player_id' => $reward['player_id'] ? $reward['player_id'] : 0, + 'player_name' => $reward['player_name'], + 'fly_num' => $reward['fly_num'], + 'times' => $reward['times'], + 'group_sort' => $reward['group_sort'], + 'grouping' => $reward['grouping'], + 'channel' => $reward['channel'], + 'led_color' => $reward['led_color'], + ]; + + $newDatas[] = $data; + } + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + return $newDatas; + }); + + } + + if($this->exportcourse == 104){ + + $result = $export->exportRank_tao($params, function ($pages) use (&$total_commission, $total) { + $Leagueround = new Leagueround(); + $datas = $Leagueround->final_rank($this->exportmatch_id); + // var_dump($datas);exit; + $newDatas = []; + $nums = 0; + foreach ($datas as &$reward) { + $data = [ + 'num' => ++$nums, + 'player_id' => $reward['player_id'] ? $reward['player_id'] : 0, + 'player_name' => $reward['player_name'], + 'fly_num' => $reward['fly_num'], + 'times' => $reward['times'], + 'group_sort' => $reward['group_sort'], + 'grouping' => $reward['grouping'], + 'channel' => $reward['channel'], + 'led_color' => $reward['led_color'], + ]; + + $newDatas[] = $data; + } + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + return $newDatas; + }); + + } + + if($this->exportcourse == 'all'){ + + $result = $export->export_finalRank($params, function ($pages) use (&$total_commission, $total) { + $LeagueIntegral = new LeagueIntegral(); + $datas = $LeagueIntegral->where('match_id',$this->exportmatch_id)->select(); + if(empty($datas)) $this->error('请先生成积分'); + // $datas = $Leagueround->final_rank($this->exportmatch_id); + // var_dump($datas);exit; + $user = new User(); + $players = new Players(); + // var_dump($datas);exit; + $newDatas = []; + $nums = 0; + foreach ($datas as &$reward) { + // $user_info = $user->where('member_number',$reward['player_id'])->find(); + // $player_info = $players->where('member_id',$user_info['id'])->find(); + + // if(empty($player_info['gender'])){ + // var_dump($user_info);exit; + // } + $data = [ + 'num' => ++$nums, + 'player_id' => $reward['player_id'] ? $reward['player_id'] : 0, + 'player_name' => $reward['player_name'], + 'gender' => $reward['gender'], + 'name_short' => $reward['name_short'], + 'is_young' => $reward['birthday'] >= $this->birthday ? '是' : '否', + ]; + + $newDatas[] = $data; + } + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + return $newDatas; + }); + + } + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } + + + public function win_or_lose_group($other_round){ + switch ($other_round) { + case 1: $which_round['which_round'] = '1-16';$which_round['status'] = '胜者组';break; + case 2: $which_round['which_round'] = '17-24';$which_round['status'] = '胜者组';break; + case 3: $which_round['which_round'] = '25-32';$which_round['status'] = '败者组';break; + case 4: $which_round['which_round'] = '33-40';$which_round['status'] = '败者组';break; + case 5: $which_round['which_round'] = '41-44';$which_round['status'] = '败者组';break; + case 6: $which_round['which_round'] = '45-48';$which_round['status'] = '胜者组';break; + case 7: $which_round['which_round'] = '49-52';$which_round['status'] = '败者组';break; + case 8: $which_round['which_round'] = '53-54';$which_round['status'] = '败者组';break; + case 9: $which_round['which_round'] = '55-56';$which_round['status'] = '胜者组';break; + case 10: $which_round['which_round'] = '57-58';$which_round['status'] = '败者组';break; + case 11: $which_round['which_round'] = '59';$which_round['status'] = '败者组';break; + case 12: $which_round['which_round'] = '60';$which_round['status'] = '胜者组';break; + case 13: $which_round['which_round'] = '61';$which_round['status'] = '败者组';break; + + } + return $which_round; + } + + + public function getrank_course() + { + header('Access-Control-Allow-Origin:*'); + header('Access-Control-Allow-Methods:*'); + header('Access-Control-Allow-Headers:x-requested-with,content-type'); + $matchranking = new MatchRanking(); + $match_id = $this->request->param("match_id"); + $course = $this->request->param("course"); // 赛程 + // $where['sorting'] = $this->request->param("sorting", false); + $other_round = $this->request->param("other_round", false); // 轮次 + $is_pic = $this->request->param("is_pic", 0); // 轮次 + + if (empty($match_id)) { + $this->error("赛事id不能为空!"); + } + $round = $matchranking->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->max("other_round"); + + if($other_round == 'bestsen'){ + $res_data = $this->leagueService->bestplayersen($match_id,$course); + if (!empty($res_data)) { + $res['code'] = 1; + $res['data'] = $res_data; + $res['round'] = $round; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['round'] = ''; + $res['msg'] = "获取失败"; + } + return json($res); + }elseif($other_round == 'best'){ + // var_dump($other_round);exit; + $res_data = $this->leagueService->bestplayerqua($match_id,$course); + // var_dump($res_data);exit; + if (!empty($res_data)) { + $res['code'] = 1; + $res['data'] = $res_data; + $res['round'] = $round; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['round'] = ''; + $res['msg'] = "获取失败"; + } + // var_dump($res);exit; + return json($res); + } + if($other_round == '总积分'){ + // $res_data = $this->leagueService->bestplayersen($match_id,$course); + $res_data = $this->leagueService->get_final_integral($match_id,$course); + if (!empty($res_data)) { + $res['code'] = 1; + $res['data'] = $res_data; + $res['round'] = $round; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['round'] = ''; + $res['msg'] = "获取失败"; + } + return json($res); + } + // var_dump($is_pic);exit; + $row = $this->leagueService->getrank_course($match_id,$course,$other_round,false,$is_pic); + // $row = $this->rankingService->getRankingForCourse($param['match_id'], $param['course'], $param['sorting'], $param['other_round']); + // $row = $matchranking->where($where)->select(); + $result = $matchranking->where('match_id',$match_id)->where('course',104)->where('other_round',1)->find(); + if(!empty($result)){ + if( $result['is_extra'] == 1) { + $res['is_extra'] = 1; + }else{ + $res['is_extra'] = 0; + } + } + if (!empty($row)) { + $res['code'] = 1; + $res['data'] = $row; + $res['round'] = $round; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['round'] = $round; + $res['msg'] = "获取失败1"; + } + + return json($res); + } + + + public function setPlayerRanking() + { + $param['id'] = $this->request->param("id"); // id + $param['player_id'] = $this->request->param("player_id", 0); // 飞手id + $param['match_id'] = $this->request->param("match_id", 0); // 赛事id + $param['course'] = $this->request->param("course", 32); // 赛程 + $param['group'] = $this->request->param("group"); // 分组 + $param['times'] = $this->request->param("times"); // 成绩 + $param['achievement'] = $this->request->param("achievement"); // 成绩 + $param['integral'] = $this->request->param("integral", 0); // 积分 + $param['fly_num'] = $this->request->param("fly_num"); // 飞行圈数 + $param['is_finals'] = $this->request->param("is_finals", 0); // 是否是决赛 + $param['finals_round'] = $this->request->param("finals_round"); // 决赛轮次 + $param['player_name'] = $this->request->param("player_name"); // 选手姓名 + $param['led_color'] = $this->request->param("led_color"); // 选手姓名 + $param['channel'] = $this->request->param("channel"); // 选手姓名 + $param['other_round'] = $this->request->param("other_round"); // 选手姓名 + $param['first_fly_time'] = $this->request->param("first_fly_time"); // 选手姓名 + $param['second_fly_time'] = $this->request->param("second_fly_time"); // 选手姓名 + $param['third_fly_time'] = $this->request->param("third_fly_time"); // 选手姓名 + + // $param = $this->request->param(); + // var_dump($param);exit; + $res = $this->leagueService->setPlayerScore($param); + + if (!empty($res)) { + $result['code'] = 1; + $result['data'] = []; + $result['msg'] = "成功"; + } else { + $result['code'] = 0; + $result['data'] = []; + $result['msg'] = "失败"; + } + + return json($result); + } + + private function getAllSeconds($seconds){ + // var_dump($seconds); + $lastTwoDigits = substr((string)$seconds, -3); + $newStr = substr((string)$seconds, 0, -3); + // var_dump(floor($newStr / 60));exit; + $minutes = floor($newStr / 60); + $remainingSeconds = $newStr % 60; + $res = sprintf("%02d:%02d", $minutes, $remainingSeconds); + // var_dump($res.'.'.$lastTwoDigits);exit; + return $res.'.'.$lastTwoDigits; + } +} \ No newline at end of file diff --git a/application/admin/controller/cms/Leaguemaster.php b/application/admin/controller/cms/Leaguemaster.php new file mode 100644 index 0000000..b0c6dbe --- /dev/null +++ b/application/admin/controller/cms/Leaguemaster.php @@ -0,0 +1,563 @@ +rankingService = new RankingService(); + $this->leagueRound = new Leagueround(); + $this->leagueService = new LeagueService(); + } + + public function export() + { + $matchranking = new MatchRanking(); + $user = new User(); + $players = new Players(); + $this->exportmatch_id = $this->request->param("match_id"); + $this->exportcourse = $this->request->param("course"); // 赛程 + $this->exportround = $this->request->param("other_round"); + $this->exportsorting = $this->request->param("sorting"); + $this->birthday = $this->request->param("birthday"); + + if (empty($this->exportmatch_id)) { + $this->error("赛事id不能为空!"); + } else if (empty($this->exportcourse)) { + $this->error("赛程不可为空!"); + } + + if($this->exportcourse == 'all'){ + $MatchRanking = new MatchRanking(); + $master_info = $MatchRanking->where(['match_id'=>$this->exportmatch_id,'course'=>104,'other_round'=>$this->exportround])->select(); + // var_dump($master_info[0]['times']);exit; + foreach($master_info as $va){ + if(empty($va['times'])){ + $this->error('比赛还未结束'); + } + } + // $LeagueMasterModel = new LeagueMasterModel(); + // $master_info = $LeagueMasterModel->where('final_type',0)->select(); + // if(empty($master_info)) $this->error('比赛还未结束'); + } + $archives = new \app\admin\model\cms\Archives(); + $PDOStatement = $archives->find($this->exportmatch_id); + if (empty($PDOStatement)) { + $this->error('导出数据为空'); + } + + if($this->exportcourse == 'all'){ + $cellTitles = [ + 'num' => '排名', + 'player_id' => '编号', + 'player_name' => '姓名', + 'gender' => '性别', + 'is_young' => '是否满足青少年组', + ]; + } + // 数据总条数 + // var_dump($row);exit; + // $total = 200; + $total = count($master_info) + 4; + if ($total <= 0) { + $this->error('导出数据为空'); + } + + switch ($this->exportround) { + case 1: + $PDOStatement->title = $PDOStatement->title."公开组最终排名"; + break; + case 2: + $PDOStatement->title = $PDOStatement->title."青少年组最终排名"; + break; + case 3: + $PDOStatement->title = $PDOStatement->title."女子组最终排名"; + break; + } + + //计算资格赛每组人数 + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => $PDOStatement->title, + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => false, + 'group_people' =>4, + ]; + if( $this->exportcourse != 'all'){ + $group_people = $this->leagueService->group_people($this->exportmatch_id, $this->exportcourse); + $params['group_people'] = $group_people; + } + + $total_commission = 0; + $config = $export->getConfig(); + + if($this->exportcourse == 'all'){ + + $result = $export->export_finalRank($params, function ($pages) use (&$total_commission, $total) { + $LeagueMasterModel = new LeagueMasterModel(); + // $master_info = $LeagueMasterModel->where('number',1)->where('final_type',$this->exportround)->find(); + // $MatchRanking = new MatchRanking(); + // $datas = $MatchRanking->where(['match_id'=>$this->exportmatch_id,'course'=>104,'other_round'=>$this->exportround])->select(); + // $LeagueIntegral = new LeagueIntegral(); + // $datas = $LeagueIntegral->where('match_id',$this->exportmatch_id)->select(); + // var_dump($this->exportround);exit; + $res = $this->final_master_rank($this->exportmatch_id,$this->exportround); + if($this->exportround == 1){ + // var_dump($this->exportround);exit; + $datas = $LeagueMasterModel->where('final_type',$this->exportround)->select(); + var_dump($datas);exit; + $other_data = $LeagueMasterModel->where('final_type',0)->select(); + + $datas = array_merge($datas,$other_data); + var_dump($datas);exit; + } + if($this->exportround == 2){ + // var_dump($this->exportround);exit; + $datas = $LeagueMasterModel->where('final_type',$this->exportround)->select(); + $other_data = $LeagueMasterModel->where('final_type',0)->where('birthday','>=',$this->birthday)->select(); + foreach ($other_data as $k =>$value){ + foreach ($datas as $val){ + if($value['player_id'] == $val['player_id']){ + unset($other_data[$k]); + } + } + } + $datas = array_merge($datas,$other_data); + // var_dump($datas);exit; + } + if($this->exportround == 3){ + $datas = $LeagueMasterModel->where('final_type',$this->exportround)->select(); + $other_data = $LeagueMasterModel->where('final_type',0)->where('gender','女')->select(); + foreach ($other_data as $k =>$value){ + foreach ($datas as $val){ + if($value['player_id'] == $val['player_id']){ + unset($other_data[$k]); + } + } + } + $datas = array_merge($datas,$other_data); + } + + + // $datas = array_merge($datas,$res); + // var_dump($datas);exit; + // $datas = $Leagueround->final_rank($this->exportmatch_id); + // var_dump($datas);exit; + $user = new User(); + $players = new Players(); + // var_dump($this->exportround);exit; + $newDatas = []; + $nums = 0; + foreach ($datas as &$reward) { + // $user_info = $user->where('member_number',$reward['player_id'])->find(); + // $player_info = $players->where('member_id',$user_info['id'])->find(); + + // if(empty($player_info['gender'])){ + // var_dump($user_info);exit; + // } + $data = [ + 'num' => ++$nums, + 'player_id' => $reward['player_id'] ? $reward['player_id'] : 0, + 'player_name' => $reward['player_name'], + 'gender' => $reward['gender'], + 'is_young' => $reward['birthday'] >= $this->birthday ? '是' : '否', + ]; + + $newDatas[] = $data; + } + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + return $newDatas; + }); + + } + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } + + //生成大师赛公开组决赛分组 + public function final_public_group(){ + $match_id = $this->request->param('match_id'); + $this->leagueRound->final_groups($match_id,104,false,1); + $array= ['code'=>200,'msg'=>'success']; + return json($array); + } + + + //生成大师赛青少年/女子决赛分组 + public function final_young_group(){ + // $array= ['code'=>200,'msg'=>'success']; + // return json($array); + $match_id = $this->request->param('match_id'); + $birthday = $this->request->param('birthday'); + $women = $this->request->param('women'); + $LeagueMasterModel = new LeagueMasterModel(); + $MatchRanking = new MatchRanking(); + // var_dump($group_info);exit; + if($women != '1'){ + $match_rank_info = $MatchRanking->where(['match_id'=>$match_id,'course'=>104,'other_round'=>2])->find(); + }else{ + $match_rank_info = $MatchRanking->where(['match_id'=>$match_id,'course'=>104,'other_round'=>3])->find(); + } + + if(!empty($match_rank_info)){ + $array= ['code'=>400,'msg'=>'已生成分组']; + return json($array); + } + $master_info = $LeagueMasterModel->where('number',5)->find(); + if(true){ + $res = $this->integral($match_id); + }else{ + $array= ['code'=>400,'msg'=>'fail']; + return json($array); + } + // var_dump($birthday);exit; + + if($women != '1'){ + $other_round = 2; + $group_info = $LeagueMasterModel->where('birthday','>=',$birthday)->limit(4)->order('number')->select(); + // var_dump($group_info);exit; + }else{ + $other_round = 3; + $group_info = $LeagueMasterModel->where('gender','女')->limit(4)->order('number')->select(); + } + + // $LeagueMasterModelModel->where('gender','女')->limit(4)->order('number')->select(); + // $young_info = $LeagueMasterModel->where('birthday','>=',$birthday)->limit(4)->order('number')->select(); + $u = 0; + for($i = 1;$i <=5 ;$i++){ + foreach ($group_info as &$b){ + if($u == 0){ + $b['grouping'] = '1'; + $b['channel'] = 'R1'; + $b['led_color'] = '红'; + } + if($u == 1){ + $b['grouping'] = '1'; + $b['channel'] = 'R2'; + $b['led_color'] = '黄'; + } + if($u == 2){ + $b['grouping'] = '1'; + $b['channel'] = 'R6'; + $b['led_color'] = '蓝'; + } + if($u == 3){ + $b['grouping'] = '1'; + $b['channel'] = 'R8'; + $b['led_color'] = '绿'; + } + $u++; + } + // var_dump($young_info);exit; + $this->insert_group($group_info,$match_id,104,$other_round); + } + $array= ['code'=>200,'msg'=>'success']; + return json($array); + // $this->success('build success'); + + } + + //生成大师赛公开组/青少年/女子决赛成绩排名 + public function final_master_rank($match_id,$other_round){ + $user = new User(); + $palyer = new Players(); + $LeagueMasterModel = new LeagueMasterModel(); + $MatchRanking = new MatchRanking(); + $LeagueMasterModel->where('match_id',$match_id)->where('final_type',$other_round)->delete(); + // var_dump($res);exit; + // $mess_arr = array('code'=>0,'message'=>'该赛事已生成过排名'); + // if($inte_exsit) return json($mess_arr); + // var_dump($other_round);exit; + $result = $MatchRanking->where(['match_id'=>$match_id,'course'=>104,'other_round'=>$other_round])->find(); + // var_dump($result);exit; + if(empty($result)) $this->error('比赛未结束1'); + // var_dump($other_round);exit; + $res1 = $this->leagueRound->group_rank_sort($match_id,104,$other_round); + + var_dump($res1);exit; + //前四名 + $i = 1; + foreach ($res1 as $v){ + $user_res = $user->where('member_number',$v['player_id'])->find(); + $player_res = $palyer->where('member_id',$user_res['id'])->find(); + // var_dump($club_res);exit; + $LeagueMasterModel->insert([ + 'match_id' => $match_id, + 'player_id' => $v['player_id'], + 'player_name' => $v['player_name'], + 'qua_rank' =>$v['qua_rank'], + 'country' =>$v['country'], + 'national_flag' =>$v['national_flag'], + 'gender' => $player_res['gender'], + 'birthday' => $player_res['birthday'], + 'number' => $i, + 'createtime' => time(), + 'final_type'=> $other_round, + ]); + $i++; + } + return true; + } + + public function integral($match_id){ + // return json($success); + // var_dump($res_sen);exit; + $user = new User(); + $palyer = new Players(); + $LeagueMasterModel = new LeagueMasterModel(); + $MatchRanking = new MatchRanking(); + // $match_id = $this->request->param('match_id'); + // $inte_exsit = $LeagueIntegral->where('match_id',$match_id)->find(); + // $res_sen = $this->leagueService->bestplayerqua($match_id,101); + // $res_sen = array_slice($res_sen,64); + // $res13 = $this->leagueRound->group_rank_sort($match_id,103,13); + // // $res = $this->extra_match($new_arr,$match_id,103); + // var_dump($res13);exit; + $res = $LeagueMasterModel->where('match_id',$match_id)->delete(); + // var_dump($res);exit; + // $mess_arr = array('code'=>0,'message'=>'该赛事已生成过排名'); + // if($inte_exsit) return json($mess_arr); + // $result = $MatchRanking->where(['match_id'=>$match_id,'course'=>104,'other_round'=>1])->find(); + // if(empty($result)) $this->error('已清空积分'); + // var_dump($result['is_extra']);exit; + + $res13 = $this->leagueRound->group_rank_sort($match_id,103,13); + $res11 = $this->leagueRound->group_rank_sort($match_id,103,11); + $res10 = $this->leagueRound->group_rank_sort($match_id,103,10); + $res8 = $this->leagueRound->group_rank_sort($match_id,103,8); + $res7 = $this->leagueRound->group_rank_sort($match_id,103,7); + $res5 = $this->leagueRound->group_rank_sort($match_id,103,5); + $res4 = $this->leagueRound->group_rank_sort($match_id,103,4); + $res3 = $this->leagueRound->group_rank_sort($match_id,103,3); + + // var_dump($res13);exit; + //5-8名 + $eve_arr = [$res13,$res11]; + $i = 5; + foreach($eve_arr as $val){ + $new_arr = []; + foreach ($val as $value) { + if(in_array($value['group_sort'],array(3,4))){ + $new_arr[] = $value; + } + } + // $res = $this->extra_match($new_arr,$match_id,103); + // exit; + foreach ($new_arr as $v){ + $user_res = $user->where('member_number',$v['player_id'])->find(); + $player_res = $palyer->where('member_id',$user_res['id'])->find(); + $LeagueMasterModel->insert([ + 'match_id' => $match_id, + 'player_id' => $v['player_id'], + 'player_name' => $v['player_name'], + 'qua_rank' =>$v['qua_rank'], + 'country' =>$v['country'], + 'national_flag' =>$v['national_flag'], + 'gender' => $player_res['gender'], + 'birthday' => $player_res['birthday'], + 'number' => $i, + 'createtime' => time(), + 'final_type' => 0, + ]); + $i++; + } + } + //9-64名 + $com_arr = [$res10,$res8,$res7,$res5,$res4,$res3]; + $i = 9; + foreach($com_arr as $val){ + $new_arr = []; + foreach ($val as $value) { + if(in_array($value['group_sort'],array(3,4))){ + $new_arr[] = $value; + } + } + $res = $this->extra_match($new_arr,$match_id,103); + // exit; + foreach ($res as $v){ + $user_res = $user->where('member_number',$v['player_id'])->find(); + $player_res = $palyer->where('member_id',$user_res['id'])->find(); + $LeagueMasterModel->insert([ + 'match_id' => $match_id, + 'player_id' => $v['player_id'], + 'player_name' => $v['player_name'], + 'qua_rank' =>$v['qua_rank'], + 'country' =>$v['country'], + 'national_flag' =>$v['national_flag'], + 'gender' => $player_res['gender'], + 'birthday' => $player_res['birthday'], + 'number' => $i, + 'createtime' => time(), + 'final_type' => 0, + ]); + $i++; + } + } + $i = 65; + //64名之后排位赛有1轮成绩的积1分 + $res_sen = $this->leagueService->bestplayerqua($match_id,101); + $res_sen = array_slice($res_sen,64); + //TODO先循环拿出fly_num=0 再查组合老数组 + foreach($res_sen as $vvv){ + + // if($vvv['fly_num'] >= 3){ + // var_dump($vvv);exit; + if($vvv['fly_num'] > 0 || $vvv['fly_num'] == 'DNF'){ + $user_res = $user->where('member_number',$vvv['info']['player_id'])->find(); + if(empty($user_res['id'])){var_dump($vvv);exit;} + $player_res = $palyer->where('member_id',$user_res['id'])->find(); + $LeagueMasterModel->insert([ + 'match_id' => $match_id, + 'player_id' => $vvv['info']['player_id'], + 'player_name' => $vvv['info']['player_name'], + 'qua_rank' =>$v['qua_rank'], + 'country' =>$v['country'], + 'national_flag' =>$v['national_flag'], + 'gender' => $player_res['gender'], + 'birthday' => $player_res['birthday'], + 'number' => $i, + 'createtime' => time(), + 'final_type' => 0, + ]); + $i++; + } + } + + + // var_dump($res);exit; + $success = array('code'=>1,'message'=>'success'); + return json($success); + } + + public function insert_group($data,$match_id,$course,$other_round){ + $matchranking = new MatchRanking(); + Db::startTrans(); + try{ + foreach ($data as $val){ + // var_dump($val);exit; + $match_rank_res = $matchranking->where([ + 'match_id' => $match_id, + 'course' => $course, + 'other_round' => $other_round, + 'player_id' => $val['player_id'], + ])->find(); + $player['match_id'] = $match_id; + $player['course'] = $course; + $player['other_round'] = $other_round; + $player['player_id'] = $val['player_id']; + $player['player_name'] = $val['player_name']; + $player['channel'] = $val['channel']; + $player['led_color'] = $val['led_color']; + $player['grouping'] = $val['grouping']; + $player['qua_rank'] = $val['qua_rank']; + $player['country'] = $val['country']; + $player['national_flag'] = $val['national_flag']; + if(empty($match_rank_res)){ + $matchranking->insert($player); + } + } + + // 提交事务 + Db::commit(); + } catch (\Exception $e) { + // 回滚事务 + Db::rollback(); + } + } + + //决赛是否有加赛 + public function is_extra(){ + $match_id = $this->request->param('match_id'); + $is_open = $this->request->param('is_open'); + $MatchRanking = new MatchRanking(); + $result = $MatchRanking->where(['match_id'=>$match_id,'course'=>104,'other_round' => 1,])->select(); + foreach ($result as $v){ + $MatchRanking->where('id', $v['id']) + ->update(['is_extra'=>$is_open]); + } + if(true){ + $data = ['code'=>1,'message'=>'success']; + return json($data); + }else{ + $data = ['code'=>0,'message'=>'fail']; + return json($data); + } + } + + public function custom_rank(){ + $MatchRanking = new MatchRanking(); + // var_dump($_POST);exit; + if(empty($_POST['data']) || empty($_POST['match_id']))$this->error('lack param'); + foreach ($_POST['data'] as $v){ + // var_dump($v);exit; + $MatchRanking->where(['match_id'=> $_POST['match_id'],'course'=>104,'other_round'=>1,'is_extra'=>1,'player_id'=>$v['player_id']]) + ->update(['custom_sorting'=>$v['custom_sorting']]); + } + $data = ['code'=>1,'message'=>'success']; + return json($data); + } + + public function find_sort($match_id,$course,$other_round){ + $res = $this->leagueRound->group_rank_sort($match_id,$course,$other_round); + foreach ($res as $value) { + if(in_array($value['group_sort'],array(3,4))){ + $new_arr[] = $value; + } + } + $res = $this->extra_match($new_arr,$match_id,$course); + //存表 差俱乐部id + } + + //加赛 + public function extra_match($data,$match_id,$course){ + $res1 = $this->find_quarank($match_id,$course); + // var_dump($res1);exit; + foreach ($res1 as $vvvv){ + foreach ($data as $va){ + if($va['player_id'] == $vvvv){ + $obj_arr[] = $va; + } + + } + } + // var_dump($obj_arr);exit; + // var_dump($obj_arr);exit; + return $obj_arr; + } + + public function find_quarank($match_id,$course){ + // $match_id = 606;$course = 101; + $res = $this->leagueService->bestplayerqua($match_id,$course); + foreach ($res as $val){ + $new_arr[] = $val['info']['player_id']; + } + // var_dump($new_arr);exit; + return $new_arr; + } + +} \ No newline at end of file diff --git a/application/admin/controller/cms/Leagueround.php b/application/admin/controller/cms/Leagueround.php new file mode 100644 index 0000000..49755fd --- /dev/null +++ b/application/admin/controller/cms/Leagueround.php @@ -0,0 +1,831 @@ +leagueService = new LeagueService(); + } + + public function final_rank($match_id){ + if(empty($match_id)) $this->error('缺少参数'); + $matchranking = new MatchRanking(); + $oldarr = $matchranking->where([ + 'match_id' => $match_id, + 'course' => 104, + 'other_round' => 1,]) + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 END,datetime_value asc,CAST(qua_rank AS UNSIGNED) + ") + ->select(); + $j = 1; + if(!empty($oldarr)){ + foreach ($oldarr as $v){ + $q = $v->toArray(); + $q['group_sort'] = $j; + $obj_arr[] = $q; + $j++; + } + } + return $obj_arr; + } + + public function final_group(){ + $match_id = $this->request->param("match_id"); + $this->final_groups($match_id,104); + } + //决赛分组 + public function final_groups($match_id,$course,$export = false,$times = 5){ + $matchranking = new MatchRanking(); + $round13 = $matchranking->where([ + 'match_id' => $match_id, + 'course' => 103, + 'other_round' => 13,]) + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 END,datetime_value asc,CAST(qua_rank AS UNSIGNED) + ") + ->select(); + $j = 1; + if(!empty($round13)){ + foreach ($round13 as $v){ + $q = $v->toArray(); + $q['group_sort'] = $j; + $arr13[] = $q; + $j++; + } + } + // var_dump($arr13);exit; + $round12 = $matchranking->where([ + 'match_id' => $match_id, + 'course' => 103, + 'other_round' => 12,]) + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 END,datetime_value asc,CAST(qua_rank AS UNSIGNED) + ") + ->select(); + if(!empty($round12)){ + foreach ($round12 as $v){ + $q = $v->toArray(); + $q['group_sort'] = $j; + $arr12[] = $q; + $j++; + } + } + // $final_arr = $this->specify_order(1,$arr1,$arr2,'','',true); + $final_arr[] = $arr13[1]; + $final_arr[] = $arr12[1]; + $final_arr[] = $arr12[0]; + $final_arr[] = $arr13[0]; + $u = 0; + for($i = 1;$i <=$times ;$i++){ + foreach ($final_arr as &$b){ + if($u == 0){ + $b['grouping'] = '1'; + $b['channel'] = 'R1'; + $b['led_color'] = '红'; + } + if($u == 1){ + $b['grouping'] = '1'; + $b['channel'] = 'R2'; + $b['led_color'] = '黄'; + } + if($u == 2){ + $b['grouping'] = '1'; + $b['channel'] = 'R6'; + $b['led_color'] = '蓝'; + } + if($u == 3){ + $b['grouping'] = '1'; + $b['channel'] = 'R8'; + $b['led_color'] = '绿'; + } + $u++; + } + // var_dump($final_arr);exit; + $this->insert_group($final_arr,$match_id,104,$i); + } + $this->success('build success'); + + + } + + public function group_round2($match_id,$course,$other_round,$export = false){ + $matchranking = new MatchRanking(); + $this->exportmatch_id = $match_id; + $this->exportcourse = $course; // 赛程 + // var_dump($other_round);exit; + + $this->other_round = $other_round; // 赛程 + if($other_round == 3 && $export === false) $other_round = 2; + + if($export === true) {$other_round = $other_round + 1;} + + if(empty($this->other_round) || empty($this->exportmatch_id) || empty($this->exportcourse)) $this->error('缺少参数'); + + $obj_arr = $this->group_rank_sort($this->exportmatch_id,$this->exportcourse,$other_round - 1); + // var_dump($obj_arr);exit; + if($export === true) return $obj_arr; + // var_dump($this->other_round);exit; + $t = 0;$h1 = 0;$h2 = 0;$h3 = 0;$h4 = 0;$g = 0; + // $group_arr = ['A','B','C','D','E','F','G','H']; + $group_arr = ['17','18','19','20','21','22','23','24']; + // if($this->other_round == 3){ + // $group_arr = ['25','26','27','28','29','30','31','32']; + // } + // var_dump($group_arr);exit; + foreach ($obj_arr as $vv){ + // if($h == 4) $t++; + if($g == 8) {$g = 0; $t++;} + $vv['grouping'] = $group_arr[$t]; + + if($vv['group_sort'] == 1){ + $arr1[$h1] = $vv; + $h1++; + } + if($vv['group_sort'] == 2){ + $arr2[$h2] = $vv; + $h2++; + } + if($vv['group_sort'] == 3){ + $arr3[$h3] = $vv; + $h3++; + } + if($vv['group_sort'] == 4){ + $arr4[$h4] = $vv; + $h4++; + } + + $g++; + } + // var_dump($arr4);exit; + // var_dump($arr2);exit; + //胜者组 + for($u = 0;$u < 16;$u++){ + if($u % 2 == 0){ + $arr2[$u]['channel'] = 'R1'; + $arr2[$u]['led_color'] = '红'; + // $arr2[$u]['grouping'] = $group_arr[$u]; + $arr1[$u]['channel'] = 'R2'; + $arr1[$u]['led_color'] = '黄'; + // $arr1[$u]['grouping'] = $group_arr[$u]; + $win_arr[] = $arr2[$u]; + $win_arr[] = $arr1[$u]; + }else{ + $arr1[$u]['channel'] = 'R6'; + $arr1[$u]['led_color'] = '蓝'; + // $arr1[$u]['grouping'] = $group_arr[$u]; + $arr2[$u]['channel'] = 'R8'; + $arr2[$u]['led_color'] = '绿'; + // $arr2[$u]['grouping'] = $group_arr[$u]; + $win_arr[] = $arr1[$u]; + $win_arr[] = $arr2[$u]; + } + } + // var_dump($arr3);exit; + //败者组 + $lose_arr = $this->specify_order(3,'','',$arr3,$arr4); + // var_dump($win_arr);exit; + + // var_dump($lose_arr);exit; + // if($this->other_round == 2) $win_or_lose = $win_arr; + // if($this->other_round == 3) $win_or_lose = $lose_arr; + // var_dump($win_or_lose);exit; + + $this->insert_group($win_arr,$this->exportmatch_id,$this->exportcourse,$this->other_round); + $this->insert_group($lose_arr,$this->exportmatch_id,$this->exportcourse,3); + + $this->success('build success'); + + } + + public function group_round6($match_id,$course,$other_round,$export = false){ + $matchranking = new MatchRanking(); + $this->exportmatch_id = $match_id; + $this->exportcourse = $course; // 赛程 + // var_dump($other_round);exit; + $other_round = 4; + $this->other_round = $other_round; // 赛程 + + if($other_round == 4){ + $calculate_win_round = $other_round - 2; + } + // $this->exportmatch_id = 606;$this->exportcourse= 103;$this->other_round = 1; + if(empty($this->other_round) || empty($this->exportmatch_id) || empty($this->exportcourse)) $this->error('缺少参数'); + + $obj_arr = $this->group_rank_sort($this->exportmatch_id,$this->exportcourse,$calculate_win_round); + // var_dump($obj_arr);exit; + if($export === true ){return $obj_arr;} + // var_dump($obj_arr);exit; + $h1 = 0;$h2 = 0; + foreach ($obj_arr as $vv){ + // var_dump($vv['group_sort']);exit; + if($vv['group_sort'] == 1){ + $arrwin1[$h1] = $vv; + $h1++; + } + if($vv['group_sort'] == 2){ + $arrwin2[$h2] = $vv; + $h2++; + } + } + + if($other_round == 4) {$fortimes = 8; $group_arr = ['45','46','47','48'];} + // var_dump($fortimes);exit; + for($u = 0,$j = 0,$h = 0;$u < $fortimes;$u++){ + + if($u % 2 == 0){ + $arrwin2[$u]['channel'] = 'R1'; + $arrwin2[$u]['led_color'] = '红'; + $arrwin2[$u]['grouping'] = $group_arr[$j]; + $arrwin1[$u]['channel'] = 'R2'; + $arrwin1[$u]['led_color'] = '黄'; + $arrwin1[$u]['grouping'] = $group_arr[$j]; + $win_arr[] = $arrwin2[$u]; + $win_arr[] = $arrwin1[$u]; + $h = $h + 0.5; + }else{ + $arrwin1[$u]['channel'] = 'R6'; + $arrwin1[$u]['led_color'] = '蓝'; + $arrwin1[$u]['grouping'] = $group_arr[$j]; + $arrwin2[$u]['channel'] = 'R8'; + $arrwin2[$u]['led_color'] = '绿'; + $arrwin2[$u]['grouping'] = $group_arr[$j]; + $win_arr[] = $arrwin1[$u]; + $win_arr[] = $arrwin2[$u]; + $h = $h + 0.5; + // var_dump($h == ($j + 1));exit; + if($h == ($j + 1)) {$j++;} + } + // var_dump($h);exit; + + } + // var_dump($win_arr);exit; + if($other_round != 13){ + $this->insert_group($win_arr,$this->exportmatch_id,$this->exportcourse,$other_round + 2); + } + $this->success('build success'); + + } + + + public function group_round4($match_id,$course,$other_round,$export = false){ + $matchranking = new MatchRanking(); + $this->exportmatch_id = $match_id; + $this->exportcourse = $course; // 赛程 + // var_dump($other_round);exit; + + $this->other_round = $other_round; // 赛程 + + if($other_round == 4){ + $calculate_round = $other_round - 1; + $calculate_win_round = $other_round - 2; + } + if($other_round == 7 || $other_round == 10 || $other_round == 13){ + $calculate_round = $other_round - 2; + $calculate_win_round = $other_round - 1; + } + if($export === true){$calculate_round = $other_round;$calculate_win_round = $other_round;} + + // $this->exportmatch_id = 606;$this->exportcourse= 103;$this->other_round = 1; + if(empty($this->other_round) || empty($this->exportmatch_id) || empty($this->exportcourse)) $this->error('缺少参数'); + + $obj_arr = $this->group_rank_sort($this->exportmatch_id,$this->exportcourse,$calculate_round); + + // if($export === true && $other_round == 3){return $obj_arr;} + // var_dump($calculate_win_round);exit; + $h1 = 0;$h2 = 0;$h3 = 0;$h4 = 0; + // $group_arr = ['A','B','C','D','E','F','G','H']; + $group_arr = ['33','34','35','36','37','38','39','40']; + foreach ($obj_arr as $vv){ + + if($vv['group_sort'] == 1){ + $arr1[$h1] = $vv; + $h1++; + } + if($vv['group_sort'] == 2){ + $arr2[$h2] = $vv; + $h2++; + } + } + + $obj_arr = $this->group_rank_sort($this->exportmatch_id,$this->exportcourse,$calculate_win_round); + // var_dump($calculate_win_round);exit; + // var_dump($obj_arr);exit; + if($export === true ){return $obj_arr;} + // var_dump($obj_arr);exit; + $h1 = 0;$h2 = 0;$h3 = 0;$h4 = 0; + foreach ($obj_arr as $vv){ + // var_dump($vv['group_sort']);exit; + if($vv['group_sort'] == 1){ + $arrwin1[$h1] = $vv; + $h1++; + } + if($vv['group_sort'] == 2){ + $arrwin2[$h2] = $vv; + $h2++; + } + if($vv['group_sort'] == 3){ + $arr3[$h3] = $vv; + $h3++; + } + if($vv['group_sort'] == 4){ + $arr4[$h4] = $vv; + $h4++; + } + } + // var_dump($arrwin1);exit; + $final_arr = $this->specify_order($this->other_round,$arr1,$arr2,$arr3,$arr4); + // var_dump($final_arr);exit; + + if($other_round == 4) {$fortimes = 8; $group_arr = ['45','46','47','48'];} + if($other_round == 7) {$fortimes = 4; $group_arr = ['55','56'];} + if($other_round == 10) {$fortimes = 2; $group_arr = ['60'];} + if($other_round == 13) {$fortimes = 1; $group_arr = ['61'];} + // var_dump($fortimes);exit; + for($u = 0,$j = 0,$h = 0;$u < $fortimes;$u++){ + + if($u % 2 == 0){ + $arrwin2[$u]['channel'] = 'R1'; + $arrwin2[$u]['led_color'] = '红'; + $arrwin2[$u]['grouping'] = $group_arr[$j]; + $arrwin1[$u]['channel'] = 'R2'; + $arrwin1[$u]['led_color'] = '黄'; + $arrwin1[$u]['grouping'] = $group_arr[$j]; + $win_arr[] = $arrwin2[$u]; + $win_arr[] = $arrwin1[$u]; + $h = $h + 0.5; + }else{ + $arrwin1[$u]['channel'] = 'R6'; + $arrwin1[$u]['led_color'] = '蓝'; + $arrwin1[$u]['grouping'] = $group_arr[$j]; + $arrwin2[$u]['channel'] = 'R8'; + $arrwin2[$u]['led_color'] = '绿'; + $arrwin2[$u]['grouping'] = $group_arr[$j]; + $win_arr[] = $arrwin1[$u]; + $win_arr[] = $arrwin2[$u]; + $h = $h + 0.5; + // var_dump($h == ($j + 1));exit; + if($h == ($j + 1)) {$j++;} + } + // var_dump($h);exit; + + } + // var_dump($win_arr);exit; + $this->insert_group($final_arr,$this->exportmatch_id,$this->exportcourse,$this->other_round); + if($other_round != 13 && $other_round != 4){ + $this->insert_group($win_arr,$this->exportmatch_id,$this->exportcourse,$other_round + 2); + } + $this->success('build success'); + + } + + + public function group_round5($match_id,$course,$other_round,$export = false){ + $matchranking = new MatchRanking(); + $this->exportmatch_id = $match_id; + $this->exportcourse = $course; // 赛程 + // var_dump($other_round);exit; + if($export === true ) {$other_round = $other_round + 1;} + + $this->other_round = $other_round; // 赛程 + // $this->exportmatch_id = 606;$this->exportcourse= 103;$this->other_round = 1; + if(empty($this->other_round) || empty($this->exportmatch_id) || empty($this->exportcourse)) $this->error('缺少参数'); + + $obj_arr = $this->group_rank_sort($this->exportmatch_id,$this->exportcourse,$other_round - 1); + + // var_dump($obj_arr);exit; + if( $export === true) {return $obj_arr;} + // var_dump($obj_arr);exit; + $h1 = 0;$h2 = 0;$h3 = 0;$h4 = 0; + // $group_arr = ['A','B','C','D','E','F','G','H']; + $group_arr = ['41','42','43','44']; + foreach ($obj_arr as $vv){ + if($vv['group_sort'] == 1){ + $arrwin1[$h1] = $vv; + $h1++; + } + if($vv['group_sort'] == 2){ + $arrwin2[$h2] = $vv; + $h2++; + } + } + + $final_arr = $this->specify_order($this->other_round,$arrwin1,$arrwin2,'',''); + // var_dump($final_arr);exit; + //插入数据 + $this->insert_group($final_arr,$this->exportmatch_id,$this->exportcourse,$this->other_round); + + $this->success('build success'); + + } + + public function specify_order($round,$arr1,$arr2,$arr3,$arr4,$is_final = false){ + if($round == 3){ + $final_arr[] = $arr4[0]; + $final_arr[] = $arr3[1]; + $final_arr[] = $arr3[2]; + $final_arr[] = $arr4[3]; + + $final_arr[] = $arr4[4]; + $final_arr[] = $arr3[5]; + $final_arr[] = $arr3[6]; + $final_arr[] = $arr4[7]; + + $final_arr[] = $arr4[1]; + $final_arr[] = $arr3[0]; + $final_arr[] = $arr3[3]; + $final_arr[] = $arr4[2]; + + $final_arr[] = $arr4[5]; + $final_arr[] = $arr3[4]; + $final_arr[] = $arr3[7]; + $final_arr[] = $arr4[6]; + + $final_arr[] = $arr4[8]; + $final_arr[] = $arr3[9]; + $final_arr[] = $arr3[10]; + $final_arr[] = $arr4[11]; + + $final_arr[] = $arr4[12]; + $final_arr[] = $arr3[13]; + $final_arr[] = $arr3[14]; + $final_arr[] = $arr4[15]; + + $final_arr[] = $arr4[9]; + $final_arr[] = $arr3[8]; + $final_arr[] = $arr3[11]; + $final_arr[] = $arr4[10]; + + $final_arr[] = $arr4[13]; + $final_arr[] = $arr3[12]; + $final_arr[] = $arr3[15]; + $final_arr[] = $arr4[14]; + // var_dump($final_arr);exit; + } + if($round == 11){ + $final_arr[] = $arr2[0]; + $final_arr[] = $arr1[0]; + $final_arr[] = $arr1[1]; + $final_arr[] = $arr2[1]; + } + if($round == 8){ + $final_arr[] = $arr2[2]; + $final_arr[] = $arr1[0]; + $final_arr[] = $arr1[1]; + $final_arr[] = $arr2[3]; + + $final_arr[] = $arr2[0]; + $final_arr[] = $arr1[2]; + $final_arr[] = $arr1[3]; + $final_arr[] = $arr2[1]; + } + if($round == 5){ + $final_arr[] = $arr2[2]; + $final_arr[] = $arr1[0]; + $final_arr[] = $arr1[1]; + $final_arr[] = $arr2[3]; + + $final_arr[] = $arr2[0]; + $final_arr[] = $arr1[2]; + $final_arr[] = $arr1[3]; + $final_arr[] = $arr2[1]; + + $final_arr[] = $arr2[6]; + $final_arr[] = $arr1[4]; + $final_arr[] = $arr1[5]; + $final_arr[] = $arr2[7]; + + $final_arr[] = $arr2[4]; + $final_arr[] = $arr1[6]; + $final_arr[] = $arr1[7]; + $final_arr[] = $arr2[5]; + } + if($round == 4){ + $final_arr[] = $arr3[0]; + $final_arr[] = $arr2[6]; + $final_arr[] = $arr1[7]; + $final_arr[] = $arr4[1]; + + $final_arr[] = $arr3[2]; + $final_arr[] = $arr2[4]; + $final_arr[] = $arr1[5]; + $final_arr[] = $arr4[3]; + + $final_arr[] = $arr3[1]; + $final_arr[] = $arr2[7]; + $final_arr[] = $arr1[6]; + $final_arr[] = $arr4[0]; + + $final_arr[] = $arr3[3]; + $final_arr[] = $arr2[5]; + $final_arr[] = $arr1[4]; + $final_arr[] = $arr4[2]; + + $final_arr[] = $arr3[4]; + $final_arr[] = $arr2[1]; + $final_arr[] = $arr1[0]; + $final_arr[] = $arr4[5]; + + $final_arr[] = $arr3[6]; + $final_arr[] = $arr2[3]; + $final_arr[] = $arr1[2]; + $final_arr[] = $arr4[7]; + + $final_arr[] = $arr3[5]; + $final_arr[] = $arr2[0]; + $final_arr[] = $arr1[1]; + $final_arr[] = $arr4[4]; + + $final_arr[] = $arr3[7]; + $final_arr[] = $arr2[2]; + $final_arr[] = $arr1[3]; + $final_arr[] = $arr4[6]; + } + if($round == 7){ + $final_arr[] = $arr3[0]; + $final_arr[] = $arr2[2]; + $final_arr[] = $arr1[3]; + $final_arr[] = $arr4[1]; + + $final_arr[] = $arr3[2]; + $final_arr[] = $arr2[0]; + $final_arr[] = $arr1[1]; + $final_arr[] = $arr4[3]; + + $final_arr[] = $arr3[1]; + $final_arr[] = $arr2[3]; + $final_arr[] = $arr1[2]; + $final_arr[] = $arr4[0]; + + $final_arr[] = $arr3[3]; + $final_arr[] = $arr2[1]; + $final_arr[] = $arr1[0]; + $final_arr[] = $arr4[2]; + } + if($round == 10){ + $final_arr[] = $arr3[0]; + $final_arr[] = $arr1[0]; + $final_arr[] = $arr2[1]; + $final_arr[] = $arr4[1]; + + $final_arr[] = $arr3[1]; + $final_arr[] = $arr1[1]; + $final_arr[] = $arr2[0]; + $final_arr[] = $arr4[0]; + } + if($round == 13){ + $final_arr[] = $arr3[0]; + $final_arr[] = $arr1[0]; + $final_arr[] = $arr2[0]; + $final_arr[] = $arr4[0]; + } + // $group_arr = ['A','B','C','D','E','F','G','H']; + $group_arr = ['1','2','3','4','5','6','7','8']; + if($round == 3){ + $group_arr = ['25','26','27','28','29','30','31','32']; + }elseif($round == 4){ + $group_arr = ['33','34','35','36','37','38','39','40']; + }elseif($round == 5){ + $group_arr = ['41','42','43','44']; + }elseif($round == 7){ + $group_arr = ['49','50','51','52']; + }elseif($round == 8){ + $group_arr = ['53','54']; + }elseif($round == 10){ + $group_arr = ['57','58']; + }elseif($round == 11){ + $group_arr = ['59']; + }elseif($round == 13){ + $group_arr = ['61']; + } + $u = 0;$j = 0; + foreach ($final_arr as &$b){ + if($u == 4){ $u = 0; $j++;} + if($u == 0){ + $b['grouping'] = $group_arr[$j]; + $b['channel'] = 'R1'; + $b['led_color'] = '红'; + } + if($u == 1){ + $b['grouping'] = $group_arr[$j]; + $b['channel'] = 'R2'; + $b['led_color'] = '黄'; + } + if($u == 2){ + $b['grouping'] = $group_arr[$j]; + $b['channel'] = 'R6'; + $b['led_color'] = '蓝'; + } + if($u == 3){ + $b['grouping'] = $group_arr[$j]; + $b['channel'] = 'R8'; + $b['led_color'] = '绿'; + } + $u++; + } + + return $final_arr; + } + + //加赛 + public function extra_match($duplicates,$obj_exist,$obj_old_arr,$match_id,$course,$obj_arr){ + $duplicateElements = array_keys($duplicates); + // var_dump($duplicateElements);exit; + $res = $this->search_array($obj_old_arr,$duplicateElements[0]); + // var_dump($res);exit; + $obj_arr1 = $obj_exist; + foreach (array_keys($res) as $vv){ + unset($obj_exist[$vv]); + } + // var_dump($obj_unset);exit; + $res1 = $this->find_quarank($match_id,$course); + $i = 0;$array = []; + // var_dump($res1);exit; + foreach ($res as $k => $vvv){ + $result = $this->search_array($res1,$obj_arr1[$k]['player_id']); + $array = $array + $result; + $i++; + } + // var_dump($array);exit; + ksort($array); + $ccc = array_keys($res); + // var_dump($array);exit; + $i = 0; + foreach ($array as $vvvv){ + foreach ($obj_arr1 as $va){ + if($va['player_id'] == $vvvv){ + $obj_exist[$ccc[$i]] = $va; + $i++; + } + + } + } + ksort($obj_exist);$j = 1; + // var_dump($obj_exist);exit; + foreach ($obj_exist as &$val){ + $val['group_sort'] = $j; + $obj_arr[] = $val; + $j++; + } + // var_dump($obj_arr);exit; + return $obj_arr; + } + + public function find_quarank($match_id,$course){ + // $match_id = 606;$course = 101; + $res = $this->leagueService->bestplayerqua($match_id,$course); + foreach ($res as $val){ + $new_arr[] = $val['info']['player_id']; + } + // var_dump($new_arr);exit; + return $new_arr; + } + + public function search_array($array, $value) { + return array_filter($array, function($item) use ($value) { + return $item == $value; + }); + } + + public function group_rank_sort($match_id,$course,$other_round){ + $matchranking = new MatchRanking(); + $all_group = $matchranking->field('grouping,times')->where([ + 'match_id' => $match_id, + 'course' => $course, + 'other_round' => $other_round,])->group('grouping')->orderRaw('CAST(grouping AS UNSIGNED)')->select(); + $new_group = []; + // if($all_group['times']) + // var_dump($all_group);exit; + foreach($all_group as $value){ + $new_group[] = ($value->toArray())['grouping']; + if(empty(($value->toArray())['times'])) { + return '缺少数据1'; + } + + } + // var_dump($new_times);exit; + $i = 0;$newarr = [];$obj_arr = []; + //组内排序 + foreach ($new_group as $val){ + // if($i == 1) {var_dump($val);exit;} + $oldarr = $matchranking->where([ + 'grouping' => $val, + 'match_id' => $match_id, + 'course' => $course, + 'other_round' => $other_round,]) + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 END + ") + ->orderRaw('datetime_value asc,CAST(qua_rank AS UNSIGNED)')->select(); + // var_dump($oldarr);exit; + $j = 1;$obj_old_arr= [];$obj_exist=[]; + if(!empty($oldarr)){ + foreach ($oldarr as $v){ + $q = $v->toArray(); + $q['group_sort'] = $j; + // $o = $q['times']; + $obj_arr[] = $q; + // $obj_exist[] = $q; + // $obj_old_arr[] = $o; + // $obj_fly_arr[] = $q['fly_num']; + $j++; + } + } + // var_dump(123);exit; + // $obj_arrs = array_count_values($obj_old_arr); + // $duplicates = array_filter($obj_arrs, function($count) { + // return $count > 1; + // }); + // var_dump($obj_arrs);exit; + // if(!empty($duplicates)){ + // // var_dump($obj_exist);exit; + // array_splice($obj_arr,-4); + // $obj_arr = $this->extra_match($duplicates,$obj_exist,$obj_old_arr,$match_id,$course,$obj_arr); + // } + + $i++; + // var_dump($newarr);exit; + } + + return $obj_arr; + } + + + public function insert_group($data,$match_id,$course,$other_round){ + $matchranking = new MatchRanking(); + Db::startTrans(); + try{ + foreach ($data as $val){ + // var_dump($val);exit; + $match_rank_res = $matchranking->where([ + 'match_id' => $match_id, + 'course' => $course, + 'other_round' => $other_round, + 'player_id' => $val['player_id'], + ])->find(); + $player['match_id'] = $match_id; + $player['course'] = $course; + $player['other_round'] = $other_round; + $player['player_id'] = $val['player_id']; + $player['player_name'] = $val['player_name']; + $player['channel'] = $val['channel']; + $player['led_color'] = $val['led_color']; + $player['grouping'] = $val['grouping']; + $player['name_short'] = $val['name_short']; + $player['qua_rank'] = $val['qua_rank']; + if($match_id = '1888'){ + $player['country'] = $val['country']; + $player['national_flag'] = $val['national_flag']; + } + + if(empty($match_rank_res)){ + $matchranking->insert($player); + } + } + + // 提交事务 + Db::commit(); + } catch (\Exception $e) { + // 回滚事务 + Db::rollback(); + } + } + +} \ No newline at end of file diff --git a/application/admin/controller/cms/Leaguesort.php b/application/admin/controller/cms/Leaguesort.php new file mode 100644 index 0000000..ed3a34d --- /dev/null +++ b/application/admin/controller/cms/Leaguesort.php @@ -0,0 +1,322 @@ +rankingService = new RankingService(); + $this->leagueRound = new Leagueround(); + $this->leagueService = new LeagueService(); + } + + public function integral(){ + // return json($success); + // var_dump($res_sen);exit; + $user = new User(); + $palyer = new Players(); + $ClubInvate = new ClubInvate(); + $LeagueIntegral = new LeagueIntegral(); + $MatchRanking = new MatchRanking(); + $LeagueClubIntegral = new LeagueClubIntegral(); + $match_id = $this->request->param('match_id'); + $inte_exsit = $LeagueIntegral->where('match_id',$match_id)->find(); + // $res_sen = $this->leagueService->bestplayerqua($match_id,101); + // $res_sen = array_slice($res_sen,64); + // $res13 = $this->leagueRound->group_rank_sort($match_id,103,13); + // // $res = $this->extra_match($new_arr,$match_id,103); + // var_dump($res13);exit; + $res = $LeagueIntegral->where('match_id',$match_id)->delete(); + $res1 = $LeagueClubIntegral->where('match_id',$match_id)->delete(); + // var_dump($res);exit; + // $mess_arr = array('code'=>0,'message'=>'该赛事已生成过排名'); + // if($inte_exsit) return json($mess_arr); + $result = $MatchRanking->where(['match_id'=>$match_id,'course'=>104,'other_round'=>1])->find(); + if(empty($result)) $this->error('已清空积分'); + // var_dump($result['is_extra']);exit; + if($result['is_extra'] == 1){ + + $res = $MatchRanking->where(['match_id'=>$match_id,'course'=>104,'other_round'=>1])->order('custom_sorting')->select(); + }else{ + // $res = $this->leagueRound->final_rank($match_id); + $res = $this->leagueService->get_final_integral_rank($match_id,104); + } + + $res13 = $this->leagueRound->group_rank_sort($match_id,103,13); + $res11 = $this->leagueRound->group_rank_sort($match_id,103,11); + $res10 = $this->leagueRound->group_rank_sort($match_id,103,10); + $res8 = $this->leagueRound->group_rank_sort($match_id,103,8); + $res7 = $this->leagueRound->group_rank_sort($match_id,103,7); + $res5 = $this->leagueRound->group_rank_sort($match_id,103,5); + $res4 = $this->leagueRound->group_rank_sort($match_id,103,4); + $res3 = $this->leagueRound->group_rank_sort($match_id,103,3); + + // var_dump($res);exit; + //前四名 + $i = 1; + foreach ($res as $v){ + $user_res = $user->where('member_number',$v['player_id'])->find(); + $player_res = $palyer->where('member_id',$user_res['id'])->find(); + $club_res = $ClubInvate->where('player_id',$player_res['id'])->where('status',6)->where('deletetime',null)->find(); + // var_dump($club_res);exit; + $LeagueIntegral->insert([ + 'match_id' => $match_id, + 'player_id' => $v['player_id'], + 'player_name' => $v['player_name'], + 'club_id' => !empty($club_res) ? $club_res['club_id'] : '', + 'club_name' => !empty($club_res) ? $club_res['club_name'] : '', + 'gender' => $player_res['gender'], + 'birthday' => $player_res['birthday'], + 'name_short' => $club_res['name_short'], + 'number' => $i, + 'grade' => $this->numtograde($i), + 'createtime' => time(), + ]); + $i++; + } + // var_dump($res);exit; + //5-8名 + $eve_arr = [$res13,$res11]; + $i = 5; + foreach($eve_arr as $val){ + $new_arr = []; + foreach ($val as $value) { + if(in_array($value['group_sort'],array(3,4))){ + $new_arr[] = $value; + } + } + // $res = $this->extra_match($new_arr,$match_id,103); + // exit; + foreach ($new_arr as $v){ + $user_res = $user->where('member_number',$v['player_id'])->find(); + $player_res = $palyer->where('member_id',$user_res['id'])->find(); + $club_res = $ClubInvate->where('player_id',$player_res['id'])->where('status',6)->where('deletetime',null)->find(); + $LeagueIntegral->insert([ + 'match_id' => $match_id, + 'player_id' => $v['player_id'], + 'player_name' => $v['player_name'], + 'club_id' => !empty($club_res) ? $club_res['club_id'] : '', + 'club_name' => !empty($club_res) ? $club_res['club_name'] : '', + 'gender' => $player_res['gender'], + 'birthday' => $player_res['birthday'], + 'name_short' => $club_res['name_short'], + 'number' => $i, + 'grade' => $this->numtograde($i), + 'createtime' => time(), + ]); + $i++; + } + } + //9-64名 + $com_arr = [$res10,$res8,$res7,$res5,$res4,$res3]; + $i = 9; + foreach($com_arr as $val){ + $new_arr = []; + foreach ($val as $value) { + if(in_array($value['group_sort'],array(3,4))){ + $new_arr[] = $value; + } + } + $res = $this->extra_match($new_arr,$match_id,103); + // exit; + foreach ($res as $v){ + $user_res = $user->where('member_number',$v['player_id'])->find(); + $player_res = $palyer->where('member_id',$user_res['id'])->find(); + $club_res = $ClubInvate->where('player_id',$player_res['id'])->where('status',6)->where('deletetime',null)->find(); + $LeagueIntegral->insert([ + 'match_id' => $match_id, + 'player_id' => $v['player_id'], + 'player_name' => $v['player_name'], + 'club_id' => !empty($club_res) ? $club_res['club_id'] : '', + 'club_name' => !empty($club_res) ? $club_res['club_name'] : '', + 'gender' => $player_res['gender'], + 'birthday' => $player_res['birthday'], + 'name_short' => $club_res['name_short'], + 'number' => $i, + 'grade' => $this->numtograde($i), + 'createtime' => time(), + ]); + $i++; + } + } + $i = 65; + //64名之后排位赛有1轮成绩的积1分 + $res_sen = $this->leagueService->bestplayerqua($match_id,101); + $res_sen = array_slice($res_sen,64); + //TODO先循环拿出fly_num=0 再查组合老数组 + foreach($res_sen as $vvv){ + + // if($vvv['fly_num'] >= 3){ + // var_dump($vvv);exit; + if($vvv['fly_num'] > 0 || $vvv['fly_num'] == 'DNF'){ + $user_res = $user->where('member_number',$vvv['info']['player_id'])->find(); + if(empty($user_res['id'])){var_dump($vvv);exit;} + $player_res = $palyer->where('member_id',$user_res['id'])->find(); + $club_res = $ClubInvate->where('player_id',$player_res['id'])->where('status',6)->where('deletetime',null)->find(); + $LeagueIntegral->insert([ + 'match_id' => $match_id, + 'player_id' => $vvv['info']['player_id'], + 'player_name' => $vvv['info']['player_name'], + 'club_id' => !empty($club_res) ? $club_res['club_id'] : '', + 'club_name' => !empty($club_res) ? $club_res['club_name'] : '', + 'gender' => $player_res['gender'], + 'birthday' => $player_res['birthday'], + 'name_short' => $club_res['name_short'], + 'number' => $i, + 'grade' => ($vvv['fly_num'] >= 3) ? 1 : 0, + 'createtime' => time(), + ]); + $i++; + } + } + + //分站赛队伍总积分 + $all_club = $LeagueIntegral->where('match_id',$match_id)->where('club_id','>',0)->group('club_id')->select(); + + foreach ($all_club as $val){ + $signle_club = $LeagueIntegral->where('match_id',$match_id)->where('club_id',$val['club_id'])->order('grade desc')->limit(4)->select(); + // var_dump($signle_club[0]['sum_grade']);exit; + $all_grade = 0; + foreach ($signle_club as $va){ + $all_grade = $va['grade'] + $all_grade; + } + $LeagueClubIntegral->insert([ + 'match_id' => $match_id, + 'club_id'=>$val['club_id'], + 'club_name'=>$val['club_name'], + 'name_short' => $val['name_short'], + 'sum_grade'=>$all_grade, + 'createtime' => time(), + ]); + } + + //分站赛队伍总积分排序 + $integral_club_res1 = $LeagueClubIntegral->where(['match_id' => $match_id])->order('sum_grade desc')->select(); + $previousValue = null;$i = 1; + foreach ( $integral_club_res1 as $val){ + if ($previousValue !== null) { + if($val['sum_grade'] != $previousValue){ + $i = $i + 1; + } + } + $LeagueClubIntegral->where('id',$val['id'])->update([ + 'number' => $i, + ]); + $previousValue = $val['sum_grade']; + } + + // var_dump($res);exit; + $success = array('code'=>1,'message'=>'success'); + return json($success); + } + public function numtograde($number){ + if($number == 1) $grade = 20; + if($number == 2) $grade = 17; + if($number == 3) $grade = 15; + if($number == 4) $grade = 13; + if($number>= 5 && $number<= 8) $grade = 12; + if($number>= 9 && $number<= 16) $grade = 10; + if($number>= 17 && $number<= 32) $grade = 8; + if($number>= 33 && $number<= 48) $grade = 6; + if($number>= 49 && $number<= 64) $grade = 4; + // if($number>= 5 && $number<= 8) $grade = 12; + return $grade; + } + + //决赛是否有加赛 + public function is_extra(){ + $match_id = $this->request->param('match_id'); + $is_open = $this->request->param('is_open'); + $MatchRanking = new MatchRanking(); + $result = $MatchRanking->where(['match_id'=>$match_id,'course'=>104,'other_round' => 1,])->select(); + foreach ($result as $v){ + $MatchRanking->where('id', $v['id']) + ->update(['is_extra'=>$is_open]); + } + if(true){ + $data = ['code'=>1,'message'=>'success']; + return json($data); + }else{ + $data = ['code'=>0,'message'=>'fail']; + return json($data); + } + } + + public function custom_rank(){ + $MatchRanking = new MatchRanking(); + // var_dump($_POST);exit; + if(empty($_POST['data']) || empty($_POST['match_id']))$this->error('lack param'); + foreach ($_POST['data'] as $v){ + // var_dump($v);exit; + $MatchRanking->where(['match_id'=> $_POST['match_id'],'course'=>104,'other_round'=>1,'is_extra'=>1,'player_id'=>$v['player_id']]) + ->update(['custom_sorting'=>$v['custom_sorting']]); + } + $data = ['code'=>1,'message'=>'success']; + return json($data); + } + + public function find_sort($match_id,$course,$other_round){ + $res = $this->leagueRound->group_rank_sort($match_id,$course,$other_round); + foreach ($res as $value) { + if(in_array($value['group_sort'],array(3,4))){ + $new_arr[] = $value; + } + } + $res = $this->extra_match($new_arr,$match_id,$course); + //存表 差俱乐部id + } + public function aaa(){ + $match_id = 606;$course = 101; + $res = $this->leagueService->bestplayerqua($match_id,$course); + var_dump($res);exit; + } + //加赛 + public function extra_match($data,$match_id,$course){ + $res1 = $this->find_quarank($match_id,$course); + // var_dump($res1);exit; + foreach ($res1 as $vvvv){ + foreach ($data as $va){ + if($va['player_id'] == $vvvv){ + $obj_arr[] = $va; + } + + } + } + // var_dump($obj_arr);exit; + // var_dump($obj_arr);exit; + return $obj_arr; + } + + public function find_quarank($match_id,$course){ + // $match_id = 606;$course = 101; + $res = $this->leagueService->bestplayerqua($match_id,$course); + foreach ($res as $val){ + $new_arr[] = $val['info']['player_id']; + } + // var_dump($new_arr);exit; + return $new_arr; + } + +} \ No newline at end of file diff --git a/application/admin/controller/cms/Leaguexport.php b/application/admin/controller/cms/Leaguexport.php new file mode 100644 index 0000000..ee27f1f --- /dev/null +++ b/application/admin/controller/cms/Leaguexport.php @@ -0,0 +1,296 @@ +leagueService = new LeagueService(); + } + + public function arr(){ + $a=array("a"=>"red","b"=>"green","c"=>"blue",'d'=>"red"); + foreach ($a as $k => $v){ + if($v == 'red') + $key[] = $k; + } + var_dump($key);exit; + // echo array_search("red",$a);exit; + // $array[0] = '王一博'; + // $array[3] = '贾乃亮'; + // $array[1] = '王梓烨'; + // $array[2] = '金传博'; + // var_dump($array); + // ksort($array); + // var_dump($array);exit; + } + + //生成分组 + public function click_group(){ + $matchranking = new MatchRanking(); + $match_id = $this->request->param("match_id"); + $course = $this->request->param("course"); // 赛程 + $other_round = $this->request->param("other_round"); // 赛程 + $result = $matchranking->where('match_id',$match_id)->where('course',103)->where('other_round',$other_round)->find(); + if(!empty($result)) {$this->error('该分组已生成过');} + if($other_round == 1) $this->click_groups($match_id,$course,$other_round); + if($other_round == 2 || $other_round == 3 ) { + $leagueround = new Leagueround(); + $leagueround->group_round2($match_id,$course,$other_round); + } + if($other_round == 6 ) { + $leagueround = new Leagueround(); + $leagueround->group_round6($match_id,$course,$other_round); + } + if($other_round == 4 || $other_round == 7 || $other_round == 10 || $other_round == 13) { + $leagueround = new Leagueround(); + $leagueround->group_round4($match_id,$course,$other_round); + } + if($other_round == 5 || $other_round == 8 || $other_round == 11) { + $leagueround = new Leagueround(); + $leagueround->group_round5($match_id,$course,$other_round); + } + + } + + //淘汰赛第一轮 + public function click_groups($match_id,$course,$other_round){ + $matchranking = new MatchRanking(); + $this->exportmatch_id = $match_id; + $this->exportcourse = $course; // 赛程 + $this->other_round = $other_round; // 赛程 + // $this->exportmatch_id = 606;$this->exportcourse= 103; + if(empty($this->other_round) || empty($this->exportmatch_id) || empty($this->exportcourse)) $this->error('缺少参数'); + $res = $this->export_group($this->exportmatch_id, $this->exportcourse); + // var_dump($res);exit; + // 启动事务 + Db::startTrans(); + try{ + foreach ($res as $val){ + // var_dump($val);exit; + $match_rank_res = $matchranking->where([ + 'match_id' => $this->exportmatch_id, + 'course' => $this->exportcourse, + 'other_round' => $this->other_round, + 'player_id' => $val['info']['player_id'], + ])->find(); + $player['match_id'] = $this->exportmatch_id; + $player['course'] = $this->exportcourse; + $player['other_round'] = $this->other_round; + $player['player_id'] = $val['info']['player_id']; + $player['player_name'] = $val['info']['player_name']; + $player['channel'] = $val['info']['channel']; + $player['led_color'] = $val['info']['led_color']; + $player['grouping'] = $val['info']['grouping']; + $player['name_short'] = $val['info']['name_short']; + $player['qua_rank'] = $val['info']['qua_rank']; + $player['country'] = $val['info']['country']; + $player['national_flag'] = $val['info']['national_flag']; + if(empty($match_rank_res)){ + $matchranking->insert($player); + } + } + + // 提交事务 + Db::commit(); + } catch (\Exception $e) { + // 回滚事务 + Db::rollback(); + } + $this->success('build success'); + } + + //导出分组 + public function export_wash() + { + $matchranking = new MatchRanking(); + $this->exportmatch_id = $this->request->param("match_id"); + $this->exportcourse = $this->request->param("course"); // 赛程 + $this->other_round = $this->request->param("other_round"); // 赛程 + // $this->exportcourse = 101; + $archives = new \app\admin\model\cms\Archives(); + $PDOStatement = $archives->find($this->exportmatch_id); + $row = $matchranking->where([ + 'match_id' =>$this->exportmatch_id, + 'course' => $this->exportcourse, + 'other_round' => $this->other_round, + ])->select(); + // var_dump($this->exportmatch_id);exit; + if (empty($PDOStatement)) { + $this->error('导出数据为空'); + } + $cellTitles = [ + 'grouping' => '组别', + 'player_id' => '编号', + 'player_name' => '姓名', + 'name_short' => '所属队伍', + 'channel' => '频道', + 'led_color' => 'LED颜色', + ]; + // 数据总条数 + $total = count($row); + if ($total <= 0) { + $this->error('导出数据为空'); + } + $which_round = $this->win_or_lose_group($this->other_round); + + switch ($this->exportcourse) { + case 103: + $PDOStatement->title = $PDOStatement->title."淘汰赛【第".$this->other_round.'轮'.$which_round['which_round'].'组】'.$which_round['status'].'分组名单'; + break; + case 104: + $PDOStatement->title = $PDOStatement->title."决赛分组名单"; + break; + + } + $export = new \addons\shopro\library\Export(); + // var_dump($PDOStatement->title);exit; + $params = [ + 'file_name' => $PDOStatement->title, + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => false, + ]; + + $total_commission = 0; + $config = $export->getConfig(); + $result = $export->export_group_wash($params, function ($pages) use (&$total_commission, $total) { + $matchranking = new MatchRanking(); + $res = $matchranking->where([ + 'match_id' =>$this->exportmatch_id, + 'course' => $this->exportcourse, + 'other_round' => $this->other_round, + ]) + ->orderRaw('CAST(grouping AS UNSIGNED),CASE led_color + WHEN "红" THEN 1 + WHEN "黄" THEN 2 + WHEN "蓝" THEN 3 + WHEN "绿" THEN 4 + ELSE 5 END')->select(); + // var_dump($res);exit; + foreach ($res as &$val){ + $val = $val->toArray(); + } + $datas = $res; + $newDatas = []; + foreach ($datas as &$reward) { + $data = [ + 'player_id' => $reward['player_id'] ? $reward['player_id'] : 0, + 'player_name' => $reward['player_name'], + 'name_short' => $reward['name_short'], + 'grouping' => $reward['grouping'], + 'channel' => $reward['channel'], + 'led_color' => $reward['led_color'], + ]; + + $newDatas[] = $data; + } + + // } + + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } + + public function win_or_lose_group($other_round){ + switch ($other_round) { + case 1: $which_round['which_round'] = '1-16';$which_round['status'] = '胜者组';break; + case 2: $which_round['which_round'] = '17-24';$which_round['status'] = '胜者组';break; + case 3: $which_round['which_round'] = '25-32';$which_round['status'] = '败者组';break; + case 4: $which_round['which_round'] = '33-40';$which_round['status'] = '败者组';break; + case 5: $which_round['which_round'] = '41-44';$which_round['status'] = '败者组';break; + case 6: $which_round['which_round'] = '45-48';$which_round['status'] = '胜者组';break; + case 7: $which_round['which_round'] = '49-52';$which_round['status'] = '败者组';break; + case 8: $which_round['which_round'] = '53-54';$which_round['status'] = '败者组';break; + case 9: $which_round['which_round'] = '55-56';$which_round['status'] = '胜者组';break; + case 10: $which_round['which_round'] = '57-58';$which_round['status'] = '败者组';break; + case 11: $which_round['which_round'] = '59';$which_round['status'] = '败者组';break; + case 12: $which_round['which_round'] = '60';$which_round['status'] = '胜者组';break; + case 13: $which_round['which_round'] = '61';$which_round['status'] = '败者组';break; + + } + return $which_round; + } + + private function taotaiorder(){ + $arr = ['0' => 1,'4'=>16,'8'=>8,'12'=>14,'16'=>4,'20'=>12,'24'=>6,'28'=>10,'32'=>9,'36'=>5,'40'=>11,'44'=>3,'48'=>13,'52'=>7,'56'=>15,'60'=>2]; + foreach ($arr as $k => $val){ + $arr[$k + 1] = $val + 16; + $arr[$k + 2] = $val + 32; + $arr[$k + 3] = $val + 48; + } + ksort($arr); + return $arr; + // var_dump($arr);exit; + } + + + + public function export_group($match_id,$course){ + // $match_id = $this->request->param("match_id"); + // $course = $this->request->param("course"); // 赛程 + // $match_id = 606;$course = 101; + $group_arr = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P']; + $group_arr = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16']; + $res_data = $this->leagueService->bestplayerqua($match_id,$course); + if(count($res_data) < 64) $this->error('资格赛人数小于64'); + // var_dump($res_data);exit; + $arr = $this->taotaiorder(); + // var_dump($arr);exit; + $i = 0; $a = 0; $b = 0; + foreach ($arr as $val){ + if($a == 4) {$a = 0;$b = $b + 1;} + $new_arr[$i] = $res_data[$val - 1]; + if($a == 0){ + $new_arr[$i]['info']['channel'] = 'R1'; + $new_arr[$i]['info']['led_color'] = '红'; + $new_arr[$i]['info']['grouping'] = $group_arr[$b]; + }elseif($a == 1){ + $new_arr[$i]['info']['channel'] = 'R2'; + $new_arr[$i]['info']['led_color'] = '黄'; + $new_arr[$i]['info']['grouping'] = $group_arr[$b]; + }elseif($a == 2){ + $new_arr[$i]['info']['channel'] = 'R6'; + $new_arr[$i]['info']['led_color'] = '蓝'; + $new_arr[$i]['info']['grouping'] = $group_arr[$b]; + }elseif($a == 3){ + $new_arr[$i]['info']['channel'] = 'R8'; + $new_arr[$i]['info']['led_color'] = '绿'; + $new_arr[$i]['info']['grouping'] = $group_arr[$b]; + } + $new_arr[$i]['info']['qua_rank'] = $val; + $i++; + $a++; + } + + return $new_arr; + // var_dump($new_arr);exit; + } + +} \ No newline at end of file diff --git a/application/admin/controller/cms/Modelx.php b/application/admin/controller/cms/Modelx.php new file mode 100644 index 0000000..125dcc1 --- /dev/null +++ b/application/admin/controller/cms/Modelx.php @@ -0,0 +1,85 @@ +model = new \app\admin\model\cms\Modelx; + } + + /** + * 复制模型 + */ + public function duplicate($ids = "") + { + $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()) { + $table = $this->request->request("table"); + $tableInfo = null; + try { + $tableInfo = \think\Db::name($table)->getTableInfo(); + } catch (\Exception $e) { + } + if ($tableInfo) { + $this->error("模型表名已经存在"); + } + Db::startTrans(); + try { + $data = [ + 'name' => $row->getData('name') . '_copy', + 'table' => $table, + 'fields' => $row->fields, + 'channeltpl' => $row->channeltpl, + 'listtpl' => $row->listtpl, + 'showtpl' => $row->showtpl, + 'setting' => $row->setting, + ]; + $modelx = \app\admin\model\cms\Modelx::create($data, true); + $fieldsList = \app\admin\model\cms\Fields::where('source', 'model')->where('source_id', $row['id'])->select(); + foreach ($fieldsList as $index => $item) { + $data = $item->toArray(); + $data['source_id'] = $modelx->id; + unset($data['id'], $data['createtime'], $data['updatetime']); + \app\admin\model\cms\Fields::create($data, true); + } + Db::commit(); + } catch (\Exception $e) { + Db::rollback(); + $this->error("复制失败,错误:" . $e->getMessage()); + } + $this->success(); + } + $this->view->assign("row", $row); + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/cms/Order.php b/application/admin/controller/cms/Order.php new file mode 100644 index 0000000..41cf937 --- /dev/null +++ b/application/admin/controller/cms/Order.php @@ -0,0 +1,65 @@ +model = new \app\admin\model\cms\Order; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + $this->relationSearch = true; + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $total = $this->model + ->with(['user', 'archives']) + ->where($where) + ->order($sort, $order) + ->count(); + $list = $this->model + ->with(['user', 'archives']) + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + foreach ($list as $item) { + $item->user->visible(['id', 'username', 'nickname', 'avatar']); + $item->archives->visible(['id', 'title', 'diyname']); + } + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + + return $this->view->fetch(); + } +} diff --git a/application/admin/controller/cms/Page.php b/application/admin/controller/cms/Page.php new file mode 100644 index 0000000..40ba7ea --- /dev/null +++ b/application/admin/controller/cms/Page.php @@ -0,0 +1,203 @@ +dataLimit = $config['pagedatalimit']; + } + + $config = get_addon_config('cms'); + $this->assignconfig('spiderRecord', intval($config['spiderrecord']?? 0)); + + $this->model = new \app\admin\model\cms\Page; + $this->view->assign("flagList", $this->model->getFlagList()); + $this->view->assign("statusList", $this->model->getStatusList()); + $this->assignconfig("flagList", $this->model->getFlagList()); + } + + /** + * 查看 + */ + public function index() + { + $typeArr = \app\admin\model\cms\Page::distinct('type')->column('type'); + $this->view->assign('typeList', $typeArr); + $this->assignconfig('typeList', $typeArr); + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + 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); + + + \app\admin\model\cms\SpiderLog::render($list, 'page'); + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $row = $this->request->post("row/a", []); + if (isset($row['parsetpl']) && $row['parsetpl']) { + $this->token(); + } + } + $values = []; + $fields = \addons\cms\library\Service::getCustomFields('page', 0, $values); + + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + return parent::add(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + if ($this->request->isPost()) { + $row = $this->request->post("row/a", []); + if (isset($row['parsetpl']) && $row['parsetpl']) { + $this->token(); + } + } + $values = \app\admin\model\cms\Page::get($ids); + if (!$values) { + $this->error(__('No Results were found')); + } + $values = $values->toArray(); + $fields = \addons\cms\library\Service::getCustomFields('page', 0, $values); + + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + return parent::edit($ids); + } + + /** + * 选择单页 + */ + public function select() + { + if (!$this->auth->check('cms/page/index')) { + Hook::listen('admin_nopermission', $this); + $this->error(__('You have no permission'), ''); + } + $typeArr = \app\admin\model\cms\Page::distinct('type')->column('type'); + $this->view->assign('typeList', $typeArr); + $this->assignconfig('typeList', $typeArr); + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $total = $this->model + ->where($where) + ->order($sort, $order) + ->count(); + + $list = $this->model + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + + $list = collection($list)->toArray(); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 动态下拉选择类型 + * @internal + */ + public function selectpage_type() + { + $list = []; + $word = (array)$this->request->request("q_word/a"); + $field = $this->request->request('showField'); + $keyValue = $this->request->request('keyValue'); + if (!$keyValue) { + if (array_filter($word)) { + foreach ($word as $k => $v) { + $list[] = ['id' => $v, $field => $v]; + } + } + $typeArr = \app\admin\model\cms\Page::column('type'); + $typeArr = array_unique($typeArr); + foreach ($typeArr as $index => $item) { + $list[] = ['id' => $item, $field => $item]; + } + } else { + $list[] = ['id' => $keyValue, $field => $keyValue]; + } + return json(['total' => count($list), 'list' => $list]); + } + + /** + * 检测元素是否可用 + * @internal + */ + public function check_element_available() + { + $id = $this->request->request('id'); + $name = $this->request->request('name'); + $value = $this->request->request('value'); + $name = substr($name, 4, -1); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if ($id) { + $this->model->where('id', '<>', $id); + } + $exist = $this->model->where($name, $value)->find(); + if ($exist) { + $this->error(__('The data already exist')); + } else { + $this->success(); + } + } +} diff --git a/application/admin/controller/cms/Ranking.php b/application/admin/controller/cms/Ranking.php new file mode 100644 index 0000000..3b842a8 --- /dev/null +++ b/application/admin/controller/cms/Ranking.php @@ -0,0 +1,776 @@ +rankingService = new RankingService(); + } + + /** + * 上传参赛选手和成绩 + * @Author:Soar + * @Time:2023/11/23 9:58 + * @return void + * @throws \PhpOffice\PhpSpreadsheet\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function addRanking() + { + $this->error("开发中..."); + $attachment = null; + //默认普通上传文件 + $file = $this->request->file('file'); + $course = $this->request->post('course'); + $match_id = $this->request->post('match_id'); + if (empty($match_id)) { + $this->error("id不能为空!"); + } + try { + $upload = new Upload($file); + $attachment = $upload->upload(); + + $rankingService = new RankingService(); + $upload_excel = $rankingService->upload_excel($attachment->url, $course, $match_id); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + } + + /** + * 上传参赛选手 + * @Author:Soar + * @Time:2023/11/23 9:58 + * @return void + * @throws \PhpOffice\PhpSpreadsheet\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function addPlayerList() + { + $Archives = new Archives(); + $attachment = null; + //默认普通上传文件 + $file = $this->request->file('file'); + $match_id = $this->request->param('match_id'); + $course = $this->request->param('course', '32'); + // var_dump($match_id);exit; + $rachives_res = $Archives->where('id',$match_id)->find(); + try { + $upload = new Upload($file); + $attachment = $upload->upload(); + + $rankingService = new RankingService(); + if($rachives_res['model_id'] == 6){ + //联赛导入 + $upload_excel = $rankingService->uploadPlayers($attachment->url, $match_id, $course); + }else{ + //公开赛 + // var_dump(123);exit; + $upload_excel = $rankingService->uploadPlayer($attachment->url, $match_id, $course); + } + + if ($upload_excel === true) { + $this->result("导入成功", 1); + } else { + $this->error("导入失败", '',$upload_excel); + } + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + + } + +//单轮导入 + public function singleRoundExport() + { + $Archives = new Archives(); + $attachment = null; + //默认普通上传文件 + $file = $this->request->file('file'); + $match_id = $this->request->param('match_id'); + $course = $this->request->param('course', '32'); + try { + $upload = new Upload($file); + $attachment = $upload->upload(); + + $rankingService = new RankingService(); + + //联赛导入 + $upload_excel = $rankingService->singleRoundExport($attachment->url, $match_id, $course); + + + if ($upload_excel === true) { + $this->result("导入成功", 1); + } else { + $this->error("导入失败", '',$upload_excel); + } + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + + } + +//大师赛单轮导入 + public function singleRoundMasterExport() + { + $Archives = new Archives(); + $attachment = null; + //默认普通上传文件 + $file = $this->request->file('file'); + $match_id = $this->request->param('match_id'); + $course = $this->request->param('course', '32'); + try { + $upload = new Upload($file); + $attachment = $upload->upload(); + + $rankingService = new RankingService(); + + //联赛导入 + $upload_excel = $rankingService->singleRoundMasterExport($attachment->url, $match_id, $course); + + + if ($upload_excel === true) { + $this->result("导入成功", 1); + } else { + $this->error("导入失败", '',$upload_excel); + } + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + + } + + /** + * 添加、更新参赛选手信息 + * @Author:Soar + * @Time:2023/11/23 9:58 + * @return \think\response\Json + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function setPlayerRanking() + { + $param['player_id'] = $this->request->param("player_id", 0); // 飞手id + $param['match_id'] = $this->request->param("match_id", 0); // 赛事id + $param['course'] = $this->request->param("course", 32); // 赛程 + $param['group'] = $this->request->param("group"); // 分组 + $param['achievement'] = $this->request->param("achievement"); // 成绩 + $param['integral'] = $this->request->param("integral", 0); // 积分 + $param['fly_num'] = $this->request->param("fly_num"); // 飞行圈数 + $param['is_finals'] = $this->request->param("is_finals", 0); // 是否是决赛 + $param['finals_round'] = $this->request->param("finals_round"); // 决赛轮次 + $param['player_name'] = $this->request->param("player_name"); // 选手姓名 + $param['led_color'] = $this->request->param("led_color"); // 选手姓名 + $param['channel'] = $this->request->param("channel"); // 选手姓名 + $param['other_round'] = $this->request->param("other_round"); // 选手姓名 + + $res = $this->rankingService->setToPlayer($param); + + if (!empty($res)) { + $result['code'] = 1; + $result['data'] = []; + $result['msg'] = "成功"; + } else { + $result['code'] = 0; + $result['data'] = []; + $result['msg'] = "失败"; + } + + return json($result); + } + + /** + * 删除指定选手信息 + * @Author:Soar + * @Time:2023/11/23 9:57 + * @return \think\response\Json + */ + public function delRanking() + { + $id = $this->request->post("id"); + + if (empty($id)) { + $this->error("id不能为空!"); + } + + $row = $this->rankingService->delRanking($id); + + if (!empty($row)) { + $res['code'] = 1; + $res['data'] = []; + $res['msg'] = "删除成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['msg'] = "删除失败"; + } + + return json($res); + } + + /** + * 根据赛事id获取所有参赛选手 + * @Author:Soar + * @Time:2023/11/20 11:00 + * @param $match_id + * @return void + */ + public function getCompetitionList() + { + $match_id = $this->request->param("match_id"); + + if (empty($match_id)) { + $this->error("赛事id不能为空!"); + } + + $row = $this->rankingService->getCompetitionList($match_id); + + if (!empty($row)) { + $res['code'] = 1; + $res['data'] = $row; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['msg'] = "数据为空"; + } + + return json($res); + + } + + /** + * 获取半决赛选手 + * @Author:Soar + * @Time:2023/11/23 10:00 + * @return \think\response\Json + */ + public function getSemifinal() + { + $match_id = $this->request->param("match_id"); + + if (empty($match_id)) { + $this->error("赛事id不能为空!"); + } + + $row = $this->rankingService->getSemifinalListForRanking($match_id); + + if (!empty($row)) { + $res['code'] = 1; + $res['data'] = $row; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['msg'] = "数据为空"; + } + + return json($res); + } + + public function customSorting() + { + $match_id = $this->request->param("match_id"); + $form_sort = $this->request->param("form_sort"); + $to_sort = $this->request->param("to_sort"); + $course = $this->request->param("course"); + + if (empty($match_id)) { + $this->error("赛事id不能为空!"); + } + + $this->rankingService->CustomSorting($form_sort, $to_sort, $match_id, $course); + + $this->result("修改成功"); + } + + public function soar() + { + print_r(4725 / 365);exit; + return $this->rankingService->getRankingSort(); + } + + /** + * 根据赛事ID和赛程清空数据 + * @Author:Soar + * @Time:2023/11/23 9:56 + * @return \think\response\Json + */ + public function clearRanking() + { + $param['match_id'] = $this->request->param("match_id"); + $param['course'] = $this->request->param("course"); // 赛程 + + if (empty($param['match_id'])) { + $this->error("赛事id不能为空!"); + } + + $row = $this->rankingService->clearRanking($param); + + if (!empty($row)) { + $res['code'] = 1; + $res['data'] = []; + $res['msg'] = "清空成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['msg'] = "清空失败"; + } + + return json($res); + } + + /** + * 根据指定赛事ID和赛程信息获取参赛选手 + * @Author:Soar + * @Time:2023/11/23 9:56 + * @return \think\response\Json + */ + public function getRankingforcourse() + { + $param['match_id'] = $this->request->param("match_id"); + $param['course'] = $this->request->param("course"); // 赛程 + $param['sorting'] = $this->request->param("sorting", false); + $param['other_round'] = $this->request->param("other_round", false); // 轮次 + + if ($param['sorting'] == "false") { + $param['sorting'] = false; + } + if (empty($param['match_id'])) { + $this->error("赛事id不能为空!"); + } + + $row = $this->rankingService->getRankingForCourse($param['match_id'], $param['course'], $param['sorting'], $param['other_round']); + + if (!empty($row)) { + $res['code'] = 1; + $res['data'] = $row; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['msg'] = "获取失败"; + } + + return json($res); + } + + public function getFinalsList() + { + $param['match_id'] = $this->request->post("match_id"); + $param['other_round'] = $this->request->post("other_round"); // 轮次 + $param['sorting'] = $this->request->post("sorting"); + + if (empty($param['match_id'])) { + $this->error("赛事id不能为空!"); + } + + $row = $this->rankingService->getFinals($param); + + if (!empty($row)) { + $res['code'] = 1; + $res['data'] = $row; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['msg'] = "数据为空"; + } + + return json($res); + } + + public function export() + { + $this->exportmatch_id = $this->request->param("match_id"); + $this->exportcourse = $this->request->param("course"); // 赛程 + $this->exportsorting = $this->request->param("sorting", false); + + if ($this->exportsorting == "false") { + $this->exportsorting = false; + } + + if (empty($this->exportmatch_id)) { + $this->error("赛事id不能为空!"); + } else if (empty($this->exportcourse)) { + $this->error("赛程不可为空!"); + } + + $row = $this->rankingService->getRankingForCourse($this->exportmatch_id, $this->exportcourse, $this->exportsorting, "最佳成绩"); + $archives = new \app\admin\model\cms\Archives(); + $PDOStatement = $archives->find($this->exportmatch_id); + if (empty($PDOStatement)) { + $this->error('导出数据为空'); + } + $cellTitles = [ + 'num' => '序号', + 'player_id' => '编号', + 'player_name' => '姓名', + 'fly_num' => '飞行圈数', + 'times' => '时间', + 'grouping' => '组别', + 'channel' => '频道', + 'led_color' => 'LED颜色', + + ]; + + // 数据总条数 + $total = count($row['item']); + if ($total <= 0) { + $this->error('导出数据为空'); + } + switch ($this->exportcourse) { + case 32: + if ($this->exportsorting == false) { + $PDOStatement->title = $PDOStatement->title."【决出前32】-未排序"; + } else { + $PDOStatement->title = $PDOStatement->title."【决出前32】-已排序"; + } + break; + case 16: + if ($this->exportsorting == false) { + $PDOStatement->title = $PDOStatement->title."【32进16】-未排序"; + } else { + $PDOStatement->title = $PDOStatement->title."【32进16】-已排序"; + } + break; + case 8: + if ($this->exportsorting == false) { + $PDOStatement->title = $PDOStatement->title."【16进8】-未排序"; + } else { + $PDOStatement->title = $PDOStatement->title."【16进8】-已排序"; + } + break; + case 4: + if ($this->exportsorting == false) { + $PDOStatement->title = $PDOStatement->title."【8进4】-未排序"; + } else { + $PDOStatement->title = $PDOStatement->title."【8进4】-已排序"; + } + break; + default: + if ($this->exportsorting == false) { + $PDOStatement->title = $PDOStatement->title."【决赛】-未排序"; + } else { + $PDOStatement->title = $PDOStatement->title."【决赛】-已排序"; + } + break; + + } + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => $PDOStatement->title, + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => false, + ]; + + $total_commission = 0; + $config = $export->getConfig(); + $result = $export->exportRanking($params, function ($pages) use (&$total_commission, $total) { + $datas = $this->rankingService->getRankingForCourse($this->exportmatch_id, $this->exportcourse, $this->exportsorting, "最佳成绩"); + $datas = collection($datas['item']); + $datas->each(function ($order) { + })->toArray(); + + $newDatas = []; + $nums = 0; + foreach ($datas as &$reward) { + $data = [ + 'num' => ++$nums, + 'player_id' => $reward['player_id'] ? $reward['player_id'] : 0, + 'player_name' => $reward['player_name'], + 'fly_num' => $reward['fly_num'], + 'times' => $reward['times'], + 'grouping' => $reward['grouping'], + 'channel' => $reward['channel'], + 'led_color' => $reward['led_color'], + ]; + + $newDatas[] = $data; + } + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } + + public function export_young() + { + $match_id = $this->request->param("match_id"); + $birthday = $this->request->param("birthday"); + if (empty($match_id)) { + $this->error("非法请求"); + } + $players = new Players(); + if (!empty($birthday)) { + // 按照生日日期获取所有参赛选手 + $players_list = $players + ->alias("a") + ->join('user users', 'users.id = a.member_id', 'LEFT') + ->where("a.birthday", ">=", $birthday) + ->field("a.id, users.member_number, a.birthday") + ->order("a.birthday") + ->select(); + } else { + // 按照性别获取所有参赛选手 + $players_list = $players + ->alias("a") + ->join('user users', 'users.id = a.member_id', 'LEFT') + ->where("a.gender", "eq", "女") + ->field("a.id, users.member_number") + ->select(); + } + + + + $ids = array_column($players_list, "id"); +// $player_ids = array_column($players_list, "member_number"); + // 获取所有参赛选手 + $matchContestant = new MatchContestant(); + // 去报名表查询所有参赛的飞手 + $match_list = $matchContestant->whereIn("player_id", $ids) + ->where("match_id", "eq", $match_id) + ->select(); + // 根据参赛选手 加上player_member_id + foreach ($match_list as $vals) { + foreach ($players_list as $values) { + if ($vals->player_id == $values->id) { + $vals->player_member_id = $values->member_number; + break; + } + } + } +// $players_list = collection($players_list)->toArray(); + + $member_number_ids = array_column($match_list, "player_member_id"); + // 根据User表中的 member_number 获取比赛信息 权重 决赛 > 半决赛 > 8强 > 16强 > 32强 > 预选赛 + // 决赛数据 + $rankingService = new \app\index\service\RankingService(); + $achievement['finals'] = $rankingService->getRankFinals($match_id); + + + if (empty($achievement['finals'])) { + $this->error("决赛暂未结束"); + } + unset($achievement['finals']['integral_list']); + // 遍历查询是否有 + foreach ($achievement['finals'] as $key => $item) { + if (!in_array($item->player_id, $member_number_ids)) { + unset($achievement['finals'][$key]); + } else { + unset($member_number_ids[array_search($item->player_id, $member_number_ids)]); + } + } + + + + // 初始化model + $matchRanking = new MatchRanking(); + // 查询 进入半决赛的成绩 + $achievement['semifinal'] = $matchRanking->whereIn("player_id", $member_number_ids) + ->where("match_id", "eq", $match_id) + ->where("bast_performance", 1) + ->where("course", "eq", 4) + ->order("group_score") + ->select(); + + foreach ($achievement['semifinal'] as $val) { + unset($member_number_ids[array_search($val->player_id, $member_number_ids)]); + } + + // 查询 进入8强的成绩 + $achievement['final_eight'] = $matchRanking->whereIn("player_id", $member_number_ids) + ->where("match_id", "eq", $match_id) + ->where("bast_performance", 1) + ->where("course", "eq", 8) + ->order("group_score") + ->select(); + + foreach ($achievement['final_eight'] as $val) { + unset($member_number_ids[array_search($val->player_id, $member_number_ids)]); + } + + // 查询 进入16强数据 + $achievement['final_sixteen'] = $matchRanking->whereIn("player_id", $member_number_ids) + ->where("match_id", "eq", $match_id) + ->where("bast_performance", 1) + ->where("course", "eq", 16) + ->order("group_score") + ->select(); + foreach ($achievement['final_sixteen'] as $val) { + unset($member_number_ids[array_search($val->player_id, $member_number_ids)]); + } + // 查询 进入32强数据 + $achievement['final_thirty_two'] = $matchRanking->whereIn("player_id", $member_number_ids) + ->where("match_id", "eq", $match_id) + ->where("bast_performance", 1) + ->where("course", "eq", 32) + ->order("group_score") + ->select(); + + foreach ($achievement['final_thirty_two'] as $val) { + unset($member_number_ids[array_search($val->player_id, $member_number_ids)]); + } + + if (!empty($member_number_ids)) { + $user = new User(); + $keys = 0; + foreach ($member_number_ids as $val) { + $user_info = $user->with("players")->where("member_number", $val)->find(); + $user_info = $user_info->toArray(); + + $achievement['other_list'][$keys]['match_id'] = $match_id; + $achievement['other_list'][$keys]['player_name'] = $user_info['players']['real_name']; + $achievement['other_list'][$keys]['fly_num'] = null; + $achievement['other_list'][$keys]['times'] = null; + $achievement['other_list'][$keys]['grouping'] = null; + $achievement['other_list'][$keys]['course'] = "DNF"; + $achievement['other_list'][$keys]['player_id'] = $val; + + $keys++; + } + } + + $this->achievement = $achievement; + // 数据总条数 + $total = count($achievement['semifinal']) + count($achievement['final_eight']) + + count($achievement['final_sixteen']) + count($achievement['final_thirty_two']) + count($achievement['finals']); + if ($total <= 0) { + $this->error('导出数据为空'); + } + + $cellTitles = [ + 'num' => '序号', + 'player_id' => '编号', + 'player_name' => '姓名', + 'course' => '赛程', + 'times' => '最佳成绩', + 'fly_num' => '飞行圈数', + + ]; + $archives = new \app\admin\model\cms\Archives(); + $PDOStatement = $archives->find($match_id); + $export = new \addons\shopro\library\Export(); + if (empty($birthday)) { + $params = [ + 'file_name' => $PDOStatement->title."《女子组排名》", + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => false, + ]; + } else { + $params = [ + 'file_name' => $PDOStatement->title."《青少年组排名》", + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => false, + ]; + } + + + $total_commission = 0; + $config = $export->getConfig(); + $result = $export->exportRankingYoung($params, function ($pages) use (&$total_commission, $total) { + $datas = $this->achievement; + + + $newDatas = []; + $nums = 0; + foreach ($datas as $vals) { + if (!is_array($vals)) { + $datas_info = collection($vals); + $datas_info->each(function ($order) { + })->toArray(); + } else { + $datas_info = $vals; + } + + foreach ($datas_info as $reward) { + + if (!empty($reward->achievement_list)) { + $data = [ + 'num' => ++$nums, + 'player_id' => $reward['player_id'], + 'player_name' => $reward['player_name'], + 'course' => "决赛", + 'times' => $reward['times'], + 'fly_num' => $reward['fly_num'], + ]; + } else { + $data = [ + 'num' => ++$nums, + 'player_id' => $reward['player_id'], + 'player_name' => $reward['player_name'], + 'course' => $reward['course'], + 'times' => $reward['times'] ? $reward['times'] : "DNF", + 'fly_num' => $reward['fly_num'] ? $reward['fly_num'] : "DNF" + ]; + } + + if ($data['course'] == 4) { + $data['course'] = "8进4"; + } elseif ($data['course'] == 8) { + $data['course'] = "16进8"; + } elseif ($data['course'] == 16) { + $data['course'] = "32进16"; + } elseif ($data['course'] == 32) { + $data['course'] = "排位赛"; + } else { + if ($data['course'] != "决赛") { + $data['course'] = $reward['course']; + } + } + $newDatas[] = $data; + + + } + } + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + + } + + public function update_sort() + { + $this->rankingService->testRanking(); + } +} \ No newline at end of file diff --git a/application/admin/controller/cms/SearchLog.php b/application/admin/controller/cms/SearchLog.php new file mode 100644 index 0000000..880a0ba --- /dev/null +++ b/application/admin/controller/cms/SearchLog.php @@ -0,0 +1,35 @@ +model = new \app\admin\model\cms\SearchLog; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + +} diff --git a/application/admin/controller/cms/Special.php b/application/admin/controller/cms/Special.php new file mode 100644 index 0000000..4c0ed1e --- /dev/null +++ b/application/admin/controller/cms/Special.php @@ -0,0 +1,123 @@ +dataLimit = $config['specialdatalimit']; + } + + $config = get_addon_config('cms'); + $this->assignconfig('spiderRecord', intval($config['spiderrecord']?? 0)); + + $this->model = new \app\admin\model\cms\Special; + $this->view->assign("flagList", $this->model->getFlagList()); + $this->view->assign("statusList", $this->model->getStatusList()); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + 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); + + + \app\admin\model\cms\SpiderLog::render($list, 'special'); + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + $values = []; + $fields = \addons\cms\library\Service::getCustomFields('special', 0, $values); + + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + return parent::add(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + $values = \app\admin\model\cms\Special::get($ids); + if (!$values) { + $this->error(__('No Results were found')); + } + $values = $values->toArray(); + $fields = \addons\cms\library\Service::getCustomFields('special', 0, $values); + + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + return parent::edit($ids); + } + + /** + * 检测元素是否可用 + * @internal + */ + public function check_element_available() + { + $id = $this->request->request('id'); + $name = $this->request->request('name'); + $value = $this->request->request('value'); + $name = substr($name, 4, -1); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if ($id) { + $this->model->where('id', '<>', $id); + } + $exist = $this->model->where($name, $value)->find(); + if ($exist) { + $this->error(__('The data already exist')); + } else { + $this->success(); + } + } + +} diff --git a/application/admin/controller/cms/SpiderLog.php b/application/admin/controller/cms/SpiderLog.php new file mode 100644 index 0000000..c850335 --- /dev/null +++ b/application/admin/controller/cms/SpiderLog.php @@ -0,0 +1,41 @@ +model = new \app\admin\model\cms\SpiderLog; + $this->view->assign("spiderRecord", intval($config['spiderrecord']?? 0)); + $this->view->assign("typeList", $this->model->getTypeList()); + $this->view->assign("nameList", $config['spiders'] ?? []); + $this->assignconfig("nameList", $config['spiders'] ?? []); + } + + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + + +} diff --git a/application/admin/controller/cms/Statistics.php b/application/admin/controller/cms/Statistics.php new file mode 100644 index 0000000..81936c5 --- /dev/null +++ b/application/admin/controller/cms/Statistics.php @@ -0,0 +1,402 @@ +request->isPost()) { + $date = $this->request->post('date', ''); + $type = $this->request->post('type', ''); + if ($type == 'sale') { + list($orderSaleCategory, $orderSaleAmount, $orderSaleNums) = $this->getSaleStatisticsData($date); + $statistics = ['orderSaleCategory' => $orderSaleCategory, 'orderSaleAmount' => $orderSaleAmount, 'orderSaleNums' => $orderSaleNums]; + } elseif ($type == 'percent') { + list($orderPercentCategory, $orderPercentAmount, $orderPercentNums) = $this->getPercentStatisticsData($date); + $statistics = ['orderPercentCategory' => $orderPercentCategory, 'orderPercentAmount' => $orderPercentAmount, 'orderPercentNums' => $orderPercentNums]; + } elseif ($type == 'order') { + list($category, $data) = $this->getOrderStatisticsData($date); + $statistics = ['category' => $category, 'data' => $data]; + } elseif ($type == 'archives') { + list($category, $data) = $this->getArchivesStatisticsData($date); + $statistics = ['category' => $category, 'data' => $data]; + } + $this->success('', '', $statistics); + } + + //管理员发文统计图表 + list($category, $data) = $this->getArchivesStatisticsData(''); + $this->assignconfig('adminArchivesListCategory', $category); + $this->assignconfig('adminArchivesListData', $data); + + //今日订单和会员 + $totalOrderAmount = round(\app\admin\model\cms\Order::where('status', 'paid')->sum('payamount'), 2); + $yesterdayOrderAmount = round(\app\admin\model\cms\Order::where('status', 'paid')->whereTime('paytime', 'yesterday')->sum('payamount'), 2); + $todayOrderAmount = round(\app\admin\model\cms\Order::where('status', 'paid')->whereTime('paytime', 'today')->sum('payamount'), 2); + $todayOrderRatio = $yesterdayOrderAmount > 0 ? ceil((($todayOrderAmount - $yesterdayOrderAmount) / $yesterdayOrderAmount) * 100) : ($todayOrderAmount > 0 ? 100 : 0); + + $totalUser = User::count(); + $yesterdayUser = User::whereTime('jointime', 'yesterday')->count(); + $todayUser = User::whereTime('jointime', 'today')->count(); + $todayUserRatio = $yesterdayUser > 0 ? ceil((($todayUser - $yesterdayUser) / $yesterdayUser) * 100) : ($todayUser > 0 ? 100 : 0); + + //文档和评论统计 + $totalArchives = \app\admin\model\cms\Archives::count(); + $unsettleArchives = \app\admin\model\cms\Archives::where('status', 'hidden')->count(); + $totalComment = \app\admin\model\cms\Comment::count(); + $unsettleComment = \app\admin\model\cms\Comment::where('status', 'hidden')->count(); + $diyformList = \app\admin\model\cms\Diyform::all(); + foreach ($diyformList as $index => $item) { + $item->nums = \think\Db::name($item['table'])->count(); + } + + //订单数和订单额统计 + list($orderSaleCategory, $orderSaleAmount, $orderSaleNums) = $this->getSaleStatisticsData(); + $this->assignconfig('orderSaleCategory', $orderSaleCategory); + $this->assignconfig('orderSaleAmount', $orderSaleAmount); + $this->assignconfig('orderSaleNums', $orderSaleNums); + + //订单占比统计 + list($orderPercentCategory, $orderPercentAmount, $orderPercentNums) = $this->getPercentStatisticsData(); + $this->assignconfig('orderPercentCategory', $orderPercentCategory); + $this->assignconfig('orderPercentAmount', $orderPercentAmount); + $this->assignconfig('orderPercentNums', $orderPercentNums); + + //热门标签 + $tagList = \app\admin\model\cms\Tag::order('nums', 'desc')->limit(10)->select(); + $tagsTotal = 0; + foreach ($tagList as $index => $item) { + $tagsTotal += $item['nums']; + } + foreach ($tagList as $index => $item) { + $item['percent'] = $tagsTotal > 0 ? round($item['nums'] / $tagsTotal * 100, 2) : 0; + } + + //热门搜索列表 + $hotSearchList = SearchLog::order('nums', 'desc')->cache(3600)->limit(10)->select(); + $hotTagList = \addons\cms\model\Tag::order('nums', 'desc')->cache(3600)->limit(10)->select(); + $hotArchivesList = \addons\cms\model\Archives::order('views', 'desc')->cache(3600)->limit(10)->select(); + + //付费排行榜 + $todayPaidTotal = \app\admin\model\cms\Order::whereTime('paytime', 'today')->sum("payamount"); + $todayPaidList = \app\admin\model\cms\Order::with(['archives'])->whereTime('paytime', 'today')->group('archives_id')->field("COUNT(*) as nums,SUM(payamount) as amount,archives_id")->order("amount", "desc")->limit(10)->select(); + foreach ($todayPaidList as $index => $item) { + $item->percent = $todayPaidTotal > 0 ? round(($item['amount'] / $todayPaidTotal) * 100, 2) : 0; + } + + $weekPaidTotal = \app\admin\model\cms\Order::whereTime('paytime', 'week')->sum("payamount"); + $weekPaidList = \app\admin\model\cms\Order::with(['archives'])->whereTime('paytime', 'week')->group('archives_id')->field("COUNT(*) as nums,SUM(payamount) as amount,archives_id")->order("amount", "desc")->limit(10)->select(); + foreach ($weekPaidList as $index => $item) { + $item->percent = $weekPaidTotal > 0 ? round(($item['amount'] / $weekPaidTotal) * 100, 2) : 0; + } + + $monthPaidTotal = \app\admin\model\cms\Order::whereTime('paytime', 'month')->sum("payamount"); + $monthPaidList = \app\admin\model\cms\Order::with(['archives'])->whereTime('paytime', 'month')->group('archives_id')->field("COUNT(*) as nums,SUM(payamount) as amount,archives_id")->order("amount", "desc")->limit(10)->select(); + foreach ($monthPaidList as $index => $item) { + $item->percent = $monthPaidTotal > 0 ? round(($item['amount'] / $monthPaidTotal) * 100, 2) : 0; + } + + //投稿排行榜 + $todayContributeTotal = \app\admin\model\cms\Archives::whereTime('createtime', 'today')->count(); + $todayContributeList = \app\admin\model\cms\Archives::with(['user'])->where('user_id', '>', 0)->whereTime('createtime', 'today')->group('user_id')->field("COUNT(*) as nums,user_id")->order("nums", "desc")->limit(10)->select(); + foreach ($todayContributeList as $index => $item) { + $item->percent = $todayContributeTotal > 0 ? round(($item['nums'] / $todayContributeTotal) * 100, 2) : 0; + } + + $weekContributeTotal = \app\admin\model\cms\Archives::whereTime('createtime', 'week')->count(); + $weekContributeList = \app\admin\model\cms\Archives::with(['user'])->where('user_id', '>', 0)->whereTime('createtime', 'week')->group('user_id')->field("COUNT(*) as nums,user_id")->order("nums", "desc")->limit(10)->select(); + foreach ($weekContributeList as $index => $item) { + $item->percent = $weekContributeTotal > 0 ? round(($item['nums'] / $weekContributeTotal) * 100, 2) : 0; + } + + $monthContributeTotal = \app\admin\model\cms\Archives::whereTime('createtime', 'month')->count(); + $monthContributeList = \app\admin\model\cms\Archives::with(['user'])->where('user_id', '>', 0)->whereTime('createtime', 'month')->group('user_id')->field("COUNT(*) as nums,user_id")->order("nums", "desc")->limit(10)->select(); + foreach ($monthContributeList as $index => $item) { + $item->percent = $monthContributeTotal > 0 ? round(($item['nums'] / $monthContributeTotal) * 100, 2) : 0; + } + + $this->view->assign("totalOrderAmount", $totalOrderAmount); + $this->view->assign("yesterdayOrderAmount", $yesterdayOrderAmount); + $this->view->assign("todayOrderAmount", $todayOrderAmount); + $this->view->assign("todayOrderRatio", $todayOrderRatio); + + $this->view->assign("totalUser", $totalUser); + $this->view->assign("yesterdayUser", $yesterdayUser); + $this->view->assign("todayUser", $todayUser); + $this->view->assign("todayUserRatio", $todayUserRatio); + + $this->view->assign("totalArchives", $totalArchives); + $this->view->assign("unsettleArchives", $unsettleArchives); + $this->view->assign("totalComment", $totalComment); + $this->view->assign("unsettleComment", $unsettleComment); + + $this->view->assign("tagsList", $tagList); + $this->view->assign("hotTagList", $hotTagList); + $this->view->assign("hotArchivesList", $hotArchivesList); + $this->view->assign("hotSearchList", $hotSearchList); + + $this->view->assign("todayPaidList", $todayPaidList); + $this->view->assign("weekPaidList", $weekPaidList); + $this->view->assign("monthPaidList", $monthPaidList); + + $this->view->assign("todayContributeList", $todayContributeList); + $this->view->assign("weekContributeList", $weekContributeList); + $this->view->assign("monthContributeList", $monthContributeList); + + $this->view->assign("modelList", \app\admin\model\cms\Modelx::order('id asc')->select()); + + return $this->view->fetch(); + } + + /** + * 获取订单销量销售额统计数据 + * @param string $date + * @return array + */ + protected function getSaleStatisticsData($date = '') + { + $starttime = \fast\Date::unixtime(); + $endtime = \fast\Date::unixtime('day', 0, 'end'); + + $format = '%H:00'; + + $orderList = \app\admin\model\cms\Order::where('paytime', 'between time', [$starttime, $endtime]) + ->field('paytime, status, COUNT(*) AS nums, SUM(payamount) AS amount, MIN(paytime) AS min_paytime, MAX(paytime) AS max_paytime, + DATE_FORMAT(FROM_UNIXTIME(paytime), "' . $format . '") AS paydate') + ->group('paydate') + ->select(); + $column = []; + for ($time = $starttime; $time <= $endtime;) { + $column[] = date("H:00", $time); + $time += 3600; + } + $orderSaleNums = $orderSaleAmount = array_fill_keys($column, 0); + foreach ($orderList as $k => $v) { + $orderSaleNums[$v['paydate']] = $v['nums']; + $orderSaleAmount[$v['paydate']] = round($v['amount'], 2); + } + $orderSaleCategory = array_keys($orderSaleAmount); + $orderSaleAmount = array_values($orderSaleAmount); + $orderSaleNums = array_values($orderSaleNums); + return [$orderSaleCategory, $orderSaleAmount, $orderSaleNums]; + } + + /** + * 获取订单占比统计数据 + * @param string $date + * @return array + */ + protected function getPercentStatisticsData($date = '') + { + $starttime = \fast\Date::unixtime(); + $endtime = \fast\Date::unixtime('day', 0, 'end'); + $modelList = []; + $orderPercentCategory = $orderPercentAmount = $orderPercentNums = []; + $list = \app\admin\model\cms\Order::with('archives') + ->where('order.createtime', 'between time', [$starttime, $endtime]) + ->where('order.status', 'paid') + ->field("archives.model_id,SUM(payamount) as amount,COUNT(*) as nums") + ->group('archives.model_id') + ->select(); + foreach ($list as $index => $item) { + $modelList[$item['archives']['model']['name']] = $item['amount']; + $name = $item['archives']['model']['name']; + $name = $name ? $name : "其它"; + $orderPercentCategory[] = $name; + $orderPercentAmount[] = ['value' => round($item['amount'], 2), 'name' => $name]; + $orderPercentNums[] = ['value' => $item['nums'], 'name' => $name]; + } + if (!$orderPercentCategory) { + $orderPercentCategory = [""]; + $orderPercentNums = [['value' => 0, 'name' => '订单数']]; + $orderPercentAmount = [['value' => 0, 'name' => '订单额']]; + } + return [$orderPercentCategory, $orderPercentAmount, $orderPercentNums]; + } + + /** + * 获取订单统计数据 + * @param string $date + * @return array + */ + protected function getOrderStatisticsData($date = '') + { + if ($date) { + list($start, $end) = explode(' - ', $date); + + $starttime = strtotime($start); + $endtime = strtotime($end); + } else { + $starttime = \fast\Date::unixtime('day', 0, 'begin'); + $endtime = \fast\Date::unixtime('day', 0, 'end'); + } + $totalseconds = $endtime - $starttime; + + $format = '%Y-%m-%d'; + if ($totalseconds > 86400 * 30 * 2) { + $format = '%Y-%m'; + } else { + if ($totalseconds > 86400) { + $format = '%Y-%m-%d'; + } else { + $format = '%H:00'; + } + } + + $model_id = $this->request->post("model_id"); + $orderList = \app\admin\model\cms\Order::where('paytime', 'between time', [$starttime, $endtime]) + ->where(function ($query) use ($model_id, $starttime, $endtime) { + if ($model_id) { + //如果指定模型 + $query->where('archives_id', 'in', function ($query) use ($model_id, $starttime, $endtime) { + $query->name("cms_archives")->where('model_id', $model_id)->where('id', 'in', function ($query) use ($starttime, $endtime) { + $query->name('cms_order')->where('paytime', 'between time', [$starttime, $endtime])->field('archives_id'); + })->field('id'); + }); + } + }) + ->field('paytime, status, SUM(payamount) AS amount, MIN(paytime) AS min_paytime, MAX(paytime) AS max_paytime, + DATE_FORMAT(FROM_UNIXTIME(createtime), "' . $format . '") AS pay_date') + ->group('pay_date') + ->select(); + + if ($totalseconds > 84600 * 30 * 2) { + $starttime = strtotime('last month', $starttime); + while (($starttime = strtotime('next month', $starttime)) <= $endtime) { + $column[] = date('Y-m', $starttime); + } + } else { + if ($totalseconds > 86400) { + for ($time = $starttime; $time <= $endtime;) { + $column[] = date("Y-m-d", $time); + $time += 86400; + } + } else { + for ($time = $starttime; $time <= $endtime;) { + $column[] = date("H:00", $time); + $time += 3600; + } + } + } + $list = array_fill_keys($column, 0); + foreach ($orderList as $k => $v) { + $list[$v['pay_date']] = round($v['amount'], 2); + } + $category = array_keys($list); + $data = array_values($list); + return [$category, $data]; + + } + + /** + * 获取发文统计数据 + * @param string $date + * @return array + */ + protected function getArchivesStatisticsData($date = '') + { + if ($date) { + list($start, $end) = explode(' - ', $date); + + $starttime = strtotime($start); + $endtime = strtotime($end); + } else { + $starttime = \fast\Date::unixtime('day', 0, 'begin'); + $endtime = \fast\Date::unixtime('day', 0, 'end'); + } + $totalseconds = $endtime - $starttime; + + $format = '%Y-%m-%d'; + if ($totalseconds > 86400 * 30 * 2) { + $format = '%Y-%m'; + } else { + if ($totalseconds > 86400) { + $format = '%Y-%m-%d'; + } else { + $format = '%H:00'; + } + } + $model_id = $this->request->post("model_id", ""); + $archivesList = \app\admin\model\cms\Archives::with(["admin"]) + ->where('createtime', 'between time', [$starttime, $endtime]) + ->where(function ($query) use ($model_id) { + if ($model_id) { + $query->where('model_id', $model_id); + } + }) + ->field('admin_id, createtime, status, COUNT(*) AS nums, MIN(createtime) AS min_createtime, MAX(createtime) AS max_createtime, + DATE_FORMAT(FROM_UNIXTIME(createtime), "' . $format . '") AS create_date') + ->group('admin_id,create_date') + ->select(); + + if ($totalseconds > 84600 * 30 * 2) { + $starttime = strtotime('last month', $starttime); + while (($starttime = strtotime('next month', $starttime)) <= $endtime) { + $column[] = date('Y-m', $starttime); + } + } else { + if ($totalseconds > 86400) { + for ($time = $starttime; $time <= $endtime;) { + $column[] = date("Y-m-d", $time); + $time += 86400; + } + } else { + for ($time = $starttime; $time <= $endtime;) { + $column[] = date("H:00", $time); + $time += 3600; + } + } + } + $list = []; + $dataList = []; + $columnList = array_fill_keys($column, 0); + foreach ($archivesList as $k => $v) { + $nickname = $v->admin ? $v->admin->nickname : "未知"; + if (!isset($list[$nickname])) { + $list[$nickname] = $columnList; + } + $list[$nickname][$v['create_date']] = $v['nums']; + } + foreach ($list as $index => $item) { + $dataList[] = [ + 'name' => $index, + 'type' => 'line', + 'smooth' => true, + 'areaStyle' => [], + 'data' => array_values($item) + ]; + } + $columnList = array_keys($columnList); + return [$columnList, $dataList]; + + } + +} diff --git a/application/admin/controller/cms/Tag.php b/application/admin/controller/cms/Tag.php new file mode 100644 index 0000000..cc61b4d --- /dev/null +++ b/application/admin/controller/cms/Tag.php @@ -0,0 +1,90 @@ +model = new \app\admin\model\cms\Tag; + + $config = get_addon_config('cms'); + $this->assignconfig('spiderRecord', intval($config['spiderrecord'] ?? 0)); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + 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); + + + \app\admin\model\cms\SpiderLog::render($list, 'tag'); + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + public function selectpage() + { + $response = parent::selectpage(); + $word = (array)$this->request->request("q_word/a"); + if (array_filter($word)) { + $result = $response->getData(); + $list = []; + foreach ($result['list'] as $index => $item) { + $list[] = strtolower($item['name']); + } + foreach ($word as $k => $v) { + if (!in_array(strtolower($v), $list)) { + array_unshift($result['list'], ['id' => $v, 'name' => $v]); + } + $result['total']++; + } + $response->data($result); + } + return $response; + } + + public function autocomplete() + { + $q = $this->request->request('q'); + $list = \app\admin\model\cms\Tag::where('name', 'like', '%' . $q . '%')->column('name'); + echo json_encode($list); + return; + } + +} diff --git a/application/admin/controller/fastim/Config.php b/application/admin/controller/fastim/Config.php new file mode 100644 index 0000000..f235c2a --- /dev/null +++ b/application/admin/controller/fastim/Config.php @@ -0,0 +1,65 @@ +model = new ConfigModel; + } + + /* + * 查看 + */ + public function index() + { + $this->view->assign('configList', $this->model::getConfigListByGroup(null, 0)); + return $this->view->fetch(); + } + + /* + * 保存修改 + */ + public function update() + { + if ($this->request->isPost()) { + $this->token(); + $row = $this->request->post("row/a", [], 'trim'); + + if ($row) { + $configList = []; + foreach ($this->model->all() as $v) { + if (isset($row[$v['name']])) { + $value = $row[$v['name']]; + if (is_array($value) && isset($value['field'])) { + $value = json_encode(ConfigModel::getArrayData($value), JSON_UNESCAPED_UNICODE); + } else { + $value = is_array($value) ? implode(',', $value) : $value; + } + $v['value'] = $value; + $configList[] = $v->toArray(); + } + } + try { + $this->model->allowField(true)->saveAll($configList); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', '')); + } + } +} \ No newline at end of file diff --git a/application/admin/controller/fastim/CsrGroup.php b/application/admin/controller/fastim/CsrGroup.php new file mode 100644 index 0000000..363515f --- /dev/null +++ b/application/admin/controller/fastim/CsrGroup.php @@ -0,0 +1,33 @@ +model = new \app\admin\model\fastim\CsrGroup; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + public function import() + { + parent::import(); + } + +} diff --git a/application/admin/controller/fastim/FastReply.php b/application/admin/controller/fastim/FastReply.php new file mode 100644 index 0000000..336caa2 --- /dev/null +++ b/application/admin/controller/fastim/FastReply.php @@ -0,0 +1,177 @@ +model = new \app\admin\model\fastim\FastReply; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $params = $this->request->post("row/a"); + if ($params) { + $params = $this->preExcludeFields($params); + $params['content'] = Common::htmlImgUrlHandle($params['content']); + + if ($this->dataLimit && $this->dataLimitFieldAutoFill) { + $params[$this->dataLimitField] = $this->auth->id; + } + $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) { + $params = $this->preExcludeFields($params); + $params['content'] = Common::htmlImgUrlHandle($params['content']); + + $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 import() + { + parent::import(); + } + + /** + * 查看 + */ + public function index() + { + //当前是否为关联查询 + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + [$where, $sort, $order, $offset, $limit] = $this->buildparams(); + + $list = $this->model->with(['user' => ['fauser', 'admin']]) + ->where($where) + ->order($sort, $order) + ->paginate($limit); + + foreach ($list as $row) { + + $row->getRelation('user')->visible(['nickname']); + $aNickname = $row->user->admin->nickname ?? ''; + $faNickname = $row->user->fauser->nickname ?? ''; + $row->nickname = $row->user->nickname; + if ($aNickname || $faNickname) { + $row->nickname = $row->user->nickname . '(' . ($aNickname ? $aNickname : $faNickname) . ')'; + } + } + + $result = ["total" => $list->total(), "rows" => $list->items()]; + + return json($result); + } + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/fastim/Groupchat.php b/application/admin/controller/fastim/Groupchat.php new file mode 100644 index 0000000..6d5aa05 --- /dev/null +++ b/application/admin/controller/fastim/Groupchat.php @@ -0,0 +1,133 @@ +model = new \app\admin\model\fastim\Groupchat; + $this->view->assign("addModeList", $this->model->getAddModeList()); + $this->view->assign("inviteJoinGroupList", $this->model->getInviteJoinGroupList()); + $this->view->assign("speakList", $this->model->getSpeakList()); + $this->view->assign("historyMessageList", $this->model->getHistoryMessageList()); + } + + public function import() + { + parent::import(); + } + + /** + * 聊天记录 + */ + public function record($ids, $page = 1) + { + $groupChatInfo = Common::getImGroupChat($ids); + $imSession = Common::imSession('group', $groupChatInfo['leader'], $groupChatInfo['id']); + $sessionInfo = Common::sessionInfo($imSession['id'], $groupChatInfo['leader']); + + if (!$sessionInfo) { + $this->error(__('No Results were found')); + } + + if ($page == 1) { + $min = 0; + } else { + $min = ($page - 1) * $this->pageCount; + } + + if ($sessionInfo['type'] == 'group') { + + $message = Common::loadGroupChatRecord($sessionInfo, [ + 'min' => $min, + 'pageCount' => $this->pageCount + ], $sessionInfo['user_one']); + + $sessionInfo['windowType'] = 'message'; + } else { + $this->error('加载失败,请重试!'); + } + + $nextpage = (count($message) < $this->pageCount) ? false : true; + $message = Common::groupByTime($message); + + if ($this->request->isAjax()) { + $this->success('ok', '', [ + 'pageData' => [ + 'nextpage' => $nextpage, + 'page' => $page + ], + 'message' => $message, + 'sessionInfo' => $sessionInfo + ]); + } + + $this->assignconfig('pageData', [ + 'nextpage' => $nextpage, + 'page' => $page, + '__CDN__' => $this->request->domain() + ]); + $this->assignconfig('message', $message); + $this->assignconfig('sessionInfo', $sessionInfo); + return $this->view->fetch(); + } + + /** + * 查看 + */ + public function index() + { + //当前是否为关联查询 + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + [$where, $sort, $order, $offset, $limit] = $this->buildparams(); + + $list = $this->model + ->with(['fastimuser' => ['fauser', 'admin']]) + ->where($where) + ->order($sort, $order) + ->paginate($limit); + + foreach ($list as $row) { + + $row->getRelation('fastimuser')->visible(['nickname']); + $aNickname = $row->fastimuser->admin->nickname ?? ''; + $faNickname = $row->fastimuser->fauser->nickname ?? ''; + if ($aNickname || $faNickname) { + $row->fastimuser->nickname = $row->fastimuser->nickname . '(' . ($aNickname ? $aNickname : $faNickname) . ')'; + } + } + + $result = ["total" => $list->total(), "rows" => $list->items()]; + + return json($result); + } + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/fastim/Kbs.php b/application/admin/controller/fastim/Kbs.php new file mode 100644 index 0000000..a8f4925 --- /dev/null +++ b/application/admin/controller/fastim/Kbs.php @@ -0,0 +1,144 @@ +model = new \app\admin\model\fastim\Kbs; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $params = $this->request->post("row/a"); + if ($params) { + $params = $this->preExcludeFields($params); + $params['content'] = Common::htmlImgUrlHandle($params['content']); + + if ($this->dataLimit && $this->dataLimitFieldAutoFill) { + $params[$this->dataLimitField] = $this->auth->id; + } + $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) { + $params = $this->preExcludeFields($params); + $params['content'] = Common::htmlImgUrlHandle($params['content']); + + $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 import() + { + parent::import(); + } + + public function autocomplete() + { + return []; + } +} diff --git a/application/admin/controller/fastim/Records.php b/application/admin/controller/fastim/Records.php new file mode 100644 index 0000000..69621be --- /dev/null +++ b/application/admin/controller/fastim/Records.php @@ -0,0 +1,145 @@ +model = new \app\admin\model\fastim\Records; + $this->view->assign("typeList", $this->model->getTypeList()); + $this->view->assign("statusList", $this->model->getStatusList()); + } + + public function import() + { + parent::import(); + } + + /** + * 编辑 + */ + 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) { + $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 index() + { + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $dbprefix = config('database.prefix'); + + $list = $this->model + ->with([ + 'sender' => ['fauser', 'admin'], + 'recipient' => ['fauser', 'admin'], + 'groupchat' + ]) + ->where($where) + ->where($dbprefix . 'fastim_message.type', 'in', ['voice', 'group_chat_notice', 'kbs_list', 'friend_apply', 'group_invitation', 'group_notice', 'group_apply', 'system', 'link', 'file', 'video', 'audio', 'image', 'default']) + ->order($sort, $order) + ->paginate($limit); + foreach ($list as $row) { + $row->getRelation('groupchat')->visible(['nickname']); + $row->getRelation('sender')->visible(['nickname']); + $row->getRelation('recipient')->visible(['nickname']); + + $aNickname = $row->sender->admin->nickname ?? ''; + $faNickname = $row->sender->fauser->nickname ?? ''; + if ($aNickname || $faNickname) { + $row->sender->nickname = $row->sender->nickname . '(' . ($aNickname ? $aNickname : $faNickname) . ')'; + } + + $aNickname = $row->recipient->admin->nickname ?? ''; + $faNickname = $row->recipient->fauser->nickname ?? ''; + if ($aNickname || $faNickname) { + $row->recipient->nickname = $row->recipient->nickname . '(' . ($aNickname ? $aNickname : $faNickname) . ')'; + } + + $row->sender->nickname = (isset($row->sender->nickname) && $row->sender->nickname) ? $row->sender->nickname : '-'; + $row->recipient->nickname = (isset($row->recipient->nickname) && $row->recipient->nickname) ? $row->recipient->nickname : '-'; + } + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } +} diff --git a/application/admin/controller/fastim/Report.php b/application/admin/controller/fastim/Report.php new file mode 100644 index 0000000..e5c9185 --- /dev/null +++ b/application/admin/controller/fastim/Report.php @@ -0,0 +1,89 @@ +model = new \app\admin\model\fastim\Report; + $this->view->assign("typeList", $this->model->getTypeList()); + $this->view->assign("statusList", $this->model->getStatusList()); + } + + public function import() + { + parent::import(); + } + + /** + * 查看 + */ + public function index() + { + //当前是否为关联查询 + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + [$where, $sort, $order, $offset, $limit] = $this->buildparams(); + + $list = $this->model->with([ + 'user' => ['fauser', 'admin'], + 'reportuser' => ['fauser', 'admin'], + 'chatgroup' + ])->where($where)->order($sort, $order)->paginate($limit); + + foreach ($list as $row) { + + $row->getRelation('reportuser')->visible(['nickname']); + $aNickname = $row->reportuser->admin->nickname ?? ''; + $faNickname = $row->reportuser->fauser->nickname ?? ''; + $row->nickname = $row->reportuser->nickname; + if ($aNickname || $faNickname) { + $row->nickname = $row->nickname . '(' . ($aNickname ? $aNickname : $faNickname) . ')'; + } + + $row->getRelation('chatgroup')->visible(['nickname']); + $row->getRelation('user')->visible(['nickname']); + + if ($row->type == 'group') { + $row->user->nickname = $row->chatgroup->nickname; + } elseif ($row->type == 'user') { + $aNickname = $row->user->admin->nickname ?? ''; + $faNickname = $row->user->fauser->nickname ?? ''; + if ($aNickname || $faNickname) { + $row->user->nickname = $row->user->nickname . '(' . ($aNickname ? $aNickname : $faNickname) . ')'; + } + } else { + $row->user->nickname = '-'; + } + } + + $result = ["total" => $list->total(), "rows" => $list->items()]; + + return json($result); + } + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/fastim/Session.php b/application/admin/controller/fastim/Session.php new file mode 100644 index 0000000..71b23f4 --- /dev/null +++ b/application/admin/controller/fastim/Session.php @@ -0,0 +1,155 @@ +model = new \app\admin\model\fastim\Session; + $this->view->assign("typeList", $this->model->getTypeList()); + } + + public function import() + { + parent::import(); + } + + /** + * 聊天记录 + */ + public function record($ids, $page = 1) + { + $sessionInfo = Common::sessionInfo($ids); + if (!$sessionInfo) { + $this->error(__('No Results were found')); + } + + // 重新获取带会话用户的会话资料 + $sessionInfo = Common::sessionInfo($ids, $sessionInfo['user_one']); + + if ($page == 1) { + $min = 0; + } else { + $min = ($page - 1) * $this->pageCount; + } + + if ($sessionInfo['type'] == 'single') { + // 单聊消息 + $message = Common::loadSingleChatRecord($sessionInfo['id'], [ + 'min' => $min, + 'pageCount' => $this->pageCount + ], $sessionInfo['user_one']); + + $sessionInfo['windowType'] = 'message'; + } elseif ($sessionInfo['type'] == 'service') { + if ($sessionInfo['chat_id'] == 1 || $sessionInfo['chat_id'] == 2 || $sessionInfo['chat_id'] == 3) { + $message = Common::loadServiceRecord($sessionInfo, [ + 'min' => $min, + 'pageCount' => $this->pageCount + ], $sessionInfo['user_one']); + + if ($message) { + $sessionInfo['windowType'] = $message['windowType']; + $message = $message['message']; + } + } + } + + $nextpage = (count($message) < $this->pageCount) ? false : true; + $message = Common::groupByTime($message); + + if ($this->request->isAjax()) { + $this->success('ok', '', [ + 'pageData' => [ + 'nextpage' => $nextpage, + 'page' => $page + ], + 'message' => $message, + 'sessionInfo' => $sessionInfo + ]); + } + + $this->assignconfig('pageData', [ + 'nextpage' => $nextpage, + 'page' => $page, + '__CDN__' => $this->request->domain() + ]); + $this->assignconfig('message', $message); + $this->assignconfig('sessionInfo', $sessionInfo); + return $this->view->fetch(); + } + + /** + * 查看 + */ + public function index() + { + //当前是否为关联查询 + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + [$where, $sort, $order, $offset, $limit] = $this->buildparams(); + + $list = $this->model->with([ + 'user' => ['fauser', 'admin'], + 'usertwo' => ['fauser', 'admin'] + ]) + ->where($where) + ->where('session.type="single" OR session.type="service"') + ->order($sort, $order) + ->paginate($limit); + + foreach ($list as $row) { + + $row->getRelation('user')->visible(['nickname']); + $aNickname = $row->user->admin->nickname ?? ''; + $faNickname = $row->user->fauser->nickname ?? ''; + if ($aNickname || $faNickname) { + $row->user->nickname = $row->user->nickname . '(' . ($aNickname ? $aNickname : $faNickname) . ')'; + } + + $row->getRelation('usertwo')->visible(['nickname', 'id']); + if ($row->type == 'single') { + $aNickname = $row->usertwo->admin->nickname ?? ''; + $faNickname = $row->usertwo->fauser->nickname ?? ''; + if ($aNickname || $faNickname) { + $row->usertwo->nickname = $row->usertwo->nickname . '(' . ($aNickname ? $aNickname : $faNickname) . ')'; + } + } elseif ($row->chat_info) { + $row->usertwo->id = $row->chat_info['id']; + $row->usertwo->nickname = $row->chat_info['nickname']; + } + } + + $result = ["total" => $list->total(), "rows" => $list->items()]; + + return json($result); + } + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/fastim/User.php b/application/admin/controller/fastim/User.php new file mode 100644 index 0000000..3dbeb3e --- /dev/null +++ b/application/admin/controller/fastim/User.php @@ -0,0 +1,268 @@ +model = new \app\admin\model\fastim\User; + $this->view->assign("typeList", $this->model->getTypeList()); + $this->view->assign("genderList", $this->model->getGenderList()); + $this->view->assign("statusList", $this->model->getStatusList()); + $this->view->assign("occupationList", Common::formatOccupation(false));// 职业 + } + + public function import() + { + parent::import(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $params = $this->request->post("row/a"); + if ($params) { + $params = $this->preExcludeFields($params); + $params['welcome_msg'] = Common::htmlImgUrlHandle($params['welcome_msg']); + + if ($this->dataLimit && $this->dataLimitFieldAutoFill) { + $params[$this->dataLimitField] = $this->auth->id; + } + $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) { + $params = $this->preExcludeFields($params); + $params['birthday'] = $params['birthday'] ? $params['birthday'] : date('Y-m-d'); + $params['welcome_msg'] = Common::htmlImgUrlHandle($params['welcome_msg']); + + $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(); + } + + /** + * 加载im用户列表 + * @return [type] [description] + */ + public function loaduser() + { + //设置过滤方法 + $this->request->filter(['trim', 'strip_tags', 'htmlspecialchars']); + + //搜索关键词,客户端输入以空格分开,这里接收为数组 + $word = (array)$this->request->request("q_word/a"); + //当前页 + $page = $this->request->request("pageNumber"); + //分页大小 + $pagesize = $this->request->request("pageSize"); + + $custom = (array)$this->request->request("custom/a"); + + $primaryvalue = $this->request->request("keyValue"); + if ($primaryvalue !== null) { + $where = ['u.id' => ['in', $primaryvalue]]; + $pagesize = 999999; + } else { + $where = function ($query) use ($word, $custom) { + foreach ($word as $key => $value) { + if ($value) { + $query->whereOr('u.nickname', 'like', '%' . $value . '%'); + $query->whereOr('fu.nickname', 'like', '%' . $value . '%'); + $query->whereOr('a.nickname', 'like', '%' . $value . '%'); + } + } + + if ($custom && is_array($custom)) { + foreach ($custom as $k => $v) { + if (is_array($v) && 2 == count($v)) { + $query->where($k, trim($v[0]), $v[1]); + } else { + $query->where($k, '=', $v); + } + } + } + }; + } + + $res = \think\Db::name('fastim_user') + ->alias('u') + ->field('u.id,u.nickname,fu.nickname as fu_nickname,a.nickname as a_nickname') + ->join('user fu', 'fu.id=u.user_id', 'LEFT') + ->join('admin a', 'a.id=u.admin_id', 'LEFT') + ->where($where) + ->order('u.createtime desc') + ->page($page, $pagesize) + ->select(); + + $total = \think\Db::name('fastim_user') + ->alias('u') + ->join('user fu', 'fu.id=u.user_id', 'LEFT') + ->join('admin a', 'a.id=u.admin_id', 'LEFT') + ->where($where) + ->order('u.createtime desc') + ->count('u.id'); + + foreach ($res as $key => $value) { + if ($value['a_nickname'] || $value['fu_nickname']) { + $res[$key]['nickname'] = $value['nickname'] . '(' . ($value['a_nickname'] ? $value['a_nickname'] : $value['fu_nickname']) . ')'; + } + unset($res[$key]['fu_nickname'], $res[$key]['a_nickname']); + } + + return json(['list' => $res, 'total' => $total]); + } + + /** + * 查看 + */ + public function index() + { + //当前是否为关联查询 + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + [$where, $sort, $order, $offset, $limit] = $this->buildparams(); + + $list = $this->model->with(['fauser', 'admin', 'csrgroup']) + ->where($where) + ->order($sort, $order) + ->paginate($limit); + + foreach ($list as $row) { + + $row->getRelation('csrgroup')->visible(['name']); + + $row->getRelation('fauser')->visible(['nickname', 'mobile']); + $row->getRelation('admin')->visible(['nickname']); + $row->hidden(['token', 'wechat_openid']); + + $row->trueNickname = Common::trueAttr(Common::nicknameSort([ + $row->fauser->nickname, + $row->admin->nickname, + $row->nickname + ], $row->id)); + + $row->avatar = Common::avatarSrc([ + $row->avatar, + $row->fauser->avatar, + $row->admin->avatar + ], $row->trueNickname); + + $row->mobile = Common::trueAttr([ + $row->mobile, + $row->fauser->mobile + ]); + } + + $result = ["total" => $list->total(), "rows" => $list->items()]; + + return json($result); + } + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/general/Attachment.php b/application/admin/controller/general/Attachment.php new file mode 100644 index 0000000..7c4cdfc --- /dev/null +++ b/application/admin/controller/general/Attachment.php @@ -0,0 +1,160 @@ +model = model('Attachment'); + $this->view->assign("mimetypeList", \app\common\model\Attachment::getMimetypeList()); + $this->view->assign("categoryList", \app\common\model\Attachment::getCategoryList()); + $this->assignconfig("categoryList", \app\common\model\Attachment::getCategoryList()); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + $mimetypeQuery = []; + $filter = $this->request->request('filter'); + $filterArr = (array)json_decode($filter, true); + if (isset($filterArr['category']) && $filterArr['category'] == 'unclassed') { + $filterArr['category'] = ',unclassed'; + $this->request->get(['filter' => json_encode(array_diff_key($filterArr, ['category' => '']))]); + } + if (isset($filterArr['mimetype']) && preg_match("/(\/|\,|\*)/", $filterArr['mimetype'])) { + $mimetype = $filterArr['mimetype']; + $filterArr = array_diff_key($filterArr, ['mimetype' => '']); + $mimetypeQuery = function ($query) use ($mimetype) { + $mimetypeArr = array_filter(explode(',', $mimetype)); + foreach ($mimetypeArr as $index => $item) { + $query->whereOr('mimetype', 'like', '%' . str_replace("/*", "/", $item) . '%'); + } + }; + } + $this->request->get(['filter' => json_encode($filterArr)]); + + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + ->where($mimetypeQuery) + ->where($where) + ->order($sort, $order) + ->paginate($limit); + + $cdnurl = preg_replace("/\/(\w+)\.php$/i", '', $this->request->root()); + foreach ($list as $k => &$v) { + $v['fullurl'] = ($v['storage'] == 'local' ? $cdnurl : $this->view->config['upload']['cdnurl']) . $v['url']; + } + unset($v); + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 选择附件 + */ + public function select() + { + if ($this->request->isAjax()) { + return $this->index(); + } + $mimetype = $this->request->get('mimetype', ''); + $mimetype = substr($mimetype, -1) === '/' ? $mimetype . '*' : $mimetype; + $this->view->assign('mimetype', $mimetype); + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isAjax()) { + $this->error(); + } + return $this->view->fetch(); + } + + /** + * 删除附件 + * @param array $ids + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + \think\Hook::add('upload_delete', function ($params) { + if ($params['storage'] == 'local') { + $attachmentFile = ROOT_PATH . '/public' . $params['url']; + if (is_file($attachmentFile)) { + @unlink($attachmentFile); + } + } + }); + $attachmentlist = $this->model->where('id', 'in', $ids)->select(); + foreach ($attachmentlist as $attachment) { + \think\Hook::listen("upload_delete", $attachment); + $attachment->delete(); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + /** + * 归类 + */ + public function classify() + { + if (!$this->auth->check('general/attachment/edit')) { + \think\Hook::listen('admin_nopermission', $this); + $this->error(__('You have no permission'), ''); + } + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $category = $this->request->post('category', ''); + $ids = $this->request->post('ids'); + if (!$ids) { + $this->error(__('Parameter %s can not be empty', 'ids')); + } + $categoryList = \app\common\model\Attachment::getCategoryList(); + if ($category && !isset($categoryList[$category])) { + $this->error(__('Category not found')); + } + $category = $category == 'unclassed' ? '' : $category; + \app\common\model\Attachment::where('id', 'in', $ids)->update(['category' => $category]); + $this->success(); + } + +} diff --git a/application/admin/controller/general/Config.php b/application/admin/controller/general/Config.php new file mode 100644 index 0000000..5dac56c --- /dev/null +++ b/application/admin/controller/general/Config.php @@ -0,0 +1,311 @@ +model = model('Config'); + $this->model = new ConfigModel; + ConfigModel::event('before_write', function ($row) { + if (isset($row['name']) && $row['name'] == 'name' && preg_match("/fast" . "admin/i", $row['value'])) { + throw new Exception(__("Site name incorrect")); + } + }); + } + + /** + * 查看 + */ + public function index() + { + $siteList = []; + $groupList = ConfigModel::getGroupList(); + foreach ($groupList as $k => $v) { + $siteList[$k]['name'] = $k; + $siteList[$k]['title'] = $v; + $siteList[$k]['list'] = []; + } + + foreach ($this->model->all() as $k => $v) { + if (!isset($siteList[$v['group']])) { + continue; + } + $value = $v->toArray(); + $value['title'] = __($value['title']); + if (in_array($value['type'], ['select', 'selects', 'checkbox', 'radio'])) { + $value['value'] = explode(',', $value['value']); + } + $value['content'] = json_decode($value['content'], true); + if (in_array($value['name'], ['categorytype', 'configgroup', 'attachmentcategory'])) { + $dictValue = (array)json_decode($value['value'], true); + foreach ($dictValue as $index => &$item) { + $item = __($item); + } + unset($item); + $value['value'] = json_encode($dictValue, JSON_UNESCAPED_UNICODE); + } + $value['tip'] = htmlspecialchars($value['tip']); + if ($value['name'] == 'cdnurl') { + //cdnurl不支持在线修改 + continue; + } + $siteList[$v['group']]['list'][] = $value; + } + $index = 0; + foreach ($siteList as $k => &$v) { + $v['active'] = !$index ? true : false; + $index++; + } + $this->view->assign('siteList', $siteList); + $this->view->assign('typeList', ConfigModel::getTypeList()); + $this->view->assign('ruleList', ConfigModel::getRegexList()); + $this->view->assign('groupList', ConfigModel::getGroupList()); + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if (!config('app_debug')) { + $this->error(__('Only work at development environment')); + } + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a", [], 'trim'); + if ($params) { + foreach ($params as $k => &$v) { + $v = is_array($v) && $k !== 'setting' ? implode(',', $v) : $v; + } + if (in_array($params['type'], ['select', 'selects', 'checkbox', 'radio', 'array'])) { + $params['content'] = json_encode(ConfigModel::decode($params['content']), JSON_UNESCAPED_UNICODE); + } else { + $params['content'] = ''; + } + try { + $result = $this->model->create($params); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + if ($result !== false) { + try { + ConfigModel::refreshFile(); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } else { + $this->error($this->model->getError()); + } + } + $this->error(__('Parameter %s can not be empty', '')); + } + return $this->view->fetch(); + } + + /** + * 编辑 + * @param null $ids + */ + public function edit($ids = null) + { + if ($this->request->isPost()) { + $this->token(); + $row = $this->request->post("row/a", [], 'trim'); + if ($row) { + $configList = []; + foreach ($this->model->all() as $v) { + if (isset($row[$v['name']])) { + $value = $row[$v['name']]; + if (is_array($value) && isset($value['field'])) { + $value = json_encode(ConfigModel::getArrayData($value), JSON_UNESCAPED_UNICODE); + } else { + $value = is_array($value) ? implode(',', $value) : $value; + } + $v['value'] = $value; + $configList[] = $v->toArray(); + } + } + try { + $this->model->allowField(true)->saveAll($configList); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + try { + ConfigModel::refreshFile(); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } + $this->error(__('Parameter %s can not be empty', '')); + } + } + + /** + * 删除 + * @param string $ids + */ + public function del($ids = "") + { + if (!config('app_debug')) { + $this->error(__('Only work at development environment')); + } + $name = $this->request->post('name'); + $config = ConfigModel::getByName($name); + if ($name && $config) { + try { + $config->delete(); + ConfigModel::refreshFile(); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + $this->success(); + } else { + $this->error(__('Invalid parameters')); + } + } + + /** + * 检测配置项是否存在 + * @internal + */ + public function check() + { + $params = $this->request->post("row/a"); + if ($params) { + $config = $this->model->get($params); + if (!$config) { + $this->success(); + } else { + $this->error(__('Name already exist')); + } + } else { + $this->error(__('Invalid parameters')); + } + } + + /** + * 规则列表 + * @internal + */ + public function rulelist() + { + //主键 + $primarykey = $this->request->request("keyField"); + //主键值 + $keyValue = $this->request->request("keyValue", ""); + + $keyValueArr = array_filter(explode(',', $keyValue)); + $regexList = \app\common\model\Config::getRegexList(); + $list = []; + foreach ($regexList as $k => $v) { + if ($keyValueArr) { + if (in_array($k, $keyValueArr)) { + $list[] = ['id' => $k, 'name' => $v]; + } + } else { + $list[] = ['id' => $k, 'name' => $v]; + } + } + return json(['list' => $list]); + } + + /** + * 发送测试邮件 + * @internal + */ + public function emailtest() + { + $row = $this->request->post('row/a'); + $receiver = $this->request->post("receiver"); + if ($receiver) { + if (!Validate::is($receiver, "email")) { + $this->error(__('Please input correct email')); + } + \think\Config::set('site', array_merge(\think\Config::get('site'), $row)); + $email = new Email; + $result = $email + ->to($receiver) + ->subject(__("This is a test mail", config('site.name'))) + ->message('
    ' . __('This is a test mail content', config('site.name')) . '
    ') + ->send(); + if ($result) { + $this->success(); + } else { + $this->error($email->getError()); + } + } else { + $this->error(__('Invalid parameters')); + } + } + + public function selectpage() + { + $id = $this->request->get("id/d"); + $config = \app\common\model\Config::get($id); + if (!$config) { + $this->error(__('Invalid parameters')); + } + $setting = $config['setting']; + //自定义条件 + $custom = isset($setting['conditions']) ? (array)json_decode($setting['conditions'], true) : []; + $custom = array_filter($custom); + + $this->request->request(['showField' => $setting['field'], 'keyField' => $setting['primarykey'], 'custom' => $custom, 'searchField' => [$setting['field'], $setting['primarykey']]]); + $this->model = \think\Db::connect()->setTable($setting['table']); + return parent::selectpage(); + } + + /** + * 获取表列表 + * @internal + */ + public function get_table_list() + { + $tableList = []; + $dbname = \think\Config::get('database.database'); + $tableList = \think\Db::query("SELECT `TABLE_NAME` AS `name`,`TABLE_COMMENT` AS `title` FROM `information_schema`.`TABLES` where `TABLE_SCHEMA` = '{$dbname}';"); + $this->success('', null, ['tableList' => $tableList]); + } + + /** + * 获取表字段列表 + * @internal + */ + public function get_fields_list() + { + $table = $this->request->request('table'); + $dbname = \think\Config::get('database.database'); + //从数据库中获取表字段信息 + $sql = "SELECT `COLUMN_NAME` AS `name`,`COLUMN_COMMENT` AS `title`,`DATA_TYPE` AS `type` FROM `information_schema`.`columns` WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION"; + //加载主表的列 + $fieldList = Db::query($sql, [$dbname, $table]); + $this->success("", null, ['fieldList' => $fieldList]); + } +} diff --git a/application/admin/controller/general/Profile.php b/application/admin/controller/general/Profile.php new file mode 100644 index 0000000..feffa99 --- /dev/null +++ b/application/admin/controller/general/Profile.php @@ -0,0 +1,83 @@ +request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + $this->model = model('AdminLog'); + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + ->where($where) + ->where('admin_id', $this->auth->id) + ->order($sort, $order) + ->paginate($limit); + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 更新个人信息 + */ + public function update() + { + if ($this->request->isPost()) { + $this->token(); + $params = $this->request->post("row/a"); + $params = array_filter(array_intersect_key( + $params, + array_flip(array('email', 'nickname', 'password', 'avatar')) + )); + unset($v); + if (!Validate::is($params['email'], "email")) { + $this->error(__("Please input correct email")); + } + if (isset($params['password'])) { + if (!Validate::is($params['password'], "/^[\S]{6,30}$/")) { + $this->error(__("Please input correct password")); + } + $params['salt'] = Random::alnum(); + $params['password'] = md5(md5($params['password']) . $params['salt']); + } + $exist = Admin::where('email', $params['email'])->where('id', '<>', $this->auth->id)->find(); + if ($exist) { + $this->error(__("Email already exists")); + } + if ($params) { + $admin = Admin::get($this->auth->id); + $admin->save($params); + //因为个人资料面板读取的Session显示,修改自己资料后同时更新Session + Session::set("admin", $admin->toArray()); + $this->success(); + } + $this->error(); + } + return; + } +} diff --git a/application/admin/controller/shopro/Category.php b/application/admin/controller/shopro/Category.php new file mode 100644 index 0000000..bc25aeb --- /dev/null +++ b/application/admin/controller/shopro/Category.php @@ -0,0 +1,218 @@ +model = new CategoryModel; + } + + + /** + * 服务保障列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $categories = $this->model->sheepFilter()->where('parent_id', 0)->order('weigh', 'desc')->order('id', 'desc')->select(); + + $this->success('获取成功', null, $categories); + } + + + + + /** + * 添加服务保障 + * + * @return \think\Response + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'name', 'style', 'description', 'weigh', 'vip_discount', 'categories' + ]); + $this->svalidate($params, ".add"); + $categories = json_decode($params['categories'], true); + + Db::transaction(function () use ($params, $categories) { + $this->model->allowField(true)->save($params); + + //递归处理分类数据 + $this->createOrEditCategory($categories, $this->model->id); + }); + $this->success('保存成功'); + } + + + /** + * 服务保障详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $category = $this->model->where('parent_id', 0)->where('id', $id)->find(); + if (!$category) { + $this->error(__('No Results were found')); + } + + $categories = $this->model->with('children.children')->where('parent_id', $category->id)->order('weigh', 'desc')->order('id', 'desc')->select(); + + $this->success('获取成功', null, ['category' => $category, 'categories' => $categories]); + } + + + + /** + * 修改商品分类 + * + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isPost()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'name', 'style', 'description', 'weigh', 'vip_discount', 'categories' + ]); + $this->svalidate($params, ".edit"); + $categories = json_decode($params['categories'], true); + $category = $this->model->where('parent_id', 0)->where('id', $id)->find(); + if (!$category) { + $this->error(__('No Results were found')); + } + Db::transaction(function () use ($category, $params, $categories) { + $category->allowField(true)->save($params); + + //递归处理分类数据 + $this->createOrEditCategory($categories, $category->id); + }); + $this->success('更新成功'); + } + + + + /** + * 删除服务标签 + * + * @param string $id 要删除的服务保障列表 + * @return void + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->with('children')->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + if ($item->children) { + $this->error('请先删除子分类'); + } + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + /** + * 获取所有服务列表 + * + * @return \think\Response + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $categories = (new Tree(function () { + // 组装搜索条件,排序等 + return $this->model->field("id, name, parent_id, status, weigh")->normal() + ->order('weigh', 'desc')->order('id', 'asc'); + }))->getTree(0); + + $this->success('获取成功', null, $categories); + } + + + + private function createOrEditCategory($categories, $parent_id) + { + foreach ($categories as $key => $data) { + $data['parent_id'] = $parent_id; + + if (isset($data['id']) && $data['id']) { + $category = $this->model->find($data['id']); + if (!$category) { + $this->error(__('No Results were found')); + } + if (isset($data['deleted']) && $data['deleted'] == 1) { + $category->delete(); + } else { + $category->name = $data['name']; + $category->parent_id = $data['parent_id']; + $category->image = $data['image']; + $category->description = $data['description'] ?? null; + $category->vip_discount = $data['vip_discount'] ?? null; + $category->status = $data['status']; + $category->weigh = $data['weigh']; + $category->save(); + } + } else { + if (!isset($data['deleted']) || !$data['deleted']) { + $category = new CategoryModel; + $category->name = $data['name']; + $category->parent_id = $data['parent_id']; + $category->image = $data['image']; + $category->description = $data['description'] ?? null; + $category->vip_discount = $data['vip_discount'] ?? null; + $category->status = $data['status']; + $category->weigh = $data['weigh']; + $category->save(); + $data['id'] = $category->id; + } + } + + if (isset($data['children']) && !empty($data['children']) && isset($data['id'])) { + $this->createOrEditCategory($data['children'], $data['id']); + } + } + } +} diff --git a/application/admin/controller/shopro/Common.php b/application/admin/controller/shopro/Common.php new file mode 100644 index 0000000..50f4bff --- /dev/null +++ b/application/admin/controller/shopro/Common.php @@ -0,0 +1,31 @@ + 'v3'], false); + } + + $is_pro = check_env('commission', false); + \think\View::share('is_pro', $is_pro); + $this->assignconfig("is_pro", $is_pro); + } +} diff --git a/application/admin/controller/shopro/Config.php b/application/admin/controller/shopro/Config.php new file mode 100644 index 0000000..e88e735 --- /dev/null +++ b/application/admin/controller/shopro/Config.php @@ -0,0 +1,341 @@ + '基本信息', + 'name' => 'shopro/config/basic', + 'status' => $this->auth->check('shopro/config/basic') + ], + [ + 'label' => '用户配置', + 'name' => 'shopro/config/user', + 'status' => $this->auth->check('shopro/config/user') + ], + [ + 'label' => '平台配置', + 'name' => 'shopro/config/platform', + 'status' => $this->auth->check('shopro/config/platform') + ], + [ + 'label' => '订单配置', + 'name' => 'shopro/config/order', + 'status' => $this->auth->check('shopro/config/order') + ], + [ + 'label' => '商品配置', + 'name' => 'shopro/config/goods', + 'status' => $this->auth->check('shopro/config/goods') + ], + [ + 'label' => '物流配置', + 'name' => 'shopro/config/dispatch', + 'status' => $this->auth->check('shopro/config/dispatch') + ], + [ + 'label' => '充值提现', + 'name' => 'shopro/config/rechargewithdraw', + 'status' => $this->auth->check('shopro/config/rechargewithdraw') + ], + [ + 'label' => '分销配置', + 'name' => 'shopro/config/commission', + 'status' => $this->auth->check('shopro/config/commission') + ], + [ + 'label' => '支付配置', + 'name' => 'shopro/pay_config', + 'status' => $this->auth->check('shopro/pay_config') + ], + [ + 'label' => '客服配置', + 'name' => 'shopro/config/chat', + 'status' => $this->auth->check('shopro/config/chat') + ], + [ + 'label' => 'Redis配置', + 'name' => 'shopro/config/redis', + 'status' => $this->auth->check('shopro/config/redis') + ], + [ + 'label' => '会员卡配置', + 'name' => 'shopro/config/vip', + 'status' => $this->auth->check('shopro/config/vip') + ], + ]; + $this->assignconfig("configList", $configList); + return $this->view->fetch(); + } + + /** + * 商品配置 + */ + public function vip() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.vip', false); + // var_dump($configs);exit; + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.vip', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + /** + * 基本配置 + */ + public function basic() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.basic', false); + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.basic', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 用户默认配置 + */ + public function user() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.user', false); + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.user', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 物流配置 + */ + public function dispatch() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.dispatch', false); + $configs['callback'] = $this->request->domain() . '/addons/shopro/order.express/push'; + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.dispatch', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 平台状态 + */ + public function platformStatus() + { + $status = [ + 'H5' => ShoproConfig::getConfigs('shop.platform.H5.status', false), + 'App' => ShoproConfig::getConfigs('shop.platform.App.status', false), + 'WechatMiniProgram' => ShoproConfig::getConfigs('shop.platform.WechatMiniProgram.status', false), + 'WechatOfficialAccount' => ShoproConfig::getConfigs('shop.platform.WechatOfficialAccount.status', false), + ]; + + $this->success('操作成功', null, $status); + } + + + + /** + * 平台配置 + */ + public function platform($platform) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + if (!in_array($platform, ['App', 'H5', 'WechatMiniProgram', 'WechatOfficialAccount'])) { + $this->error('平台不支持'); + } + + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.platform.' . $platform, false); + } elseif ('POST' === $this->request->method()) { + $params = $this->request->param(); + if (!isset($params['share']['methods'])) { + $params['share']['methods'] = []; + } + if (!isset($params['payment']['methods'])) { + $params['payment']['methods'] = []; + } + $configs = ShoproConfig::setConfigs('shop.platform.' . $platform, $params); + } + $this->success('操作成功', null, $configs); + } + + + public function commission() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.commission', false); + } elseif ('POST' === $this->request->method()) { + check_env('commission'); + $params = $this->request->param(); + + $configs = ShoproConfig::setConfigs('shop.commission', $params); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 订单配置 + */ + public function order() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.order', false); + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.order', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 商品配置 + */ + public function goods() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.goods', false); + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.goods', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 充值提现配置 + */ + public function rechargeWithdraw() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.recharge_withdraw', false); + } elseif ('POST' === $this->request->method()) { + $params = $this->request->param(); + if (!isset($params['recharge']['methods'])) { + $params['recharge']['methods'] = []; + } + if (!isset($params['recharge']['quick_amounts'])) { + $params['recharge']['quick_amounts'] = []; + } + if (!isset($params['withdraw']['methods'])) { + $params['withdraw']['methods'] = []; + } + $configs = ShoproConfig::setConfigs('shop.recharge_withdraw', $params); + } + $this->success('操作成功', null, $configs); + } + + + + /** + * 客服配置 + */ + public function chat() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('chat', false); + } elseif ('POST' === $this->request->method()) { + $configs = ShoproConfig::setConfigs('chat', $this->request->param()); + + // 存文件 + file_put_contents( + ROOT_PATH . 'application' . DS . 'extra' . DS . 'chat.php', + 'request->param(), true) . ";" + ); + } + $this->success('操作成功', null, $configs); + } + + + + /** + * redis 配置 + */ + public function redis() + { + if ('GET' === $this->request->method()) { + $default = [ + 'host' => '127.0.0.1', // redis 主机地址 + 'password' => '', // redis 密码 + 'port' => 6379, // redis 端口 + 'select' => 1, // redis 数据库 + 'timeout' => 0, // redis 超时时间 + 'persistent' => false, // redis 持续性,连接复用 + ]; + $redis = \think\Config::get('redis'); + $redis['empty_password'] = 0; + $redis['password'] = ''; // 隐藏密码 + $configs = $redis ? array_merge($default, $redis) : $default; + } elseif ('POST' === $this->request->method()) { + operate_filter(); + $configs = $this->request->param(); + $empty_password = (int)$configs['empty_password']; // 是否设置空密码 + unset($configs['empty_password']); + + if (isset($configs['password']) && empty($configs['password'])) { + $redis = \think\Config::get('redis'); + // 不修改密码,保持为原始值 + $configs['password'] = $redis['password'] ?? ''; + } elseif ($empty_password) { + $configs['password'] = ''; + } + + $configs['persistent'] = (isset($configs['persistent']) && ($configs['persistent'] === true || $configs['persistent'] == 'true')) ? true : false; + + // 存文件 + file_put_contents( + ROOT_PATH . 'application' . DS . 'extra' . DS . 'redis.php', + 'success('操作成功', null, $configs); + } + + + + public function getPlatformUrl() + { + $h5Url = ShoproConfig::getConfigField('shop.basic.domain'); + $wechatMpAppid = ShoproConfig::getConfigField('shop.platform.WechatMiniProgram.app_id'); + + $this->success('', null, [ + 'url' => $h5Url, + 'appid' => $wechatMpAppid + ]); + } +} diff --git a/application/admin/controller/shopro/Coupon.php b/application/admin/controller/shopro/Coupon.php new file mode 100644 index 0000000..b501283 --- /dev/null +++ b/application/admin/controller/shopro/Coupon.php @@ -0,0 +1,292 @@ +model = new CouponModel; + } + + + + /** + * 优惠券列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $coupons = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10))->each(function ($coupon) { + // 优惠券领取和使用数量 + $coupon->get_num = $coupon->get_num; + $coupon->use_num = $coupon->use_num; + }); + + $result = [ + 'coupons' => $coupons, + ]; + + $result['total_num'] = UserCouponModel::count(); + $result['expire_num'] = UserCouponModel::expired()->count(); + $result['use_num'] = UserCouponModel::used()->count(); + $result['use_percent'] = 0 . '%'; + if ($result['total_num']) { + $result['use_percent'] = bcdiv(bcmul((string)$result['use_num'], '100'), (string)$result['total_num'], 1) . '%'; + } + + $this->success('获取成功', null, $result); + } + + + + + /** + * 添加优惠券 + * + * @return \think\Response + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'name', 'type', 'use_scope', 'items', 'amount', 'max_amount', 'enough', + 'stock', 'limit_num', 'get_time', + 'use_time_type', 'use_time', 'start_days', 'days', + 'is_double_discount', 'description', 'status' + ]); + $this->svalidate($params, ".add"); + + // 时间转换 + $getTime = explode(' - ', $params['get_time']); + $useTime = explode(' - ', $params['use_time']); + unset($params['get_time'], $params['use_time']); + $params['get_start_time'] = $getTime[0] ?? 0; + $params['get_end_time'] = $getTime[1] ?? 0; + $params['use_start_time'] = $useTime[0] ?? 0; + $params['use_end_time'] = $useTime[1] ?? 0; + + $this->model->save($params); + + $this->success('保存成功'); + } + + + /** + * 优惠券详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $coupon = $this->model->where('id', $id)->find(); + if (!$coupon) { + $this->error(__('No Results were found')); + } + + $coupon->items_value = $coupon->items_value; // 可用范围值 + + $this->success('获取成功', null, $coupon); + } + + + + /** + * 修改优惠券 + * + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'name', 'use_scope', 'items', 'amount', 'max_amount', 'enough', + 'stock', 'limit_num', 'get_time', + 'use_time_type', 'use_time', 'start_days', 'days', + 'is_double_discount', 'description', 'status' + ]); + $this->svalidate($params); + + // 时间转换 + if (isset($params['get_time'])) { + $getTime = explode(' - ', $params['get_time']); + + $params['get_start_time'] = $getTime[0] ?? 0; + $params['get_end_time'] = $getTime[1] ?? 0; + unset($params['get_time']); + } + + if (isset($params['use_time'])) { + $useTime = explode(' - ', $params['use_time']); + + $params['use_start_time'] = $useTime[0] ?? 0; + $params['use_end_time'] = $useTime[1] ?? 0; + unset($params['use_time']); + } + + $coupon = $this->model->where('id', $id)->find(); + if (!$coupon) { + $this->error(__('No Results were found')); + } + + $coupon->save($params); + $this->success('更新成功'); + } + + + + /** + * 删除优惠券 + * + * @param string $id 要删除的优惠券 + * @return void + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + /** + * 优惠券列表 + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type', 'page'); + + $coupons = $this->model->sheepFilter(); + + if ($type == 'select') { + // 普通结果 + $coupons = $coupons->select(); + } elseif ($type == 'find') { + $coupons = $coupons->find(); + } else { + // 分页结果 + $coupons = $coupons->paginate($this->request->param('list_rows', 10)); + } + + $this->success('获取成功', null, $coupons); + } + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $coupons = $this->model->onlyTrashed()->sheepFilter()->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $coupons); + } + + + /** + * 还原(支持批量) + * + * @param $id + * @return \think\Response + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $items = $this->model->onlyTrashed()->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + $count += $item->restore(); + } + + return $count; + }); + + if ($result) { + $this->success('还原成功', null, $result); + } else { + $this->error(__('No rows were updated')); + } + } + + + /** + * 销毁(支持批量) + * + * @param $id + * @return \think\Response + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + if ($id !== 'all') { + $items = $this->model->onlyTrashed()->whereIn('id', $id)->select(); + } else { + $items = $this->model->onlyTrashed()->select(); + } + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + // 删除商品 + $count += $item->delete(true); + } + return $count; + }); + + if ($result) { + $this->success('销毁成功', null, $result); + } + $this->error('销毁失败'); + } +} diff --git a/application/admin/controller/shopro/Dashboard.php b/application/admin/controller/shopro/Dashboard.php new file mode 100644 index 0000000..a969b22 --- /dev/null +++ b/application/admin/controller/shopro/Dashboard.php @@ -0,0 +1,241 @@ + 'total', + 'card' => 'shopro/dashboard/total', + 'status' => $this->auth->check('shopro/dashboard/total') + ], + [ + 'name' => 'chart', + 'card' => 'shopro/dashboard/chart', + 'status' => $this->auth->check('shopro/dashboard/chart') + ], + [ + 'name' => 'ranking', + 'card' => 'shopro/dashboard/ranking', + 'status' => $this->auth->check('shopro/dashboard/ranking') + ], + ]; + $this->assignconfig('cardList', $cardList); + return $this->view->fetch(); + } + + + public function total() + { + // 用户数据 + $userData = [ + 'total' => User::count(), + 'today' => User::whereTime('createtime', 'today')->count(), + 'week' => User::whereTime('createtime', 'week')->count(), + 'array' => collection(User::field('id,createtime')->whereTime('createtime', 'today')->select())->each(function ($user) { + $user->counter = 1; + $user->createtime_unix = $user->getData('createtime') * 1000; + }) + ]; + + // -- commission code start -- + $agentData = [ + 'total' => \app\admin\model\shopro\commission\Agent::count(), + 'today' => \app\admin\model\shopro\commission\Agent::whereTime('createtime', 'today')->count(), + 'week' => \app\admin\model\shopro\commission\Agent::whereTime('createtime', 'week')->count(), + 'array' => collection(\app\admin\model\shopro\commission\Agent::field('user_id,createtime')->whereTime('createtime', 'today')->select())->each(function ($agent) { + $agent->counter = 1; + $agent->createtime_unix = $agent->getData('createtime') * 1000; + }) + ]; + // -- commission code end -- + + // 分享数据 + $shareData = [ + 'total' => Share::count(), + 'today' => Share::whereTime('createtime', 'today')->count(), + 'week' => Share::whereTime('createtime', 'week')->count(), + 'array' => collection(Share::field('id,createtime')->whereTime('createtime', 'today')->select())->each(function ($share) { + $share->counter = 1; + $share->createtime_unix = $share->getData('createtime') * 1000; + }) + ]; + + $this->success('获取成功', null, [ + 'user_data' => $userData, + 'agent_data' => $agentData ?? null, + 'share_data' => $shareData + ]); + } + + + + public function chart() + { + $date = $this->request->param('date', ''); + $date = array_values(array_filter(explode(' - ', $date))); + + $orders = Order::with(['items'])->whereTime('createtime', 'between', $date) + ->order('id', 'asc')->select(); + + // 订单数 + $data['orderNum'] = count($orders); + $data['orderArr'] = []; + + // 支付订单(包含退款的订单, 不包含货到付款还未收货(未支付)订单) + $data['payOrderNum'] = 0; + $data['payOrderArr'] = []; + //支付金额(包含退款的订单, 不包含货到付款还未收货(未支付)订单) + $data['payAmountNum'] = 0; + $data['payAmountArr'] = []; + + // 支付用户(一个人不管下多少单,都算一个,包含退款的订单, 不包含货到付款还未收货(未支付)订单) + $userIds = []; + $data['payUserNum'] = 0; + $data['payUserArr'] = []; + + // 代发货(包含货到付款) + $data['noSendNum'] = 0; + $data['noSendArr'] = []; + //售后维权 + $data['aftersaleNum'] = 0; + $data['aftersaleArr'] = []; + //退款订单 + $data['refundNum'] = 0; + $data['refundArr'] = []; + + foreach ($orders as $key => $order) { + $data['orderArr'][] = [ + 'counter' => 1, + 'createtime' => $order->getData('createtime') * 1000, + 'user_id' => $order->user_id + ]; + + // 已支付的,不包含,货到付款未支付的 + if (in_array($order->status, [Order::STATUS_PAID, Order::STATUS_COMPLETED])) { + // 支付订单数 + $data['payOrderNum']++; + + $data['payOrderArr'][] = [ + 'counter' => 1, + 'createtime' => $order->getData('createtime') * 1000, + 'user_id' => $order->user_id + ]; + + // 支付金额 + $data['payAmountNum'] = bcadd((string)$data['payAmountNum'], $order->pay_fee, 2); + + $data['payAmountArr'][] = [ + 'counter' => $order->pay_fee, + 'createtime' => $order->getData('createtime') * 1000, + ]; + + // 下单用户 + if (!in_array($order->user_id, $userIds)) { + $data['payUserNum']++; + $data['payUserArr'][] = [ + 'counter' => 1, + 'createtime' => $order->getData('createtime') * 1000, + 'user_id' => $order->user_id + ]; + } + } + + // 已支付的,和 货到付款未支付的 + if (in_array($order->status, [Order::STATUS_PAID, Order::STATUS_COMPLETED]) || $order->isOffline($order)) { + $flagnoSend = false; + $flagaftersale = false; + $flagrefund = false; + $aftersaleIng = false; + foreach ($order->items as $k => $item) { + if ( + !$flagnoSend + && $item->dispatch_status == OrderItem::DISPATCH_STATUS_NOSEND + && $item->refund_status == OrderItem::REFUND_STATUS_NOREFUND + && in_array($order->apply_refund_status, [ + Order::APPLY_REFUND_STATUS_NOAPPLY, + Order::APPLY_REFUND_STATUS_REFUSE + ]) + ) { + $flagnoSend = true; + } + + if ( + $item->aftersale_status == OrderItem::AFTERSALE_STATUS_ING + && $item->dispatch_status == OrderItem::DISPATCH_STATUS_NOSEND + && $item->refund_status == OrderItem::REFUND_STATUS_NOREFUND + ) { + $aftersaleIng = true; + } + + if (!$flagaftersale && $item->aftersale_status != OrderItem::AFTERSALE_STATUS_NOAFTER) { + $data['aftersaleNum']++; + // $data['aftersaleArr'][] = [ + // 'counter' => 1, + // 'createtime' => $order->getData('createtime') * 1000, + // ]; + $flagaftersale = true; + } + + if (!$flagrefund && $item->refund_status > OrderItem::REFUND_STATUS_NOREFUND) { + $data['refundNum']++; + // $data['refundArr'][] = [ + // 'counter' => 1, + // 'createtime' => $order->getData('createtime') * 1000, + // ]; + $flagrefund = true; + } + } + + if (!$aftersaleIng && $flagnoSend) { + // 存在正在售后中的订单,不算待发货(和订单列表保持一直) + $data['noSendNum']++; + // $data['noSendArr'][] = [ + // 'counter' => 1, + // 'createtime' => $order->getData('createtime') * 1000, + // ]; + } + } + } + + $this->success('获取成功', null, $data); + } + + + + public function ranking() + { + $goods = Goods::limit(5)->order('sales', 'desc')->select(); + foreach ($goods as $key => $gd) { + $gd->append(['real_sales']); + $result = OrderItem::field('sum(goods_num * goods_price) as sale_total_money')->where('goods_id', $gd['id']) + ->whereExists(function ($query) use ($gd) { + $order_table_name = (new Order())->getQuery()->getTable(); + $table_name = (new OrderItem())->getQuery()->getTable(); + + $query->table($order_table_name)->where($table_name . '.order_id=' . $order_table_name . '.id') + ->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED]); // 已支付的订单 + })->find(); + + $gd['sale_total_money'] = $result['sale_total_money'] ?: 0; + } + + $searchHistory = new SearchHistory(); + + $this->success('获取成功', null, [ + 'goods' => $goods, + 'hot_search' => $searchHistory->hotSearch() + ]); + } +} diff --git a/application/admin/controller/shopro/Feedback.php b/application/admin/controller/shopro/Feedback.php new file mode 100644 index 0000000..057e9e5 --- /dev/null +++ b/application/admin/controller/shopro/Feedback.php @@ -0,0 +1,112 @@ +model = new FeedbackModel(); + } + + + /** + * 查看 + * + * @return Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $feedbacks = $this->model->sheepFilter()->with('user')->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $feedbacks); + } + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $detail = $this->model->with('user')->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $detail); + } + + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + $params = $this->request->only(['status', 'remark']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + /** + * 删除优惠券 + * + * @param string $id 要删除的意见反馈 + * @return void + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/admin/controller/shopro/PayConfig.php b/application/admin/controller/shopro/PayConfig.php new file mode 100644 index 0000000..dfbfa2e --- /dev/null +++ b/application/admin/controller/shopro/PayConfig.php @@ -0,0 +1,223 @@ +model = new PayConfigModel; + } + + + /** + * 支付配置列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $payConfigs = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $payConfigs); + } + + + + + /** + * 添加支付配置 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'name', 'type', 'params', 'status' + ]); + $this->svalidate($params, ".add"); + $this->svalidate($params['params'], '.' . $params['type']); // 验证对应的支付参数是否设置完整 + + $this->model->save($params); + + $this->success('保存成功'); + } + + + /** + * 支付配置详情 + * + * @param $id + */ + public function detail($id) + { + $payConfig = $this->model->where('id', $id)->find(); + if (!$payConfig) { + $this->error(__('No Results were found')); + } + + $payConfig->append(['params']); + $this->success('获取成功', null, pay_config_show($payConfig)); + } + + + + /** + * 修改支付配置 + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'name', 'params', 'status' + ]); + $this->svalidate($params); + + $payConfig = $this->model->where('id', $id)->find(); + if (!$payConfig) { + $this->error(__('No Results were found')); + } + + if (isset($params['params'])) { + $this->svalidate($params['params'], '.' . $payConfig['type']); // 验证对应的支付参数是否设置完整 + } + + $payConfig->save($params); + $this->success('更新成功'); + } + + + + /** + * 删除支付配置 + * + * @param string $id 要删除的商品分类列表 + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + /** + * 获取所有支付配置 + */ + public function select() + { + $payConfigs = $this->model->sheepFilter()->normal() + ->field('id, name, type,status') + ->select(); + + $this->success('获取成功', null, $payConfigs); + } + + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $goods = $this->model->onlyTrashed()->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $goods); + } + + + /** + * 还原(支持批量) + * + * @param $id + * @return \think\Response + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $items = $this->model->onlyTrashed()->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + $count += $item->restore(); + } + + return $count; + }); + + if ($result) { + $this->success('还原成功', null, $result); + } else { + $this->error(__('No rows were updated')); + } + } + + + /** + * 销毁(支持批量) + * + * @param $id + * @return \think\Response + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + if ($id !== 'all') { + $items = $this->model->onlyTrashed()->whereIn('id', $id)->select(); + } else { + $items = $this->model->onlyTrashed()->select(); + } + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $config) { + // 删除商品 + $count += $config->delete(true); + } + return $count; + }); + + if ($result) { + $this->success('销毁成功', null, $result); + } + $this->error('销毁失败'); + } +} diff --git a/application/admin/controller/shopro/Paylog.php b/application/admin/controller/shopro/Paylog.php new file mode 100644 index 0000000..a20f62f --- /dev/null +++ b/application/admin/controller/shopro/Paylog.php @@ -0,0 +1,37 @@ +model = new \app\admin\model\shopro\vip\Paylog; + + } + + + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + + +} diff --git a/application/admin/controller/shopro/Share.php b/application/admin/controller/shopro/Share.php new file mode 100644 index 0000000..a82e46e --- /dev/null +++ b/application/admin/controller/shopro/Share.php @@ -0,0 +1,30 @@ +model = new ShareModel(); + } + + /** + * 查看用户分享记录 + */ + public function index() + { + $share_id = $this->request->param('id'); + + $data = ShareModel::with(['user' => function ($query) { + return $query->field(['id', 'nickname', 'avatar']); + }])->where('share_id', $share_id)->sheepFilter()->paginate($this->request->param('list_rows', 8)); + + $this->success('', null, $data); + } +} diff --git a/application/admin/controller/shopro/Vip.php b/application/admin/controller/shopro/Vip.php new file mode 100644 index 0000000..e0a58f8 --- /dev/null +++ b/application/admin/controller/shopro/Vip.php @@ -0,0 +1,37 @@ +model = new \app\admin\model\shopro\Vip; + $this->view->assign("statusList", $this->model->getStatusList()); + } + + + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + + +} diff --git a/application/admin/controller/shopro/Withdraw.php b/application/admin/controller/shopro/Withdraw.php new file mode 100644 index 0000000..d9b9227 --- /dev/null +++ b/application/admin/controller/shopro/Withdraw.php @@ -0,0 +1,126 @@ +model = new WithdrawModel; + $this->logModel = new WithdrawLogModel; + } + + + + /** + * 提现列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $withdraws = $this->model->sheepFilter()->with(['user'])->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $withdraws); + } + + + /** + * 提现日志 + */ + public function log($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $logs = $this->logModel->where('withdraw_id', $id)->order('id desc')->select(); + $morphs = [ + 'user' => UserModel::class, + 'admin' => AdminModel::class, + 'system' => AdminModel::class + ]; + $logs = morph_to($logs, $morphs, ['oper_type', 'oper_id']); + $logs = $logs->toArray(); + + // 解析操作人信息 + foreach ($logs as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + $this->success('获取成功', null, $logs); + } + + + public function handle($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->param(); + $action = $params['action'] ?? null; + $refuse_msg = $params['refuse_msg'] ?? ''; + if ($action == 'refuse' && !$refuse_msg) { + $this->error('请输入拒绝原因'); + } + + $ids = is_array($id) ? $id : explode(',', $id); + foreach ($ids as $key => $id) { + Db::startTrans(); + try { + $withdraw = $this->model->lock(true)->where('id', $id)->find(); + if (!$withdraw) { + $this->error(__('No Results were found')); + } + $withdrawLib = new WithdrawLibrary($withdraw->user_id); + + switch ($action) { + case 'agree': + $withdraw = $withdrawLib->handleAgree($withdraw); + break; + case 'agree&withdraw': + $withdraw = $withdrawLib->handleAgree($withdraw); + $withdraw = $withdrawLib->handleWithdraw($withdraw); + break; + case 'withdraw': + $withdraw = $withdrawLib->handleWithdraw($withdraw); + break; + case 'refuse': + $withdraw = $withdrawLib->handleRefuse($withdraw, $refuse_msg); + break; + } + + Db::commit(); + } catch (ShoproException $e) { + Db::commit(); // 不回滚,记录错误日志 + $this->error($e->getMessage()); + } catch (HttpResponseException $e) { + $data = $e->getResponse()->getData(); + $message = $data ? ($data['msg'] ?? '') : $e->getMessage(); + $this->error($message); + } catch (\Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + } + + $this->success('处理成功'); + } +} diff --git a/application/admin/controller/shopro/activity/Activity.php b/application/admin/controller/shopro/activity/Activity.php new file mode 100644 index 0000000..cc6822b --- /dev/null +++ b/application/admin/controller/shopro/activity/Activity.php @@ -0,0 +1,321 @@ +model = new ActivityModel; + $this->manager = ActivityFacade::instance(); + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + $type = $this->request->param('type', null); + + if (!$this->request->isAjax()) { + if ($type) { + return $this->view->fetch('shopro/activity/activity/index'); + } + + return $this->view->fetch('shopro/activity/activity/activity'); + } + + $activities = $this->model->sheepFilter()->where('type', $type)->paginate(request()->param('list_rows', 10))->toArray(); + + $items = $activities['data']; + + // 关联活动的商品 + $goodsIds = array_values(array_filter(array_column($items, 'goods_ids'))); + $goodsIdsArr = []; + foreach ($goodsIds as $ids) { + $idsArr = explode(',', $ids); + $goodsIdsArr = array_merge($goodsIdsArr, $idsArr); + } + $goodsIdsArr = array_values(array_filter(array_unique($goodsIdsArr))); + if ($goodsIdsArr) { + // 查询商品 + $goods = GoodsModel::where('id', 'in', $goodsIdsArr)->select(); + $goods = array_column($goods, null, 'id'); + } + foreach ($items as $key => $activity) { + $items[$key]['goods'] = []; + if ($activity['goods_ids']) { + $idsArr = explode(',', $activity['goods_ids']); + foreach ($idsArr as $id) { + if (isset($goods[$id])) { + $items[$key]['goods'][] = $goods[$id]; + } + } + } + } + + $activities['data'] = $items; + + $this->success('获取成功', null, $activities); + } + + + // 获取数据类型 + public function getType() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $typeList = $this->model->typeList(); + + $result = [ + 'type_list' => $typeList, + ]; + + $data = []; + foreach ($result as $key => $list) { + $data[$key][] = ['name' => '全部', 'type' => 'all']; + + foreach ($list as $k => $v) { + $data[$key][] = [ + 'name' => $v, + 'type' => $k + ]; + } + } + + $this->success('获取成功', null, $data); + } + + + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'title', 'goods_ids', 'type', 'prehead_time', 'start_time', 'end_time', + 'rules', 'richtext_id', 'richtext_title', 'goods_list' + ]); + if (isset($params['goods_list'])) { + $params['goods_list'] = json_decode($params['goods_list'], true); + } + $this->svalidate($params, ".add"); + + Db::transaction(function () use ($params) { + $this->manager->save($params); + }); + + $this->success('保存成功'); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $activity = $this->model->where('id', $id)->find(); + if (!$activity) { + $this->error(__('No Results were found')); + } + $activity->goods_list = $activity->goods_list; + $activity->rules = $activity->rules; + + $this->success('获取成功', null, $activity); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'title', 'goods_ids', 'prehead_time', 'start_time', 'end_time', + 'rules', 'richtext_id', 'richtext_title', 'goods_list' + ]); + if (isset($params['goods_list'])) { + $params['goods_list'] = json_decode($params['goods_list'], true); + } + $this->svalidate($params); + + $id = explode(',', $id); + $items = $this->model->whereIn('id', $id)->select(); + + Db::transaction(function () use ($items, $params) { + foreach ($items as $activity) { + $this->manager->update($activity, $params); + } + }); + + $this->success('更新成功'); + } + + + /** + * 获取活动商品规格并且初始化 + */ + public function skus() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->param(); + $id = $params['id'] ?? 0; + $goods_id = $params['goods_id'] ?? 0; + $activity_type = $params['activity_type'] ?? ''; + $start_time = $params['start_time'] ?? ''; + $end_time = $params['end_time'] ?? ''; + $prehead_time = $params['prehead_time'] ?? ''; + + if ($start_time && $end_time && $activity_type) { + // 如果存在开始结束时间,并且是要修改 + $goodsList = [$goods_id => ['id' => $goods_id]]; + + $this->checkActivityConflict([ + 'type' => $activity_type, + 'classify' => $this->model->getClassify($activity_type), + 'start_time' => $start_time, + 'end_time' => $end_time, + 'prehead_time' => $prehead_time + ], $goodsList, $id); + } + + // 商品规格 + $skus = SkuModel::with('children')->where('goods_id', $goods_id)->where('parent_id', 0)->select(); + + // 获取规格 + $skuPrices = SkuPriceModel::with(['activity_sku_price' => function ($query) use ($id) { + $query->where('activity_id', $id); + }])->where('goods_id', $goods_id)->select(); + + + //编辑 + $activitySkuPrices = []; + foreach ($skuPrices as $k => $skuPrice) { + $activitySkuPrices[$k] = $skuPrice->activity_sku_price ? $skuPrice->activity_sku_price->toArray() : []; + // 活动规格数据初始化 + if (!$activitySkuPrices[$k]) { + $activitySkuPrices[$k]['id'] = 0; + $activitySkuPrices[$k]['status'] = 'down'; + $activitySkuPrices[$k]['price'] = ''; + $activitySkuPrices[$k]['stock'] = ''; + $activitySkuPrices[$k]['sales'] = '0'; + $activitySkuPrices[$k]['goods_sku_price_id'] = $skuPrice->id; + } + + // 个性化初始化每个活动的 规格 字段 + $activitySkuPrices[$k] = $this->manager->showSkuPrice($activity_type, $activitySkuPrices[$k]); + } + + $this->success('获取成功', null, [ + 'skus' => $skus, + 'sku_prices' => $skuPrices, + 'activity_sku_prices' => $activitySkuPrices, + ]); + } + + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type'); + $activities = $this->model->sheepFilter()->whereIn('type', $type)->paginate(request()->param('list_rows', 10))->toArray(); + + $this->success('获取成功', null, $activities); + } + + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $this->manager->delete($item); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type'); + $activities = $this->model->onlyTrashed()->sheepFilter()->where('type', $type)->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $activities); + } +} diff --git a/application/admin/controller/shopro/activity/Groupon.php b/application/admin/controller/shopro/activity/Groupon.php new file mode 100644 index 0000000..cadc494 --- /dev/null +++ b/application/admin/controller/shopro/activity/Groupon.php @@ -0,0 +1,124 @@ +model = new GrouponModel; + } + + + + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $groupons = $this->model->sheepFilter()->with(['goods', 'user', 'grouponLogs']) + ->paginate(request()->param('list_rows', 10)); + + $this->success('获取成功', null, $groupons); + } + + + + /** + * 团详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $groupon = $this->model->with(['goods', 'user', 'grouponLogs'])->where('id', $id)->find(); + if (!$groupon) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $groupon); + } + + + /** + * 添加虚拟用户(人满自动成团) + * + * @param Request $request + * @param integer $id + */ + public function addUser($id) + { + $groupon = $this->model->where('id', $id)->find(); + if (!$groupon) { + $this->error(__('No Results were found')); + } + + $activity = ActivityModel::where('id', $groupon['activity_id'])->find(); + if (!$activity) { + $this->error('活动不存在'); + } + if ($groupon['status'] != 'ing' || $groupon['current_num'] > $groupon['num']) { + $this->error('团已完成或已失效'); + } + + $avatar = $this->request->param('avatar', ''); + $nickname = $this->request->param('nickname', ''); + $user = ['avatar' => $avatar, 'nickname' => $nickname]; + + Db::transaction(function () use ($activity, $groupon, $user) { + // 增加人数 + $this->finishFictitiousGroupon($activity, $groupon, false, 1, [$user]); + }); + + $this->success('操作成功'); + } + + + + /** + * 解散团,自动退款 + * + * @param Request $request + * @param integer $id + */ + public function invalid($id) + { + $admin = $this->auth->getUserInfo(); + $admin = Admin::find($admin['id']); + $groupon = $this->model->where('id', $id)->find(); + + if ($groupon['status'] != 'ing') { + $this->error('团已完成或已失效'); + } + + Db::transaction(function () use ($groupon, $admin) { + // 解散团,并退款 + $this->invalidRefundGroupon($groupon, $admin); + }); + + $this->success('操作成功'); + } +} diff --git a/application/admin/controller/shopro/app/ScoreShop.php b/application/admin/controller/shopro/app/ScoreShop.php new file mode 100644 index 0000000..46f7fe4 --- /dev/null +++ b/application/admin/controller/shopro/app/ScoreShop.php @@ -0,0 +1,313 @@ +model = new ScoreSkuPrice; + $this->goodsModel = new GoodsModel(); + } + + + /** + * 积分商城商品列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $scoreGoodsIds = $this->model->group('goods_id')->column('goods_id'); + + $scoreGoods = $this->goodsModel->sheepFilter()->with(['score_sku_prices']) + ->whereIn('id', $scoreGoodsIds) + ->paginate($this->request->param('list_rows', 10))->each(function($goods) { + $goods->score_price = $goods->score_price; + $goods->score_sales = $goods->score_sales; + $goods->score_stock = $goods->score_stock; + }); + + $this->success('获取成功', null, $scoreGoods); + } + + + /** + * skuPrices列表 + */ + public function skuPrices($goods_id) + { + $skuPrices = $this->model->up()->with(['sku_price'])->where('goods_id', $goods_id)->select(); + $skuPrices = collection($skuPrices)->each(function ($skuPrice) { + $skuPrice->goods_sku_ids = $skuPrice->goods_sku_ids; + $skuPrice->goods_sku_text = $skuPrice->goods_sku_text; + $skuPrice->image = $skuPrice->image; + $skuPrice->score_price = $skuPrice->score_price; + }); + + $this->success('获取成功', null, $skuPrices); + } + + + + /** + * 添加积分商城商品 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'goods_id', 'sku_prices' + ]); + $this->svalidate($params, ".add"); + + // 检查是否已经是积分商品了 + $count = $this->model->where('goods_id', $params['goods_id'])->count(); + if ($count) { + error_stop('该商品已经是积分商城商品了'); + } + + Db::transaction(function () use ($params) { + $this->editSkuPrices($params['goods_id'], $params); + }); + $this->success('保存成功'); + } + + + public function skus($goods_id) + { + $skus = SkuModel::with('children')->where('goods_id', $goods_id)->where('parent_id', 0)->select(); + $skuPrices = SkuPriceModel::with(['score_sku_price'])->where('goods_id', $goods_id)->select(); + + //编辑 + $scoreSkuPrices = []; + foreach ($skuPrices as $k => $skuPrice) { + $scoreSkuPrices[$k] = $skuPrice->score_sku_price ? : []; + // 活动规格数据初始化 + if (!$scoreSkuPrices[$k]) { + $scoreSkuPrices[$k]['id'] = 0; + $scoreSkuPrices[$k]['status'] = 'down'; + $scoreSkuPrices[$k]['price'] = ''; + $scoreSkuPrices[$k]['score'] = ''; + $scoreSkuPrices[$k]['stock'] = ''; + $scoreSkuPrices[$k]['goods_sku_price_id'] = $skuPrice->id; + } + } + + $this->success('获取成功', null, [ + 'skus' => $skus, + 'sku_prices' => $skuPrices, + 'score_sku_prices' => $scoreSkuPrices + ]); + } + + + /** + * 编辑积分商城商品 + */ + public function edit($goods_id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'sku_prices' + ]); + $this->svalidate($params, ".edit"); + + Db::transaction(function () use ($goods_id, $params) { + $this->editSkuPrices($goods_id, $params); + }); + $this->success('更新成功'); + } + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type', 'page'); + $scoreGoodsIds = $this->model->group('goods_id')->column('goods_id'); + + $scoreGoods = $this->goodsModel->sheepFilter()->with(['score_sku_prices']) + ->whereIn('id', $scoreGoodsIds); + + if ($type == 'select') { + // 普通结果 + $scoreGoods = $scoreGoods->select(); + $scoreGoods = collection($scoreGoods); + } else { + // 分页结果 + $scoreGoods = $scoreGoods->paginate($this->request->param('list_rows', 10)); + } + + $scoreGoods = $scoreGoods->each(function ($goods) { + $goods->score_price = $goods->score_price; + $goods->score_sales = $goods->score_sales; + $goods->score_stock = $goods->score_stock; + }); + + $this->success('获取成功', null, $scoreGoods); + } + + + + /** + * 删除积分商城商品 + * + * @param string $id 要删除的积分商城商品 id + * @return void + */ + public function delete($goods_id) + { + if (empty($goods_id)) { + $this->error(__('Parameter %s can not be empty', 'goods_id')); + } + + $goodsIds = explode(',', $goods_id); + $list = $this->model->whereIn('goods_id', $goodsIds)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $scoreGoodsIds = $this->model->onlyTrashed()->group('goods_id')->column('goods_id'); + $scoreGoods = $this->goodsModel->sheepFilter()->with(['del_score_sku_prices']) + ->whereIn('id', $scoreGoodsIds) + ->paginate($this->request->param('list_rows', 10))->each(function ($skuPrice) { + $deleteTimes = collection($skuPrice->del_score_sku_prices)->column('deletetime'); + $skuPrice->deletetime = $deleteTimes ? max($deleteTimes) : null; // 取用积分规格的删除时间 + }); + + $this->success('获取成功', null, $scoreGoods); + } + + + /** + * 还原(支持批量) + * + * @param $id + * @return \think\Response + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'goods_id')); + } + + $goodsIds = explode(',', $id); + Db::transaction(function () use ($goodsIds) { + foreach ($goodsIds as $goods_id) { + $count = $this->model->where('goods_id', $goods_id)->count(); + if ($count) { + error_stop('商品 ID 为 ' . $goods_id . ' 的商品已经是积分商品了,不可还原'); + } + + $list = $this->model->onlyTrashed()->whereIn('goods_id', $goods_id)->select(); + foreach ($list as $goods) { + $goods->restore(); + } + } + }); + + $this->success('还原成功'); + } + + + /** + * 销毁(支持批量) + * + * @param $id + * @return \think\Response + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'goods_id')); + } + + $goodsIds = explode(',', $id); + Db::transaction(function () use ($goodsIds) { + if (!in_array('all', $goodsIds)) { + foreach ($goodsIds as $goods_id) { + $list = $this->model->onlyTrashed()->whereIn('goods_id', $goods_id)->select(); + foreach ($list as $goods) { + $goods->delete(true); + } + } + } else { + $list = $this->model->onlyTrashed()->select(); + foreach ($list as $goods) { + $goods->delete(true); + } + } + }); + + $this->success('销毁成功'); + } + + + private function editSkuPrices($goods_id, $params) + { + //下架全部规格 + $this->model->where('goods_id', $goods_id)->update(['status' => 'down']); + + foreach ($params['sku_prices'] as $key => $skuPrice) { + if ($skuPrice['id'] == 0) { + unset($skuPrice['id']); + } + unset($skuPrice['sales']); //不更新销量 + unset($skuPrice['createtime'], $skuPrice['updatetime'], $skuPrice['deletetime']); // 不手动更新时间 + $skuPrice['goods_id'] = $goods_id; + + $model = new ScoreSkuPrice; + if (isset($skuPrice['id'])) { + $model = $this->model->find($skuPrice['id']); + $model = $model ? : new ScoreSkuPrice; + } + + $model->save($skuPrice); + } + } +} diff --git a/application/admin/controller/shopro/app/mplive/Goods.php b/application/admin/controller/shopro/app/mplive/Goods.php new file mode 100644 index 0000000..e569a67 --- /dev/null +++ b/application/admin/controller/shopro/app/mplive/Goods.php @@ -0,0 +1,163 @@ +request->isAjax()) { + return $this->view->fetch(); + } + + $model = new MpliveGoodsModel(); + $list = $model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + + // 批量更新直播商品状态 + // $this->updateAuditStatusByGoods($list); + + $this->success('获取成功', null, $list); + } + + // 直播间商品详情 + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $goods = (new MpliveGoodsModel)->findOrFail($id); + + $this->success('', null, $goods); + } + + // 创建直播间商品并提交审核 + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->param(); + + $data = [ + "coverImgUrl" => $this->uploadMedia($params['cover_img_url']), + "name" => $params['name'], + "priceType" => $params['price_type'], + "price" => $params['price'], + "price2" => $params['price_type'] === 1 ? "" : $params['price2'], // priceType为2或3时必填 + "url" => $params['url'], + ]; + + $res = $this->wechat->broadcast->create($data); + + $this->catchLiveError($res); + + $params['id'] = $res['goodsId']; + $params['audit_id'] = $res['auditId']; + $params['audit_status'] = 1; + + (new MpliveGoodsModel)->save($params); + + $this->success("操作成功"); + } + + // 直播商品审核 + public function audit($id) + { + $id = intval($id); + $act = $this->request->param('act'); + + $goods = MpliveGoodsModel::where('id', $id)->find(); + if (!$goods) { + error_stop('未找到该商品'); + } + // 撤回审核 + if ($act === 'reset') { + $auditId = $goods->audit_id; + if ($auditId) { + $res = $this->wechat->broadcast->resetAudit($auditId, $id); + $this->catchLiveError($res); + } + } + + // 重新审核 + if ($act === 'resubmit') { + $res = $this->wechat->broadcast->resubmitAudit($id); + $this->catchLiveError($res); + $goods->audit_id = $res['auditId']; + $goods->save(); + } + + return $this->status($id); + } + + // 删除直播商品 + public function delete($id) + { + $id = intval($id); + $res = $this->wechat->broadcast->delete($id); + + $this->catchLiveError($res); + + MpliveGoodsModel::where('id', $id)->delete(); + $this->success('操作成功'); + } + + // 更新直播商品 + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->param(); + + $data = [ + 'goodsId' => $id, + "coverImgUrl" => $this->uploadMedia($params['cover_img_url']), + "name" => $params['name'], + "priceType" => $params['price_type'], + "price" => $params['price'], + "price2" => $params['price_type'] === 1 ? "" : $params['price2'], // priceType为2或3时必填 + "url" => $params['url'], + ]; + + $res = $this->wechat->broadcast->update($data); + + $this->catchLiveError($res); + + $goods = MpliveGoodsModel::where('id', $id)->find(); + $goods->save($data); + + $this->success('操作成功'); + } + + // 更新直播商品状态 + public function status($id) + { + $res = $this->wechat->broadcast->getGoodsWarehouse([$id]); + + $this->catchLiveError($res); + + $list = $res['goods']; + + foreach ($list as $key => $goods) { + $mpliveGoods = MpliveGoodsModel::where('id', $goods['goods_id'])->find(); + if ($mpliveGoods) { + $mpliveGoods->audit_status = $goods['audit_status']; + $mpliveGoods->third_party_tag = $goods['third_party_tag']; + $mpliveGoods->save(); + } + } + + $this->success('操作成功'); + } +} diff --git a/application/admin/controller/shopro/app/mplive/Index.php b/application/admin/controller/shopro/app/mplive/Index.php new file mode 100644 index 0000000..5cff66e --- /dev/null +++ b/application/admin/controller/shopro/app/mplive/Index.php @@ -0,0 +1,81 @@ +wechat = Wechat::miniProgram(); + + (new ServiceProvider())->register($this->wechat); + } + + // 上传临时素材 + protected function uploadMedia($path) + { + $filesystem = config('filesystem.default'); + if ($filesystem !== 'local' || is_url($path)) { + $body = Http::get(cdnurl($path, true)); + $dir = RUNTIME_PATH . 'storage' . DS . 'temp'; + if (!is_dir($dir)) { + @mkdir($dir, 0755, true); + } + $temp_path = $dir . $this->getBaseName($path); + file_put_contents($temp_path, $body); + } else { + $temp_path = ROOT_PATH . 'public' . $path; + } + + if (!isset($temp_path) || empty($temp_path)) { + error_stop("上传失败,不是一个有效的文件: " . $path); + } + + $media = $this->wechat->media; + $res = $media->uploadImage($temp_path); + @unlink($temp_path); // 删除临时文件 + if (isset($res['media_id'])) { + return $res['media_id']; + } + return ''; + } + + // 解析图片文件名 + private function getBaseName($path) + { + if (strpos($path, 'mmbiz.qpic.cn') !== false) { + return DS . gen_random_str(8) . '.jpg'; + } + + return basename($path); + } + + // 转义直播错误信息 + protected function catchLiveError($response) + { + if (!isset($response['errcode'])) { + error_stop("未知错误"); + } + + $errorMap = MpliveRoomModel::ERR_CODE; + if (isset($response['errcode']) && ($response['errcode'] !== 0 && $response['errcode'] !== 1001)) { + if (isset($errorMap[$response['errcode']])) { + error_stop("{$errorMap[$response['errcode']]} [错误码: {$response['errcode']}]"); + } else { + error_stop("{$response['errmsg']} [错误码: {$response['errcode']}]"); + } + } + } +} diff --git a/application/admin/controller/shopro/app/mplive/Room.php b/application/admin/controller/shopro/app/mplive/Room.php new file mode 100644 index 0000000..c6d5a35 --- /dev/null +++ b/application/admin/controller/shopro/app/mplive/Room.php @@ -0,0 +1,201 @@ +request->isAjax()) { + return $this->view->fetch(); + } + + $list = (new MpliveRoomModel)->sheepFilter()->select(); + + $this->success('', null, $list); + } + + // 直播间详情 + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $room = (new MpliveRoomModel)->where('roomid', $id)->findOrFail(); + + $this->success('', null, $room); + } + + // 同步直播间列表 + public function sync() + { + $res = $this->wechat->broadcast->getRooms(); + $data = []; + + $this->catchLiveError($res); + + MpliveRoomModel::where('roomid', '>', 0)->delete(); + foreach ($res['room_info'] as $room) { + $room['status'] = $room['live_status']; + $room['type'] = $room['live_type']; + $data[] = $room; + } + + MpliveRoomModel::strict(false)->insertAll($data); + $list = MpliveRoomModel::select(); + + $this->success('', null, $list); + } + + // 创建直播间 + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->param(); + + $data = [ + 'name' => $params['name'], // 房间名字 + 'coverImg' => $this->uploadMedia($params['cover_img']), // 通过 uploadfile 上传,填写 mediaID + 'shareImg' => $this->uploadMedia($params['share_img']), //通过 uploadfile 上传,填写 mediaID + 'feedsImg' => $this->uploadMedia($params['feeds_img']), //通过 uploadfile 上传,填写 mediaID + 'startTime' => $params['start_time'], // 开始时间 + 'endTime' => $params['end_time'], // 结束时间 + 'anchorName' => $params['anchor_name'], // 主播昵称 + 'anchorWechat' => $params['anchor_wechat'], // 主播微信号 + 'subAnchorWechat' => $params['sub_anchor_wechat'], // 主播副号微信号 + 'isFeedsPublic' => $params['is_feeds_public'], // 是否开启官方收录,1 开启,0 关闭 + 'type' => $params['type'], // 直播类型,1 推流 0 手机直播 + 'closeLike' => $params['close_like'], // 是否关闭点赞 1:关闭 + 'closeGoods' => $params['close_goods'], // 是否关闭商品货架,1:关闭 + 'closeComment' => $params['close_comment'], // 是否开启评论,1:关闭 + 'closeReplay' => $params['close_replay'], // 是否关闭回放 1 关闭 + 'closeKf' => $params['close_kf'], // 是否关闭客服,1 关闭 + ]; + + $res = $this->wechat->broadcast->createLiveRoom($data); + + $this->catchLiveError($res); + + return $this->sync(); + } + + // 更新直播间 + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->param(); + + $data = [ + 'id' => $id, + 'name' => $params['name'], // 房间名字 + 'coverImg' => $this->uploadMedia($params['cover_img']), // 通过 uploadfile 上传,填写 mediaID + 'shareImg' => $this->uploadMedia($params['share_img']), //通过 uploadfile 上传,填写 mediaID + 'feedsImg' => $this->uploadMedia($params['feeds_img']), //通过 uploadfile 上传,填写 mediaID + 'startTime' => $params['start_time'], // 开始时间 + 'endTime' => $params['end_time'], // 结束时间 + 'anchorName' => $params['anchor_name'], // 主播昵称 + 'anchorWechat' => $params['anchor_wechat'], // 主播昵称 + 'isFeedsPublic' => $params['is_feeds_public'], // 是否开启官方收录,1 开启,0 关闭 + 'type' => $params['type'], // 直播类型,1 推流 0 手机直播 + 'closeLike' => $params['close_like'], // 是否关闭点赞 1:关闭 + 'closeGoods' => $params['close_goods'], // 是否关闭商品货架,1:关闭 + 'closeComment' => $params['close_comment'], // 是否开启评论,1:关闭 + 'closeReplay' => $params['close_replay'], // 是否关闭回放 1 关闭 + 'closeKf' => $params['close_kf'], // 是否关闭客服,1 关闭 + ]; + + $res = $this->wechat->broadcast->updateLiveRoom($data); + + $this->catchLiveError($res); + + return $this->sync(); + } + + // 删除直播间 + public function delete($id) + { + $res = $this->wechat->broadcast->deleteLiveRoom([ + 'id' => $id, + ]); + + $this->catchLiveError($res); + + MpliveRoomModel::where('roomid', $id)->delete(); + $this->success('操作成功'); + } + + // 推流地址 + public function pushUrl($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $res = $this->wechat->broadcast->getPushUrl([ + 'roomId' => $id + ]); + + $this->catchLiveError($res); + + $this->success('', null, ['pushAddr' => $res['pushAddr']]); + } + + // 分享二维码 + public function qrcode($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $res = $this->wechat->broadcast->getShareQrcode([ + 'roomId' => $id + ]); + + $this->catchLiveError($res); + + $this->success('', null, ['pagePath' => $res['pagePath'], 'cdnUrl' => $res['cdnUrl']]); + } + + // 查看回放 + public function playback($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $res = $this->wechat->broadcast->getPlaybacks((int)$id, (int)$start = 0, (int)$limit = 10); + + $this->catchLiveError($res); + + $data = $res['live_replay']; + + $this->success('', null, $data); + } + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = (new MpliveRoomModel)->sheepFilter()->select(); + + $this->success('', null, $list); + } +} diff --git a/application/admin/controller/shopro/chat/CommonWord.php b/application/admin/controller/shopro/chat/CommonWord.php new file mode 100644 index 0000000..ff03ee6 --- /dev/null +++ b/application/admin/controller/shopro/chat/CommonWord.php @@ -0,0 +1,129 @@ +model = new ChatCommonWord; + } + + /** + * 常用语列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $commonWords = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $commonWords); + } + + + /** + * 常用语添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['room_id', 'name', 'status', 'weigh']); + $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params, ".add"); + + $this->model->save($params); + $this->success('保存成功', null, $this->model); + } + + + + /** + * 常用语详情 + * + * @param $id + */ + public function detail($id) + { + $commonWord = $this->model->where('id', $id)->find(); + if (!$commonWord) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $commonWord); + } + + + + /** + * 常用语编辑 + * + * @param $id + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['room_id', 'name', 'status', 'weigh']); + $this->request->has('content') && $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params); + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + /** + * 删除(支持批量) + * + * @param $id + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/admin/controller/shopro/chat/CustomerService.php b/application/admin/controller/shopro/chat/CustomerService.php new file mode 100644 index 0000000..5285128 --- /dev/null +++ b/application/admin/controller/shopro/chat/CustomerService.php @@ -0,0 +1,205 @@ +model = new ChatCustomerService; + $this->adminModel = new Admin; + } + + /** + * 客服列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $customerService = $this->model->sheepFilter()->with('customer_service_user')->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $customerService); + } + + + /** + * 客服添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['name', 'avatar', 'room_id', 'max_num', 'auth', 'auth_id']); + $this->svalidate($params, ".add"); + + if ($this->checkHasAuthId($params['auth'], $params['auth_id'], $params['room_id'])) { + error_stop('该身份已绑定其他客服'); + } + + $data = Db::transaction(function () use ($params) { + $this->model->allowField(true)->save($params); + + $customerServiceUser = CustomerServiceUser::create([ + 'customer_service_id' => $this->model->id, + 'auth' => $params['auth'], + 'auth_id' => $params['auth_id'], + ]); + + return $customerServiceUser; + }); + $this->success('保存成功', null, $data); + } + + + + /** + * 客服详情 + * + * @param $id + */ + public function detail($id) + { + $customerService = $this->model->with(['customer_service_user'])->where('id', $id)->find(); + if (!$customerService) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $customerService); + } + + + + /** + * 客服编辑 + * + * @param $id + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['name', 'avatar', 'room_id', 'max_num', 'auth', 'auth_id']); + $this->svalidate($params); + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->with('customer_service_user')->select(); + Db::transaction(function () use ($list, $params) { + foreach ($list as $customerService) { + $customerService->allowField(true)->save($params); + + $customerServiceUser = $customerService['customer_service_user']; + + // 编辑了客服身份所有者 + if ($params['auth'] != $customerServiceUser['auth'] || $params['auth_id'] != $customerServiceUser['auth_id']) { + // 验证新的身份是否已经被绑定别的客服 + if ($this->checkHasAuthId($params['auth'], $params['auth_id'], $params['room_id'])) { + error_stop('该身份已绑定其他客服'); + } + + // 删除老的身份 + CustomerServiceUser::{'auth' . ucfirst($customerServiceUser['auth'])}($customerServiceUser['auth_id']) + ->where('customer_service_id', $customerService['id'])->delete(); + + // 添加新的身份 + $customerServiceUser = CustomerServiceUser::create([ + 'customer_service_id' => $customerService->id, + 'auth' => $params['auth'], + 'auth_id' => $params['auth_id'], + ]); + } + } + }); + + $this->success('更新成功'); + } + + + /** + * 客服用语 + * + * @param $id + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + Db::transaction(function () use ($list) { + foreach ($list as $customerService) { + // 删除客服的身份 + CustomerServiceUser::where('customer_service_id', $customerService['id'])->delete(); + + $customerService->delete(); + } + }); + + $this->success('删除成功'); + } + + + /** + * 检验是否已经被绑定了客服(一个管理员或者用户只能是一种客服) + * + * @param string $auth + * @param integer $auth_id + * @return void + */ + private function checkHasAuthId($auth, $auth_id, $room_id) + { + $customerServiceUser = CustomerServiceUser::{'auth' . ucfirst($auth)}($auth_id)->with('customer_service')->find(); + + if ($customerServiceUser) { + $customerService = $customerServiceUser['customer_service']; + if ($customerService && $customerService['room_id'] == $room_id) { + return true; + } + } + + return false; + } + + + /** + * 获取管理员列表 + * + * @return void + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $id = $this->request->param('id', 0); + $room_id = $this->request->param('room_id', 'admin'); + + // 已经被设置为客服的管理员 + $adminIds = CustomerServiceUser::whereExists(function ($query) use ($room_id) { + $table_name = $this->model->getQuery()->getTable(); + $query->table($table_name)->where('room_id', $room_id)->where('customer_service_id=id'); + })->where('auth', 'admin')->where('customer_service_id', '<>', $id)->column('auth_id'); + + // 正常的,并且排除了已经设置为客服的管理员 + $admins = $this->adminModel->where('status', 'normal')->whereNotIn('id', $adminIds)->select(); + + $this->success('获取成功', null, $admins); + } +} diff --git a/application/admin/controller/shopro/chat/Index.php b/application/admin/controller/shopro/chat/Index.php new file mode 100644 index 0000000..5488864 --- /dev/null +++ b/application/admin/controller/shopro/chat/Index.php @@ -0,0 +1,38 @@ +getUnifiedToken('admin:' . $admin['id']); // 统一验证 token + + // 客服配置 + $chatSystem = sheep_config('chat.system'); + + // 初始化 socket ssl 类型, 默认 cert + $ssl = $chatSystem['ssl'] ?? 'none'; + $chat_domain = ($ssl == 'none' ? 'http://' : 'https://') . request()->host(true) . ($ssl == 'reverse_proxy' ? '' : (':' . $chatSystem['port'])) . '/chat'; + + $data = [ + 'token' => $token, + 'chat_domain' => $chat_domain, + 'default_rooms' => CustomerService::defaultRooms() + ]; + $this->success('获取成功', null, $data); + } +} diff --git a/application/admin/controller/shopro/chat/Question.php b/application/admin/controller/shopro/chat/Question.php new file mode 100644 index 0000000..1180656 --- /dev/null +++ b/application/admin/controller/shopro/chat/Question.php @@ -0,0 +1,133 @@ +model = new ChatQuestion; + } + + /** + * 猜你想问列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $questions = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $questions); + } + + + /** + * 猜你想问添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['room_id', 'title', 'status', 'weigh']); + $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params, ".add"); + + $this->model->save($params); + $this->success('保存成功', null, $this->model); + } + + + + /** + * 猜你想问详情 + * + * @param $id + */ + public function detail($id) + { + $question = $this->model->where('id', $id)->find(); + if (!$question) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $question); + } + + + + /** + * 猜你想问编辑 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['room_id', 'title', 'status', 'weigh']); + $this->request->has('content') && $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params); + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + /** + * 删除猜你想问 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/admin/controller/shopro/chat/Record.php b/application/admin/controller/shopro/chat/Record.php new file mode 100644 index 0000000..69ea07d --- /dev/null +++ b/application/admin/controller/shopro/chat/Record.php @@ -0,0 +1,37 @@ +model = new ChatRecord; + } + + /** + * 聊天列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $records = $this->model->sheepFilter()->order('id desc')->paginate($this->request->param('list_rows', 10)); + + $morphs = [ + 'customer' => \app\admin\model\shopro\chat\User::class, + 'customer_service' => \app\admin\model\shopro\chat\CustomerService::class, + ]; + $records = morph_to($records, $morphs, ['sender_identify', 'sender_id']); + + $this->success('获取成功', null, $records); + } + +} diff --git a/application/admin/controller/shopro/chat/User.php b/application/admin/controller/shopro/chat/User.php new file mode 100644 index 0000000..5725ad5 --- /dev/null +++ b/application/admin/controller/shopro/chat/User.php @@ -0,0 +1,62 @@ +model = new ChatUser; + } + + /** + * 会话列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $user = $this->model->sheepFilter()->with(['user', 'customer_service'])->where('auth', 'user')->order('id desc')->paginate(request()->param('list_rows', 10)); + $this->success('获取成功', null, $user); + } + + + /**· + * 删除会话 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + Db::transaction(function () use ($list) { + foreach ($list as $user) { + // 删除这个会话的所有服务记录 + ServiceLog::where('chat_user_id', $user->id)->delete(); + + // 删除这个会话的所有聊天记录 + Record::where('chat_user_id', $user->id)->delete(); + + // 删除这个会话 + $user->delete(); + } + }); + + $this->success('删除成功'); + } +} diff --git a/application/admin/controller/shopro/commission/Agent.php b/application/admin/controller/shopro/commission/Agent.php new file mode 100644 index 0000000..aaf9c52 --- /dev/null +++ b/application/admin/controller/shopro/commission/Agent.php @@ -0,0 +1,250 @@ +model = new AgentModel(); + } + + /** + * 查看 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->with(['user.parent_user', 'level_info', 'level_status_info', 'upgrade_level'])->paginate($this->request->param('list_rows', 10)); + + $this->success('分销商列表', null, $list); + } + + /** + * 详情 + * + * @param $id + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $detail = $this->model->with(['user.parent_user', 'level_info', 'level_status_info', 'upgrade_level'])->where('user_id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + + $this->success('分销商详情', null, $detail); + } + + + /** + * 团队 + * + * @param $id + */ + public function team($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $detail = $this->model->with(['user.parent_user', 'level_info'])->where('user_id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + + $detail->agent_team = AgentModel::hasWhere('user', function ($query) use ($detail) { + return $query->where('parent_user_id', $detail->user_id); + })->with(['user', 'level_info'])->select(); + $this->success('分销商详情', null, $detail); + } + + // 选择分销商 + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $data = $this->model->sheepFilter()->with(['user', 'level_info', 'level_status_info', 'upgrade_level']) + ->paginate($this->request->param('list_rows', 10)); + + $this->success('选择分销商', null, $data); + } + + /** + * 编辑 + * + * @param $id + */ + public function edit($id = null) + { + $params = $this->request->only(['status', 'upgrade_lock', 'level_status', 'level', 'apply_info']); + + $result = Db::transaction(function () use ($id, $params) { + $row = $this->model->with(['user', 'level_info', 'level_status_info', 'upgrade_level'])->where('user_id', $id)->find(); + if (!$row) { + $this->error('未找到该分销商'); + } + + foreach ($params as $field => $value) { + switch ($field) { + case 'status': // 修改状态 + return $this->changeStatus($row, $value); + break; + case 'level_status': // 审核等级 + return $this->changeLevelStatus($row, $value); + break; + case 'level': // 修改等级 + return $this->changeLevel($row, $value); + break; + default: + return $row->save([$field => $value]); + } + } + }); + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } + + + // 修改状态 + private function changeStatus($row, $value) + { + $result = $row->save(['status' => $value]); + if ($result) { + LogModel::add($row->user_id, 'agent', ['type' => 'status', 'value' => $value]); + (new AgentService($row->user_id))->createAsyncAgentUpgrade(); + } + return $result; + } + + // 审核等级 + private function changeLevelStatus($row, $value) + { + if ($row->level_status == 0 && $value > 0) { + $this->error('非法操作'); + } + + if ($value == 0) { // 拒绝操作 + return $row->save(['level_status' => 0]); + } else { // 同意操作 + if ($row->upgrade_level) { + $result = $row->save(['level_status' => 0, 'level' => $row->upgrade_level->level]); + if ($result) { + LogModel::add($row->user_id, 'agent', ['type' => 'level', 'level' => $row->upgrade_level]); + (new AgentService($row->user_id))->createAsyncAgentUpgrade(); + } + return $result; + } + } + return false; + } + + // 修改等级 + private function changeLevel($row, $value) + { + $level = LevelModel::find($value); + if ($level) { + $result = $row->save(['level' => $level->level]); + if ($result) { + LogModel::add($row->user_id, 'agent', ['type' => 'level', 'level' => $level]); + (new AgentService($row->user_id))->createAsyncAgentUpgrade(); + } + return $result; + } else { + $this->error('未找到该等级'); + } + } + + // 更换推荐人 + public function changeParentUser($id) + { + $userAgent = new AgentService($id); + + if (!$userAgent->user) { + $this->error('未找到该用户'); + } + + $parentUserId = $this->request->param('parent_user_id', 0); + + // 更换推荐人检查 + if ($parentUserId != 0) { + $parentAgent = new AgentService($parentUserId); + if (!$parentAgent->isAgentAvaliable()) { + $this->error('选中用户暂未成为分销商,不能成为推荐人'); + } + if (!$this->checkChangeParentAgent($id, $parentUserId)) { + $this->error('不能绑定该上级'); + } + LogModel::add($parentUserId, 'share', ['user' => $userAgent->user]); + + if ($userAgent->isAgentAvaliable()) { + LogModel::add($id, 'bind', ['user' => $parentAgent->user ?? NULL]); + } + } + + $lastParentUserId = $userAgent->user->parent_user_id; + + $userAgent->user->parent_user_id = $parentUserId; + $userAgent->user->save(); + + if ($lastParentUserId > 0) { + $userAgent->createAsyncAgentUpgrade($lastParentUserId); + } + + if ($parentUserId > 0) { + $userAgent->createAsyncAgentUpgrade($parentUserId); + } + $this->success('绑定成功'); + } + + // 递归往上找推荐人,防止出现推荐循环 + private function checkChangeParentAgent($userId, $parentUserId) + { + if ($userId == $parentUserId) { + + $this->error('推荐人不能是本人'); + } + if ($parentUserId == 0) { + return true; + } + + $parentAgent = UserModel::find($parentUserId); + + if ($parentAgent) { + if ($parentAgent->parent_user_id == $userId) { + $this->error("已选中分销商的上级团队中已存在该用户"); + } + if ($parentAgent->parent_user_id == 0) { + return true; + } else { + return $this->checkChangeParentAgent($userId, $parentAgent->parent_user_id); + } + } + + return false; + } +} diff --git a/application/admin/controller/shopro/commission/Goods.php b/application/admin/controller/shopro/commission/Goods.php new file mode 100644 index 0000000..7d92d7f --- /dev/null +++ b/application/admin/controller/shopro/commission/Goods.php @@ -0,0 +1,89 @@ +model = new CommissionGoodsModel(); + $this->goodsModel = new GoodsModel(); + } + + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $data = $this->goodsModel->sheepFilter()->with('commission_goods')->paginate($this->request->param('list_rows', 10)); + $this->success('分销商品列表', null, $data); + } + + /** + * 详情 + * + * @param $id + */ + public function detail($id) + { + $goodsList = collection(GoodsModel::with(['commission_goods'])->whereIn('id', $id)->select())->each(function ($goods) { + $goods->skus = $goods->skus; + $goods->sku_prices = $goods->sku_prices; + }); + + $config = sheep_config('shop.commission'); + $this->success('分销商品详情', null, [ + 'goods' => $goodsList, + 'config' => $config + ]); + } + + /** + * 设置佣金(支持批量) + * + * @param $id + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + // 接受全部参数 + $params = $this->request->only(['status', 'self_rules', 'commission_order_status', 'commission_config', 'commission_rules']); + + $result = Db::transaction(function () use ($id, $params) { + $count = 0; + $ids = explode(',', $id); + + foreach ($ids as $goods_id) { + if ($row = $this->model->get($goods_id)) { + $row->save($params); + } else { + $model = new CommissionGoodsModel(); + $params['goods_id'] = $goods_id; + $model->save($params); + } + $count++; + } + + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } +} diff --git a/application/admin/controller/shopro/commission/Level.php b/application/admin/controller/shopro/commission/Level.php new file mode 100644 index 0000000..efe785b --- /dev/null +++ b/application/admin/controller/shopro/commission/Level.php @@ -0,0 +1,144 @@ +model = new LevelModel(); + } + + /** + * 查看 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $defaultLevel = $this->model->find(1); + if (!$defaultLevel) { + $this->model->save([ + 'name' => '默认等级', + 'level' => 1, + 'commission_rules' => [ + 'commission_1' => '0.00', + 'commission_2' => '0.00', + 'commission_3' => '0.00' + ] + ]); + } + $list = $this->model->sheepFilter()->select(); + + $this->success('全部等级', null, $list); + } + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['name', 'level', 'image', 'commission_rules', 'upgrade_type', 'upgrade_rules']); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + /** + * 编辑 + * + * @param $id + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['level', 'name', 'image', 'commission_rules', 'upgrade_type', 'upgrade_rules']); + + $result = Db::transaction(function () use ($id, $params) { + + $this->svalidate($params); + + $data = $this->model->where('level', $id)->find(); + if (!$data) { + $this->error(__('No Results were found')); + } + + return $data->save($params); + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->get($id); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('等级详情', null, $detail); + } + + /** + * 删除 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $result = Db::transaction(function () use ($id) { + return $this->model->where('level', $id)->delete(); + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + // 选择分销商等级 + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $data = $this->model->sheepFilter()->field('level, name, image, commission_rules')->select(); + $this->success('选择等级', null, $data); + } +} diff --git a/application/admin/controller/shopro/commission/Log.php b/application/admin/controller/shopro/commission/Log.php new file mode 100644 index 0000000..fadb92c --- /dev/null +++ b/application/admin/controller/shopro/commission/Log.php @@ -0,0 +1,50 @@ +model = new LogModel(); + } + + + /** + * 查看 + * + * @return Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $logs = $this->model->sheepFilter()->with(['agent'])->paginate($this->request->param('list_rows', 10)); + + $morphs = [ + 'user' => UserModel::class, + 'admin' => AdminModel::class, + 'system' => AdminModel::class + ]; + $logs = morph_to($logs, $morphs, ['oper_type', 'oper_id']); + $logs = $logs->toArray(); + + // 格式化操作人信息 + foreach ($logs['data'] as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + + $this->success('获取成功', null, $logs); + } +} diff --git a/application/admin/controller/shopro/commission/Order.php b/application/admin/controller/shopro/commission/Order.php new file mode 100644 index 0000000..f26dcea --- /dev/null +++ b/application/admin/controller/shopro/commission/Order.php @@ -0,0 +1,323 @@ +model = new OrderModel(); + } + + /** + * 查看 + */ + public function index() + { + if (!$this->request->isAjax()) { + $exportConfig = (new \addons\shopro\library\Export())->getConfig(); + $this->assignconfig("save_type", $exportConfig['save_type'] ?? 'download'); + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->with(['buyer', 'agent', 'order', 'rewards.agent', 'order_item'])->paginate($this->request->param('list_rows', 10)); + $list = $list->toArray(); + + // 统计数据 + $count = [ + 'total' => $list['total'], + 'total_amount' => 0, + 'total_commission' => 0, + 'total_commission_cancel' => 0, + 'total_commission_accounted' => 0, + 'total_commission_back' => 0, + 'total_commission_pending' => 0 + ]; + + $orders = $this->model->sheepFilter()->with(['rewards'])->select(); + collection($orders)->each(function ($order) use (&$count) { + $count['total_amount'] += $order['amount']; + foreach ($order['rewards'] as $reward) { + $count['total_commission'] += $reward['commission']; + switch ($reward['status']) { + case RewardModel::COMMISSION_REWARD_STATUS_ACCOUNTED: + $count['total_commission_accounted'] += $reward['commission']; + break; + case RewardModel::COMMISSION_REWARD_STATUS_BACK: + $count['total_commission_back'] += $reward['commission']; + break; + case RewardModel::COMMISSION_REWARD_STATUS_PENDING: + $count['total_commission_pending'] += $reward['commission']; + break; + case RewardModel::COMMISSION_REWARD_STATUS_CANCEL: + $count['total_commission_cancel'] += $reward['commission']; + break; + } + } + }); + + $this->success('', null, [ + 'list' => $list, + 'count' => $count + ]); + } + + /** + * 结算佣金 + * + * @return Response + */ + public function confirm() + { + $params = $this->request->only(['commission_reward_id', 'commission_order_id']); + + try { + Db::transaction(function () use ($params) { + $rewardService = new RewardService('admin'); + if (isset($params['commission_reward_id'])) { + return $rewardService->runCommissionReward($params['commission_reward_id']); + } elseif (isset($params['commission_order_id'])) { + return $rewardService->runCommissionRewardByOrder($params['commission_order_id']); + } + }); + } catch (HttpResponseException $e) { + $data = $e->getResponse()->getData(); + $message = $data ? ($data['msg'] ?? '') : $e->getMessage(); + $this->error($message); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + + $this->success('操作成功'); + } + + /** + * 取消结算 + * + * @return Response + */ + public function cancel() + { + $params = $this->request->only(['commission_reward_id', 'commission_order_id']); + + try { + + Db::transaction(function () use ($params) { + $rewardService = new RewardService('admin'); + if (isset($params['commission_reward_id'])) { + return $rewardService->cancelCommissionReward($params['commission_reward_id']); + } elseif (isset($params['commission_order_id'])) { + return $rewardService->backCommissionRewardByOrder($params['commission_order_id']); + } + }); + } catch (HttpResponseException $e) { + $data = $e->getResponse()->getData(); + $message = $data ? ($data['msg'] ?? '') : $e->getMessage(); + $this->error($message); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + $this->success('操作成功'); + } + + /** + * 退回已结算佣金 + */ + public function back() + { + $params = $this->request->only(['commission_reward_id', 'commission_order_id', 'deduct_order_money']); + + try { + Db::transaction(function () use ($params) { + $rewardService = new RewardService('admin'); + if (isset($params['commission_reward_id'])) { + return $rewardService->backCommissionReward($params['commission_reward_id']); + } elseif (isset($params['commission_order_id'])) { + return $rewardService->backCommissionRewardByOrder($params['commission_order_id'], $params['deduct_order_money']); + } + }); + } catch (HttpResponseException $e) { + $data = $e->getResponse()->getData(); + $message = $data ? ($data['msg'] ?? '') : $e->getMessage(); + $this->error($message); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + $this->success('操作成功'); + } + + /** + * 修改待结算佣金 + */ + public function edit($id = null) + { + $params = $this->request->only(['commission_reward_id', 'commission']); + $reward = RewardModel::get($params['commission_reward_id']); + if (!$reward) { + $this->error(__('No Results were found')); + } + + $reward->commission = $params['commission']; + $result = $reward->save(); + if ($result) { + $this->success('操作成功'); + } + $this->error('操作失败'); + } + + + + + public function export() + { + $cellTitles = [ + // 主要字段 + 'commission_order_id' => 'Id', + 'order_sn' => '订单号', + 'goods_title' => '商品名称', + 'goods_sku_text' => '商品规格', + 'goods_price' => '商品价格', + 'goods_num' => '购买数量', + 'refund_status_text' => '退款状态', + 'buyer_nickname' => '下单用户', + 'buyer_mobile' => '手机号', + 'share_nickname' => '推广分销商', + 'share_mobile' => '手机号', + 'commission_reward_status_text' => '佣金状态', + 'reward_event_text' => '结算方式', + 'commission_time' => '结算时间', + 'reward_type_text' => '商品结算方式', + 'amount' => '商品结算金额', + 'commission_order_status_text' => '分销商业绩', + 'total_commission' => '分销总金额', + 'total_commissioned' => '到账金额', + // 佣金明细 + 'reward_agent_nickname' => '分佣用户', + 'reward_agent_mobile' => '分佣手机号', + 'reward_commission' => '分佣金额', + 'reward_status_text' => '分佣状态', + 'reward_type_text' => '入账方式', + 'reward_commission_time' => '结算时间', + ]; + + // 数据总条数 + $total = $this->model->sheepFilter()->count(); + if ($total <= 0) { + $this->error('导出数据为空'); + } + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => '分销订单列表', + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => true, + 'sub_start_cell' => 'reward_agent_nickname', + 'sub_field' => 'rewards' + ]; + + $total_amount = 0; + $total_commission = 0; + $total_commission_cancel = 0; + $total_commission_accounted = 0; + $total_commission_back = 0; + $total_commission_pending = 0; + $result = $export->export($params, function ($pages) use (&$total_amount, &$total_commission, &$total_commission_cancel, &$total_commission_accounted, &$total_commission_back, &$total_commission_pending, $total) { + $datas = $this->model->sheepFilter()->with(['buyer', 'agent', 'order', 'rewards.agent', 'order_item']) + ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows']) + ->select(); + + $datas = collection($datas); + $datas->each(function ($commissionOrder) use (&$total_amount, &$total_commission, &$total_commission_cancel, &$total_commission_accounted, &$total_commission_back, &$total_commission_pending, $total) { + $total_amount += $commissionOrder['amount']; + foreach ($commissionOrder['rewards'] as $reward) { + $total_commission += $reward['commission']; + switch ($reward['status']) { + case RewardModel::COMMISSION_REWARD_STATUS_ACCOUNTED: + $total_commission_accounted += $reward['commission']; + break; + case RewardModel::COMMISSION_REWARD_STATUS_BACK: + $total_commission_back += $reward['commission']; + break; + case RewardModel::COMMISSION_REWARD_STATUS_PENDING: + $total_commission_pending += $reward['commission']; + break; + case RewardModel::COMMISSION_REWARD_STATUS_CANCEL: + $total_commission_cancel += $reward['commission']; + break; + } + } + })->toArray(); + + $newDatas = []; + foreach ($datas as $commissionOrder) { + $commission = 0; + $commissioned = 0; + foreach ($commissionOrder['rewards'] as $reward) { + if ($reward['status'] == 1) { + $commissioned += $reward['commission']; + } + $commission += $reward['commission']; + } + + $data = [ + 'commission_order_id' => $commissionOrder['id'], + 'order_sn' => $commissionOrder['order'] ? $commissionOrder['order']['order_sn'] : '', + 'goods_title' => $commissionOrder['order_item'] ? '#' . $commissionOrder['order_item']['goods_id'] . ' ' . $commissionOrder['order_item']['goods_title'] : '', + 'goods_sku_text' => $commissionOrder['order_item'] ? $commissionOrder['order_item']['goods_sku_text'] : '', + 'goods_price' => $commissionOrder['order_item'] ? $commissionOrder['order_item']['goods_price'] : '', + 'goods_num' => $commissionOrder['order_item'] ? $commissionOrder['order_item']['goods_num'] : '', + 'refund_status_text' => $commissionOrder['order_item'] ? $commissionOrder['order_item']['refund_status_text'] : '', + 'buyer_nickname' => $commissionOrder['buyer'] ? $commissionOrder['buyer']['nickname'] : '-', + 'buyer_mobile' => $commissionOrder['buyer'] ? $commissionOrder['buyer']['mobile'] . ' ' : '-', + 'share_nickname' => $commissionOrder['agent'] ? $commissionOrder['agent']['nickname'] : '-', + 'share_mobile' => $commissionOrder['agent'] ? $commissionOrder['agent']['mobile'] . ' ' : '-', + + // 这里循环 rewards 佣金详情 + + 'commission_reward_status_text' => $commissionOrder['commission_reward_status_text'], + 'reward_event_text' => $commissionOrder['reward_event_text'], + 'commission_time' => $commissionOrder['commission_time'], + 'reward_type_text' => $commissionOrder['reward_type_text'], + 'amount' => $commissionOrder['amount'], + 'commission_order_status_text' => $commissionOrder['commission_order_status_text'], + 'total_commission' => $commission, + 'total_commissioned' => $commissioned, + ]; + + $rewardsItems = []; + foreach ($commissionOrder['rewards'] as $reward) { + $rewardsItems[] = [ + 'reward_agent_nickname' => $reward['agent'] ? $reward['agent']['nickname'] : '', + 'reward_agent_mobile' => $reward['agent'] ? $reward['agent']['mobile'] : '', + 'reward_commission' => $reward['commission'], + 'reward_status_text' => $reward['status_text'], + 'reward_type_text' => $reward['type_text'], + 'reward_commission_time' => $reward['commission_time'] + ]; + } + + $data['rewards'] = $rewardsItems; + + $newDatas[] = $data; + } + + if ($pages['is_last_page']) { + $newDatas[] = ['order_id' => "商品总订单数:" . $total . ";商品结算总金额:¥" . $total_amount . ";分佣总金额:¥" . $total_commission . ";已取消佣金:¥" . $total_commission_cancel . ";已退回佣金:¥" . $total_commission_back . ";未结算佣金:" . $total_commission_pending . ";已结算佣金:" . $total_commission_accounted]; + } + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } +} diff --git a/application/admin/controller/shopro/commission/Reward.php b/application/admin/controller/shopro/commission/Reward.php new file mode 100644 index 0000000..5897e50 --- /dev/null +++ b/application/admin/controller/shopro/commission/Reward.php @@ -0,0 +1,108 @@ +model = new RewardModel(); + } + + /** + * 查看 + */ + public function index() + { + if (!$this->request->isAjax()) { + $exportConfig = (new \addons\shopro\library\Export())->getConfig(); + $this->assignconfig("save_type", $exportConfig['save_type'] ?? 'download'); + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->with(['buyer', 'agent', 'order', 'order_item'])->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $list); + } + + + public function export() + { + $cellTitles = [ + 'reward_id' => 'Id', + 'order_sn' => '订单号', + 'buyer_nickname' => '下单用户', + 'buyer_mobile' => '手机号', + 'agent_nickname' => '分销用户', + 'agent_mobile' => '分销手机号', + 'original_commission' => '原始佣金', + 'commission' => '分销佣金', + 'commission_level' => '执行层级', + 'agent_level' => '执行等级', + 'status_text' => '状态', + 'type_text' => '入账方式', + 'commission_time' => '结算时间' + ]; + + // 数据总条数 + $total = $this->model->sheepFilter()->count(); + if ($total <= 0) { + $this->error('导出数据为空'); + } + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => '佣金明细列表', + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => false, + ]; + + $total_commission = 0; + $result = $export->export($params, function ($pages) use (&$total_commission, $total) { + $datas = $this->model->sheepFilter()->with(['buyer', 'agent', 'order', 'order_item']) + ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows']) + ->select(); + + $datas = collection($datas); + $datas->each(function ($order) { + })->toArray(); + + $newDatas = []; + foreach ($datas as &$reward) { + $data = [ + 'reward_id' => $reward['id'], + 'order_sn' => $reward['order'] ? $reward['order']['order_sn'] : '', + 'buyer_nickname' => $reward['buyer'] ? $reward['buyer']['nickname'] : '-', + 'buyer_mobile' => $reward['buyer'] ? $reward['buyer']['mobile'] . ' ' : '-', + 'agent_nickname' => $reward['agent'] ? $reward['agent']['nickname'] : '-', + 'agent_mobile' => $reward['agent'] ? $reward['agent']['mobile'] . ' ' : '-', + 'original_commission' => $reward['original_commission'], + 'commission' => $reward['commission'], + 'commission_level' => $reward['commission_level'], + 'agent_level' => $reward['agent_level'], + 'status_text' => $reward['status_text'], + 'type_text' => $reward['type_text'], + 'commission_time' => $reward['commission_time'], + ]; + + $newDatas[] = $data; + } + + $total_commission += array_sum(array_column($newDatas, 'commission')); + + if ($pages['is_last_page']) { + $newDatas[] = ['reward_id' => "总数:" . $total . ";总佣金金额:¥" . $total_commission . ";"]; + } + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } +} diff --git a/application/admin/controller/shopro/data/Area.php b/application/admin/controller/shopro/data/Area.php new file mode 100644 index 0000000..bd7fb5e --- /dev/null +++ b/application/admin/controller/shopro/data/Area.php @@ -0,0 +1,170 @@ +model = new \app\admin\model\shopro\data\Area; + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->with('children.children')->where('pid', 0)->select(); // 查询全部 + $this->success('', null, $list); + } + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $level = $this->request->param('level', 'all'); + $with = ['children.children']; + if ($level == 'city') { + $with = ['children']; + } else if ($level == 'province') { + $with = []; + } + + $list = $this->model->with($with)->where('pid', 0)->field('id, name, pid, level')->select(); // 查询全部 + $this->success('', null, $list); + } + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['id', 'pid', 'name']); + + $params['level'] = 'province'; + if (isset($params['pid']) && $params['pid']) { + $parent = $this->model->find($params['pid']); + if (!$parent) { + $this->error(__('No Results were found')); + } + if ($parent['level'] == 'province') { + $params['level'] = 'city'; + } else if ($parent['level'] == 'city') { + $params['level'] = 'district'; + } + } + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($old_id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['id', 'pid', 'name']); + $params['level'] = 'province'; + if (isset($params['pid']) && $params['pid']) { + $parent = $this->model->find($params['pid']); + if (!$parent) { + $this->error(__('No Results were found')); + } + if ($parent['level'] == 'province') { + $params['level'] = 'city'; + } else if ($parent['level'] == 'city') { + $params['level'] = 'district'; + } + } + + $area = $this->model->where('id', 'in', $old_id)->find(); + if (!$area) { + $this->error(__('No Results were found')); + } + + $area->save($params); + $this->success('更新成功', null, $area); + } + + /** + * 删除 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $area = $this->model->where('id', 'in', $id)->find(); + if (!$area) { + $this->error(__('No Results were found')); + } + + $children = $this->model->where('pid', $id)->count(); + if ($children) { + $this->error('请先删除下级地市'); + } + + $area->delete(); + $this->success('删除成功'); + } +} diff --git a/application/admin/controller/shopro/data/Express.php b/application/admin/controller/shopro/data/Express.php new file mode 100644 index 0000000..fa2f465 --- /dev/null +++ b/application/admin/controller/shopro/data/Express.php @@ -0,0 +1,156 @@ +model = new \app\admin\model\shopro\data\Express; + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->request('list_rows', 10)); + $this->success('', null, $list); + } + + + /** + * 选择快递公司 + * + * @return void + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('', null, $list); + } + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['name', 'code', 'weigh']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['name', 'code', 'weigh']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/admin/controller/shopro/data/FakeUser.php b/application/admin/controller/shopro/data/FakeUser.php new file mode 100644 index 0000000..e286a8e --- /dev/null +++ b/application/admin/controller/shopro/data/FakeUser.php @@ -0,0 +1,215 @@ +model = new \app\admin\model\shopro\data\FakeUser; + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('', null, $list); + } + + + /** + * 随机获取一个虚拟用户 + * + * @return void + */ + public function getRandom() + { + $userFake = $this->model->orderRaw('rand()')->find(); + + $userFake ? $this->success('获取成功', null, $userFake) : $this->error('请在数据维护中添加虚拟用户'); + } + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('', null, $list); + } + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['username', 'nickname', 'mobile', 'password', 'avatar', 'gender', 'email']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + /** + * 随机生成用户 + */ + public function random() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + set_time_limit(0); + $num = $this->request->param('num', 1); + + for ($i = 0; $i < $num; $i++) { + $style = [ + 'adventurer', + 'big-smile', + 'bottts', + 'croodles', + 'croodles-neutral', + 'identicon', + 'micah' + ]; + + $username = gen_random_str(mt_rand(6, 15), mt_rand(0, 1)); + $avatarSources = [ + // "https://joeschmoe.io/api/v1/random", // 生成的是 svg ,无法使用 + "https://avatars.dicebear.com/api/%s/" . $username . ".png", + "https://api.multiavatar.com/" . $username . ".png" + ]; + + $avatar_url = $avatarSources[array_rand($avatarSources)]; + $avatar_url = sprintf($avatar_url, $style[array_rand($style)]); + + $store_path = '/uploads/' . date('Ymd') . '/' . md5(time() . mt_rand(1000, 9999)) . '.png'; // 存数据库路径 + $save_path = ROOT_PATH . 'public' . $store_path; // 服务器绝对路径 + image_resize_save($avatar_url, $save_path); + + $fakeUser = new \app\admin\model\shopro\data\FakeUser(); + $fakeUser->username = $username; + $fakeUser->nickname = $username; + $fakeUser->mobile = random_mobile(); + $fakeUser->password = gen_random_str(); + $fakeUser->avatar = cdnurl($store_path, true); // 这里存了完整路径 + $fakeUser->gender = mt_rand(0, 1); + $fakeUser->email = random_email($fakeUser->mobile); + $fakeUser->save(); + } + + $this->success('生成成功'); + } + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['username', 'nickname', 'mobile', 'password', 'avatar', 'gender', 'email']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/admin/controller/shopro/data/Faq.php b/application/admin/controller/shopro/data/Faq.php new file mode 100644 index 0000000..c23378d --- /dev/null +++ b/application/admin/controller/shopro/data/Faq.php @@ -0,0 +1,138 @@ +model = new \app\admin\model\shopro\data\Faq; + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->request('list_rows', 10)); + $this->success('', null, $list); + } + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['title', 'content', 'status']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['title', 'content', 'status']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/admin/controller/shopro/data/Page.php b/application/admin/controller/shopro/data/Page.php new file mode 100644 index 0000000..1609edc --- /dev/null +++ b/application/admin/controller/shopro/data/Page.php @@ -0,0 +1,156 @@ +model = new \app\admin\model\shopro\data\Page; + + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->request('list_rows', 10)); + $this->success('', null, $list); + } + + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->group('group')->with(['children'])->field('group')->select(); + $this->success('', null, $list); + } + + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['name', 'path', 'group']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['name', 'path', 'group']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + +} diff --git a/application/admin/controller/shopro/data/Richtext.php b/application/admin/controller/shopro/data/Richtext.php new file mode 100644 index 0000000..645ebad --- /dev/null +++ b/application/admin/controller/shopro/data/Richtext.php @@ -0,0 +1,170 @@ +model = new \app\admin\model\shopro\data\Richtext; + + } + + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->request('list_rows', 10)); + $this->success('', null, $list); + } + + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type', 'page'); + + $list = $this->model->sheepFilter(); + + if ($type == 'select') { + // 普通结果 + $list = $list->select(); + } elseif ($type == 'find') { + $list = $list->find(); + } else { + // 分页结果 + $list = $list->paginate($this->request->request('list_rows', 10)); + } + + $this->success('', null, $list); + } + + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['title', 'content']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['title', 'content']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + +} diff --git a/application/admin/controller/shopro/decorate/Designer.php b/application/admin/controller/shopro/decorate/Designer.php new file mode 100644 index 0000000..4c8d8bd --- /dev/null +++ b/application/admin/controller/shopro/decorate/Designer.php @@ -0,0 +1,47 @@ +request->param(); + Db::transaction(function () use ($params) { + $decorate = DecorateModel::create([ + 'name' => $params['name'], + 'memo' => $params['memo'], + 'platform' => $params['platform'], + 'status' => 'disabled' + ]); + $pageList = []; + $params['page'] = json_decode($params['page'], true); + foreach ($params['page'] as $page) { + array_push($pageList, [ + 'decorate_id' => $decorate->id, + 'image' => $page['image'] ?? '', + 'type' => $page['type'], + 'page' => json_encode($page['page'], JSON_UNESCAPED_UNICODE) + ]); + } + + PageModel::insertAll($pageList); + $this->downLoadDecorateImages($params['imageList']); + }); + $this->success(); + } + + private function downLoadDecorateImages($imageList) + { + \think\Queue::push('\addons\shopro\job\Designer@redeposit', ['imageList' => $imageList], 'shopro'); + } +} diff --git a/application/admin/controller/shopro/decorate/Page.php b/application/admin/controller/shopro/decorate/Page.php new file mode 100644 index 0000000..60c20dd --- /dev/null +++ b/application/admin/controller/shopro/decorate/Page.php @@ -0,0 +1,76 @@ +model = new PageModel(); + } + /** + * 页面列表 + */ + public function index() + { + return $this->view->fetch(); + } + + /** + * 页面详情 + * + * @param int $id + */ + public function detail($id) + { + $type = $this->request->param('type'); + $page = $this->model->where(['decorate_id' => $id, 'type' => $type])->find(); + $this->success('获取成功', null, $page); + } + + /** + * 编辑 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + $type = $this->request->param('type'); + $page = $this->request->param('page'); + $image = $this->request->param('image'); + + $pageRow = PageModel::where(['decorate_id' => $id, 'type' => $type])->find(); + + operate_filter(); + if ($pageRow) { + $pageRow->page = $page; + $pageRow->image = $image; + $pageRow->save(); + } else { + PageModel::create([ + 'decorate_id' => $id, + 'type' => $type, + 'page' => $page, + 'image' => $image + ]); + } + $this->success('保存成功'); + } + + /** + * 预览 + */ + public function preview() + { + return $this->view->fetch(); + } +} diff --git a/application/admin/controller/shopro/decorate/Template.php b/application/admin/controller/shopro/decorate/Template.php new file mode 100644 index 0000000..229301b --- /dev/null +++ b/application/admin/controller/shopro/decorate/Template.php @@ -0,0 +1,286 @@ +model = new DecorateModel(); + } + + /** + * 查看 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->with(['page' => function ($query) { + return $query->where('type', 'in', ['home', 'user', 'diypage'])->field('decorate_id, type, image'); + }])->select(); + $this->success('获取成功', null, $list); + } + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['type', 'name', 'memo', 'platform']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + /** + * 详情 + * + * @param $id + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + operate_filter(); + $params = $this->request->only([ + 'type', 'name', 'memo', 'platform' + ]); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + + /** + * 复制 + * + * @param $id + */ + public function copy($id) + { + $template = $this->model->where('id', $id)->find(); + if (!$template) { + $this->error(__('No Results were found')); + } + + Db::transaction(function () use ($template) { + $params = [ + 'name' => '复制 ' . $template->name, + 'type' => $template->type, + 'memo' => $template->memo, + 'platform' => $template->platform, + 'status' => 'disabled' + ]; + $newTemplate = $this->model->create($params); + + $pageList = PageModel::where('decorate_id', $template->id)->select(); + + $newPageList = []; + foreach ($pageList as $page) { + $newPageList[] = [ + 'decorate_id' => $newTemplate->id, + 'type' => $page['type'], + 'page' => json_encode($page['page']), + 'image' => $page['image'] + ]; + } + if (count($newPageList) > 0) { + PageModel::insertAll($newPageList); + } + }); + + $this->success('复制成功'); + } + + /** + * 启用/禁用 + * + * @param $id + */ + public function status($id) + { + $status = $this->request->param('status'); + $template = $this->model->where('id', $id)->find(); + if (!$template) { + $this->error(__('No Results were found')); + } + + operate_filter(); + $template->status = $status; + $template->save(); + $this->success('操作成功', null, $template); + } + + + /** + * 删除(支持批量) + * + * @param $id + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + operate_filter(); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $templates = $this->model->onlyTrashed()->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $templates); + } + + + /** + * 还原(支持批量) + * + * @param $id + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $items = $this->model->onlyTrashed()->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + $count += $item->restore(); + } + + return $count; + }); + + if ($result) { + $this->success('还原成功', null, $result); + } else { + $this->error(__('No rows were updated')); + } + } + + + + /** + * 销毁(支持批量) + * + * @param $id + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + if ($id !== 'all') { + $items = $this->model->onlyTrashed()->whereIn('id', $id)->select(); + } else { + $items = $this->model->onlyTrashed()->select(); + } + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + PageModel::where('decorate_id', $item->id)->delete(); + + // 删除商品 + $count += $item->delete(true); + } + return $count; + }); + + if ($result) { + $this->success('销毁成功', null, $result); + } + $this->error('销毁失败'); + } + + + /** + * 选择自定义页面 + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->where('type', 'diypage')->with(['page' => function ($query) { + return $query->field('decorate_id, type, image'); + }])->select(); + + $this->success('获取成功', null, $list); + } +} diff --git a/application/admin/controller/shopro/dispatch/Dispatch.php b/application/admin/controller/shopro/dispatch/Dispatch.php new file mode 100644 index 0000000..056b557 --- /dev/null +++ b/application/admin/controller/shopro/dispatch/Dispatch.php @@ -0,0 +1,232 @@ +model = new DispatchModel; + $this->expressModel = new DispatchExpressModel; + $this->autosendModel = new DispatchAutosendModel; + + $this->dispatch_type = $this->request->param('type', 'express'); + } + + + + /** + * 配送方式列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $dispatchs = $this->model->sheepFilter()->with([$this->dispatch_type])->where('type', $this->dispatch_type)->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $dispatchs); + } + + + + + /** + * 添加配送方式 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'name', 'type', 'status', 'express', 'autosend' + ]); + $this->svalidate($params, ".add"); + $data = $params[$this->dispatch_type] ?? []; + unset($params['express'], $params['autosend']); + + if ($this->dispatch_type == 'express') { + // 验证 express + foreach ($data as $key => $express) { + $this->svalidate($express, '.express'); + } + } else if ($this->dispatch_type == 'autosend') { + // 验证 autosend + $this->svalidate($data, '.autosend'); + } + + Db::transaction(function () use ($params, $data) { + unset($params['createtime'], $params['updatetime'], $params['id']); // 删除时间 + $this->model->allowField(true)->save($params); + + if ($this->dispatch_type == 'express') { + foreach ($data as $key => $express) { + $express['dispatch_id'] = $this->model->id; + $expressModel = new DispatchExpressModel(); + unset($express['createtime'], $express['updatetime'], $express['id']); // 删除时间 + $expressModel->allowField(true)->save($express); + } + } else if ($this->dispatch_type == 'autosend') { + $data['dispatch_id'] = $this->model->id; + $autosendModel = new DispatchAutosendModel(); + unset($data['createtime'], $data['updatetime'], $data['id']); // 删除时间 + $autosendModel->allowField(true)->save($data); + } + }); + + $this->success('保存成功'); + } + + + /** + * 配送方式详情 + * + * @param $id + */ + public function detail($id) + { + $dispatch = $this->model->with([$this->dispatch_type])->where('type', $this->dispatch_type)->where('id', $id)->find(); + if (!$dispatch) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $dispatch); + } + + + + /** + * 修改配送方式 + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'name', 'type', 'status', 'express', 'autosend' + ]); + $this->svalidate($params); + $data = $params[$this->dispatch_type] ?? []; + unset($params['express'], $params['autosend']); + + if ($this->dispatch_type == 'express') { + // 验证 express + foreach ($data as $key => $express) { + $this->svalidate($express, '.express'); + } + } else if ($this->dispatch_type == 'autosend') { + // 验证 autosend + $this->svalidate($data, '.autosend'); + } + + $id = explode(',', $id); + $lists = $this->model->whereIn('id', $id)->select(); + Db::transaction(function () use ($lists, $params, $data) { + foreach ($lists as $dispatch) { + $dispatch->allowField(true)->save($params); + if ($data) { + if ($this->dispatch_type == 'express') { + // 修改,不是只更新状态 + $expressIds = array_column($data, 'id'); + DispatchExpressModel::where('dispatch_id', $dispatch->id)->whereNotIn('id', $expressIds)->delete(); // 先删除被删除的记录 + foreach ($data as $key => $express) { + if (isset($express['id']) && $express['id']) { + $expressModel = $this->expressModel->find($express['id']); + } else { + $expressModel = new DispatchExpressModel(); + $express['dispatch_id'] = $dispatch->id; + } + $express['weigh'] = count($data) - $key; // 权重 + unset($express['createtime'], $express['updatetime']); + $expressModel && $expressModel->allowField(true)->save($express); + } + } else if ($this->dispatch_type == 'autosend') { + if (isset($data['id']) && $data['id']) { + $autosendModel = $this->autosendModel->find($data['id']); + } else { + $autosendModel = new DispatchAutosendModel(); + $data['dispatch_id'] = $dispatch->id; + } + + unset($data['createtime'], $data['updatetime']); // 删除时间 + $autosendModel->allowField(true)->save($data); + } + } + } + }); + $this->success('更新成功'); + } + + + + /** + * 删除配送方式 + * + * @param string $id 要删除的配送方式列表 + * @return void + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + $id = explode(',', $id); + $list = $this->model->with([$this->dispatch_type])->where('type', $this->dispatch_type)->where('id', 'in', $id)->select(); + Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + if ($this->dispatch_type == 'express') { + // 删除相关的 express 数据 + foreach ($item->express as $express) { + $express->delete(); + } + } else if ($this->dispatch_type == 'autosend') { + $item->{$this->dispatch_type}->delete(); + } + + $count += $item->delete(); + } + + return $count; + }); + + $this->success('删除成功'); + } + + + /** + * 获取所有配送模板 + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $dispatchs = $this->model->sheepFilter()->field('id, name, type, status')->normal()->where('type', $this->dispatch_type)->select(); + + $this->success('获取成功', null, $dispatchs); + } +} diff --git a/application/admin/controller/shopro/goods/Comment.php b/application/admin/controller/shopro/goods/Comment.php new file mode 100644 index 0000000..996efaa --- /dev/null +++ b/application/admin/controller/shopro/goods/Comment.php @@ -0,0 +1,247 @@ +model = new CommentModel; + } + + /** + * 商品评价列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $comments = $this->model->sheepFilter()->with(['goods' => function ($query) { + $query->field('id,image,title'); + }, 'order' => function ($query) { + $query->removeOption('soft_delete'); + }, 'order_item'])->paginate($this->request->param('list_rows', 10)); + + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'fake_user' => \app\admin\model\shopro\data\FakeUser::class + ]; + $comments = morph_to($comments, $morphs, ['user_type', 'user_id']); + + $this->success('获取成功', null, $comments); + } + + + + public function detail($id) + { + $comment = $this->model->with(['admin', 'goods' => function ($query) { + $query->field('id,image,title,price'); + }, 'order' => function ($query) { + $query->removeOption('soft_delete'); + }, 'order_item'])->where('id', $id)->find(); + + if (!$comment) { + $this->error(__('No Results were found')); + } + + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'fake_user' => \app\admin\model\shopro\data\FakeUser::class + ]; + $comments = morph_to([$comment], $morphs, ['user_type', 'user_id']); + + $this->success('获取成功', null, $comments->all()[0]); + } + + + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'goods_id', 'user_id', 'level', 'content', 'images', 'status' + ]); + $params['user_type'] = 'fake_user'; + $this->svalidate($params, ".add"); + $fakeUser = FakeUserModel::find($params['user_id']); + $params['user_nickname'] = $fakeUser ? $fakeUser->nickname : null; + $params['user_avatar'] = $fakeUser ? $fakeUser->avatar : null; + + Db::transaction(function () use ($params) { + $this->model->save($params); + }); + $this->success('保存成功'); + } + + + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'status' + ]); + + $id = explode(',', $id); + $list = $this->model->whereIn('id', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $comment) { + $comment->status = $params['status'] ?? 'hidden'; + $count += $comment->save(); + } + + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + public function reply($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'content' + ]); + $this->svalidate($params, '.reply'); + + $comment = $this->model->noReply()->find($id); + if (!$comment) { + $this->error(__('No Results were found')); + } + + $comment->reply_content = $params['content']; + $comment->reply_time = time(); + $comment->admin_id = $this->auth->id; + $comment->save(); + + $this->success('回复成功'); + } + + + /** + * 删除商品评价 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + $this->success('删除成功'); + } + + + + /** + * 评价回收站 + * + * @return void + */ + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $comments = $this->model->onlyTrashed()->sheepFilter()->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $comments); + } + + + /** + * 还原(支持批量) + * + * @param $id + * @return \think\Response + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + $items = $this->model->onlyTrashed()->where('id', 'in', $id)->select(); + Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + $count += $item->restore(); + } + + return $count; + }); + + $this->success('还原成功'); + } + + + /** + * 销毁(支持批量) + * + * @param $id + * @return \think\Response + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + if ($id !== 'all') { + $items = $this->model->onlyTrashed()->whereIn('id', $id)->select(); + } else { + $items = $this->model->onlyTrashed()->select(); + } + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $comment) { + // 删除评价 + $count += $comment->delete(true); + } + return $count; + }); + + if ($result) { + $this->success('销毁成功', null, $result); + } + $this->error('销毁失败'); + } + +} diff --git a/application/admin/controller/shopro/goods/Goods.php b/application/admin/controller/shopro/goods/Goods.php new file mode 100644 index 0000000..565a1f1 --- /dev/null +++ b/application/admin/controller/shopro/goods/Goods.php @@ -0,0 +1,444 @@ +model = new GoodsModel; + $this->activityModel = new ActivityModel; + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $goodsTableName = $this->model->getQuery()->getTable(); + + $goods = $this->model->sheepFilter()->with(['max_sku_price']); + + // 聚合库存 (包含下架的规格) + $skuSql = SkuPriceModel::field('sum(stock) as stock, goods_id as sku_goods_id')->group('goods_id')->buildSql(); + $goods = $goods->join([$skuSql => 'sp'], $goodsTableName . '.id = sp.sku_goods_id', 'left') + ->field("$goodsTableName.*, sp.stock") // ,score.* + ->paginate($this->request->param('list_rows', 10))->each(function ($goods) { + // 获取活动信息 + $goods->activities = $goods->activities; + $goods->promos = $goods->promos; + + $data_type = request()->param('data_type', ''); // 特殊 type 需要处理的数据 + if ($data_type == 'score_shop') { + $goods->is_score_shop = $goods->is_score_shop; + } + }); + + $this->success('获取成功', null, $goods); + } + + + // 获取数据类型 + public function getType() + { + $activityTypes = $this->activityModel->typeList(); + $statusList = $this->model->statusList(); + + $result = [ + 'activity_type' => $activityTypes, + 'status' => $statusList + ]; + + $data = []; + foreach ($result as $key => $list) { + $data[$key][] = ['name' => '全部', 'type' => 'all']; + + foreach ($list as $k => $v) { + $data[$key][] = [ + 'name' => $v, + 'type' => $k + ]; + } + } + + $this->success('获取成功', null, $data); + } + + + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'type', 'title', 'subtitle', 'category_ids', 'image', 'images', 'image_wh', 'params', + 'original_price', 'vip_price', 'price', 'is_sku', 'limit_type', 'limit_num', 'sales_show_type', + 'stock_show_type', 'show_sales', 'service_ids', 'dispatch_type', 'dispatch_id', 'is_offline', 'status', 'weigh', + ]); // likes, views, sales, + $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params, ".add"); + if (!$params['is_sku']) { + // 校验单规格属性 + $sku_params = $this->request->only(['stock', 'stock_warning', 'sn', 'weight', 'cost_price', 'original_price', 'vip_price', 'price']); + $this->svalidate($sku_params, '.sku_params'); + } + + $data = Db::transaction(function () use ($params) { + $this->model->save($params); + + $this->editSku($this->model, 'add'); + }); + $this->success('保存成功', null, $data); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $goods = $this->model->where('id', $id)->find(); + if (!$goods) { + $this->error(__('No Results were found')); + } + $goods->category_ids_arr = $goods->category_ids_arr; + + if ($goods->is_sku) { + $goods->skus = $goods->skus; + $goods->sku_prices = $goods->sku_prices; + } else { + // 将单规格的部分数据直接放到 row 上 + $goodsSkuPrice = SkuPriceModel::where('goods_id', $id)->order('id', 'asc')->find(); + + $goods->stock = $goodsSkuPrice->stock; + $goods->sn = $goodsSkuPrice->sn; + $goods->weight = $goodsSkuPrice->weight; + $goods->stock_warning = $goodsSkuPrice->stock_warning; + $goods->cost_price = $goodsSkuPrice->cost_price; + } + + $content = $goods['content']; + $goods = $goods->toArray(); + $goods['content'] = $content; + $this->success('保存成功', null, $goods); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'type', 'title', 'subtitle', 'image', 'images', 'image_wh', 'params', + 'original_price', 'vip_price', 'price', 'is_sku', 'limit_type', 'limit_num', 'sales_show_type', + 'stock_show_type', 'show_sales', 'service_ids', 'dispatch_type', 'dispatch_id', 'is_offline', 'status', 'weigh', + ]); // likes, views, sales, + $this->request->has('content') && $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params); + $params['category_ids'] = $this->request->param('category_ids', ''); // 分类不判空 + // var_dump($params['category_ids']);exit; + if (isset($params['is_sku']) && !$params['is_sku']) { + // 校验单规格属性 + $sku_params = $this->request->only(['stock_warning', 'sn', 'weight', 'cost_price', 'original_price', 'vip_price', 'price']); + $this->svalidate($sku_params, 'sku_params'); + } + + $id = explode(',', $id); + + $items = $this->model->whereIn('id', $id)->select(); + Db::transaction(function () use ($items, $params) { + foreach ($items as $goods) { + $goods->save($params); + + if (isset($params['is_sku'])) { + // 编辑商品(如果没有 is_sku 就是批量编辑上下架等) + $this->editSku($goods, 'edit'); + } + } + }); + + $this->success('更新成功', null); + } + + + public function addStock($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $goods = $this->model->where('id', $id)->find(); + if (!$goods) { + $this->error(__('No Results were found')); + } + if ($goods->is_sku) { + // 多规格 + $skuPrices = $this->request->post('sku_prices/a', []); + foreach ($skuPrices as $skuPrice) { + if (isset($skuPrice['add_stock']) && $skuPrice['add_stock'] != 0 && $skuPrice['id']) { + $skuPriceModel = SkuPriceModel::where('goods_id', $id)->order('id', 'asc')->find($skuPrice['id']); + if ($skuPriceModel) { + Db::transaction(function () use ($skuPriceModel, $skuPrice) { + $this->addStockToSkuPrice($skuPriceModel, $skuPrice['add_stock'], 'goods'); + }); + } + } + } + } else { + $add_stock = $this->request->param('add_stock', 0); + $skuPriceModel = SkuPriceModel::where('goods_id', $id)->order('id', 'asc')->find(); + + if ($skuPriceModel) { + Db::transaction(function () use ($skuPriceModel, $add_stock) { + $this->addStockToSkuPrice($skuPriceModel, $add_stock, 'goods'); + }); + } + } + + $this->success('补货成功'); + } + + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type', 'page'); + $goodsTableName = $this->model->getQuery()->getTable(); + + $goods = $this->model->sheepFilter()->with(['max_sku_price']); + + // 聚合库存 (包含下架的规格) + $skuSql = SkuPriceModel::field('sum(stock) as stock, goods_id as sku_goods_id')->group('goods_id')->buildSql(); + $goods = $goods->join([$skuSql => 'sp'], $goodsTableName . '.id = sp.sku_goods_id', 'left') + ->field("$goodsTableName.*, sp.stock"); // ,score.* + + if ($type == 'select') { + // 普通结果 + $goods = collection($goods->select()); + } else { + // 分页结果 + $goods = $goods->paginate($this->request->param('list_rows', 10)); + } + + $goods = $goods->each(function ($goods) { + // 获取活动信息 + $goods->activities = $goods->activities; + $goods->promos = $goods->promos; + + $data_type = $this->request->param('data_type', ''); // 特殊 type 需要处理的数据 + if ($data_type == 'score_shop') { + $goods->is_score_shop = $goods->is_score_shop; + } + }); + + $this->success('获取成功', null, $goods); + } + + + + /** + * 获取指定活动相关商品 + * + * @param Request $request + * @return void + */ + public function activitySelect() + { + $activity_id = $this->request->param('activity_id'); + $need_buyers = $this->request->param('need_buyers', 0); // 需要查询哪些人在参与活动 + $activity = $this->activityModel->where('id', $activity_id)->find(); + if (!$activity) { + $this->error(__('No Results were found')); + } + $goodsIds = $activity->goods_ids ? explode(',', $activity->goods_ids) : []; + + // 存一下,获取器获取指定活动的时候会用到 + foreach ($goodsIds as $id) { + session('goods-activity_id:' . $id, $activity_id); + } + $service = new GoodsService(function ($goods) use ($need_buyers) { + if ($need_buyers) { + $goods->buyers = $goods->buyers; + } + $goods->activity = $goods->activity; + return $goods; + }); + + $goods = $service->activity($activity_id)->whereIds($goodsIds)->show()->select(); + $goods = collection($goods)->toArray(); // 可以将里面的单个 model也转为数组 + foreach ($goods as &$gd) { + unset($gd['new_sku_prices'], $gd['activity']); + } + $this->success('获取成功', null, $goods); + } + + + + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + // 删除相关库存预警记录 + StockWarningModel::destroy(function ($query) use ($item) { + $query->where('goods_id', $item->id); + }); + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $goods = $this->model->onlyTrashed()->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $goods); + } + + + /** + * 还原(支持批量) + * + * @param $id + * @return \think\Response + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $items = $this->model->onlyTrashed()->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + $count += $item->restore(); + } + + return $count; + }); + + if ($result) { + $this->success('还原成功', null, $result); + } else { + $this->error(__('No rows were updated')); + } + } + + + /** + * 销毁(支持批量) + * + * @param $id + * @return \think\Response + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + if ($id !== 'all') { + $items = $this->model->onlyTrashed()->whereIn('id', $id)->select(); + } else { + $items = $this->model->onlyTrashed()->select(); + } + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $goods) { + // 删除商品相关的规格,规格记录 + SkuModel::where('goods_id', $goods->id)->delete(); + SkuPriceModel::where('goods_id', $goods->id)->delete(); + + // 删除商品 + $count += $goods->delete(true); + } + return $count; + }); + + if ($result) { + $this->success('销毁成功', null, $result); + } + $this->error('销毁失败'); + } +} diff --git a/application/admin/controller/shopro/goods/Service.php b/application/admin/controller/shopro/goods/Service.php new file mode 100644 index 0000000..75cc72f --- /dev/null +++ b/application/admin/controller/shopro/goods/Service.php @@ -0,0 +1,157 @@ +model = new ServiceModel; + } + + + /** + * 服务保障列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $services = $this->model->sheepFilter()->paginate(request()->param('list_rows', 10)); + + $this->success('获取成功', null, $services); + } + + + + + /** + * 添加服务保障 + * + * @return \think\Response + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'name', 'image', 'description' + ]); + $this->svalidate($params, ".add"); + + Db::transaction(function () use ($params) { + $this->model->save($params); + }); + $this->success('保存成功'); + } + + + /** + * 服务保障详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $service = $this->model->where('id', $id)->find(); + + if (!$service) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $service); + } + + + + /** + * 修改服务保障 + * + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'name', 'image', 'description' + ]); + $this->svalidate($params, ".edit"); + + $id = explode(',', $id); + $list = $this->model->whereIn('id', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $count += $item->save($params); + } + return $count; + }); + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + + /** + * 删除服务标签 + * + * @param string $id 要删除的服务保障列表 + * @return void + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + $this->success('删除成功'); + } + + + /** + * 获取所有服务列表 + * + * @return \think\Response + */ + public function select() + { + $services = $this->model->field('id, name')->select(); + + $this->success('获取成功', null, $services); + } +} diff --git a/application/admin/controller/shopro/goods/SkuPrice.php b/application/admin/controller/shopro/goods/SkuPrice.php new file mode 100644 index 0000000..8f10124 --- /dev/null +++ b/application/admin/controller/shopro/goods/SkuPrice.php @@ -0,0 +1,57 @@ +model = new SkuPriceModel; + } + + /** + * skuPrices列表 + * + * @return \think\Response + */ + public function index() + { + $goods_id = $this->request->param('goods_id'); + $skuPrices = $this->model->where('goods_id', $goods_id)->select(); + + $this->success('获取成功', null, $skuPrices); + } + + + + /** + * skuPrices编辑 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + $params = $this->request->only([ + 'status', + ]); + + $id = explode(',', $id); + $items = $this->model->whereIn('id', $id)->select(); + Db::transaction(function () use ($items, $params) { + foreach ($items as $skuPrice) { + $skuPrice->save($params); + } + }); + + $this->success('更新成功'); + } +} diff --git a/application/admin/controller/shopro/goods/StockLog.php b/application/admin/controller/shopro/goods/StockLog.php new file mode 100644 index 0000000..d3c4bd8 --- /dev/null +++ b/application/admin/controller/shopro/goods/StockLog.php @@ -0,0 +1,48 @@ +model = new StockLogModel; + } + + + /** + * 库存补货列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $skuPriceTableName = (new SkuPriceModel())->getQuery()->getTable(); + $stockLogs = $this->model->sheepFilter()->alias('g')->with(['goods' => function ($query) { + $query->removeOption('soft_delete'); + }, 'oper']) + ->join($skuPriceTableName . ' sp', 'g.goods_sku_price_id = sp.id', 'left') + ->field('g.*,sp.stock as total_stock') + ->paginate($this->request->param('list_rows', 10))->toArray(); + // 解析操作人信息 + foreach ($stockLogs['data'] as &$log) { + $log['oper'] = Operator::info('admin', $log['oper'] ?? null); + } + $this->success('获取成功', null, $stockLogs); + } +} diff --git a/application/admin/controller/shopro/goods/StockWarning.php b/application/admin/controller/shopro/goods/StockWarning.php new file mode 100644 index 0000000..54d22d0 --- /dev/null +++ b/application/admin/controller/shopro/goods/StockWarning.php @@ -0,0 +1,120 @@ +model = new StockWarningModel; + } + + + /** + * 库存预警列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $skuPriceTableName = (new SkuPriceModel())->getQuery()->getTable(); + $stockWarnings = $this->model->sheepFilter()->alias('g')->with(['goods' => function ($query) { + $query->removeOption('soft_delete'); + }]) + ->join($skuPriceTableName . ' sp', 'g.goods_sku_price_id = sp.id', 'left') + ->field('g.*,sp.stock') + ->paginate($this->request->param('list_rows', 10)); + + $warning_total = $this->model->sheepFilter(false, function ($filters) { + $filters['stock_type'] = 'no_enough'; + return $filters; + })->alias('g') + ->join($skuPriceTableName . ' sp', 'g.goods_sku_price_id = sp.id', 'left') + ->field('g.*,sp.stock') + ->count(); + $over_total = $this->model->sheepFilter(false, function ($filters) { + $filters['stock_type'] = 'over'; + return $filters; + })->alias('g') + ->join($skuPriceTableName . ' sp', 'g.goods_sku_price_id = sp.id', 'left') + ->field('g.*,sp.stock') + ->count(); + + $result = [ + 'rows' => $stockWarnings, + 'warning_total' => $warning_total, + 'over_total' => $over_total, + ]; + + $this->success('获取成功', null, $result); + } + + + + + /** + * 补货 + * + * @param [type] $ids + * @param [type] $stock + * @return void + */ + public function addStock ($id) { + if ($this->request->isAjax()) { + $params = $this->request->only(['stock']); + $this->svalidate($params, ".add"); + + $stockWarning = $this->model->with(['sku_price'])->where('id', $id)->find(); + if (!$stockWarning) { + $this->error(__('No Results were found')); + } + if (!$stockWarning->sku_price) { + $this->error('库存规格不存在'); + } + + Db::transaction(function () use ($stockWarning, $params) { + // 补货 + $this->addStockToSkuPrice($stockWarning->sku_price, $params['stock'], 'stock_warning'); + }); + + $this->success('补货成功'); + } + + return $this->view->fetch(); + + } + + + public function recyclebin() + { + if ($this->request->isAjax()) { + $skuPriceTableName = (new SkuPriceModel())->getQuery()->getTable(); + $stockWarnings = $this->model->onlyTrashed()->sheepFilter()->alias('g')->with(['goods' => function ($query) { + $query->removeOption('soft_delete'); + }]) + ->join($skuPriceTableName . ' sp', 'g.goods_sku_price_id = sp.id', 'left') + ->field('g.*,sp.stock') + ->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $stockWarnings); + } + + return $this->view->fetch(); + } +} diff --git a/application/admin/controller/shopro/notification/Config.php b/application/admin/controller/shopro/notification/Config.php new file mode 100644 index 0000000..569a0f1 --- /dev/null +++ b/application/admin/controller/shopro/notification/Config.php @@ -0,0 +1,227 @@ +model = new ConfigModel; + } + + + + /** + * 消息通知配置 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $receiver_type = $this->request->param('receiver_type'); + + $notifications = $this->getNotificationsByReceiverType($receiver_type); + + $groupConfigs = $this->getGroupConfigs(); + foreach ($notifications as $key => &$notification) { + $currentConfigs = $groupConfigs[$notification['event']] ?? []; + foreach ($notification['channels'] as $channel) { + $notification['configs'][$channel] = [ + 'status' => isset($currentConfigs[$channel]) ? $currentConfigs[$channel]['status'] : 'disabled', + 'send_num' => isset($currentConfigs[$channel]) ? $currentConfigs[$channel]['send_num'] : 0, + ]; + } + } + + $this->success('获取成功', null, $notifications); + } + + + + public function detail() + { + $event = $this->request->param('event'); + $channel = $this->request->param('channel'); + if (!$event || !$channel) { + error_stop('参数错误'); + } + + $notification = $this->getNotificationByEvent($event); + + $notification = $this->formatNotification($notification, $event, $channel); + + $this->success('获取成功', null, $notification); + } + + + // 编辑配置 + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $event = $this->request->param('event'); + $channel = $this->request->param('channel'); + if ($channel == 'Email') { + $content = $this->request->param('content', ''); + } else { + $content = $this->request->param('content/a', []); + } + $type = $this->request->param('type', 'default'); + if (!$event || !$channel) { + error_stop('参数错误'); + } + + $config = $this->model->where('event', $event)->where('channel', $channel)->find(); + + if (!$config) { + $config = $this->model; + $config->event = $event; + $config->channel = $channel; + } + + if (in_array($channel, ['WechatOfficialAccount', 'WechatMiniProgram']) && $type == 'default') { + // 自动组装微信默认模板 + $content['fields'] = $this->formatWechatTemplateFields($event, $channel, $content['fields']); + } + + $config->type = $type; + $config->content = $content; + $config->save(); + + $this->success('设置成功'); + } + + + + // 配置状态 + public function setStatus($event, $channel) + { + $event = $this->request->param('event'); + $channel = $this->request->param('channel'); + $status = $this->request->param('status', 'disabled'); + + if (!$event || !$channel) { + $this->error('参数错误'); + } + + $config = $this->model->where('event', $event)->where('channel', $channel)->find(); + if (!$config) { + $config = $this->model; + $config->event = $event; + $config->channel = $channel; + $config->type = 'default'; + } + $config->status = $status; + $config->save(); + + $this->success('设置成功'); + } + + + /** + * 自动获取微信模板 id + */ + public function getTemplateId() + { + $event = $this->request->param('event'); + $channel = $this->request->param('channel'); + $is_delete = $this->request->param('is_delete', 0); + $template_id = $this->request->param('template_id', ''); + if (!$event || !$channel) { + error_stop('参数错误'); + } + + $notification = $this->getNotificationByEvent($event); + + $template = $notification['template'][$channel] ?? null; + if (!$template) { + $this->error('模板不存在'); + } + + // 请求微信接口 + switch ($channel) { + case 'WechatMiniProgram': // 小程序订阅消息 + $requestParams['tid'] = $template['tid']; + $requestParams['kid'] = $template['kid']; + $requestParams['sceneDesc'] = $template['scene_desc']; + if (!$requestParams['tid'] || !$requestParams['kid']) { + $this->error('缺少模板参数'); + } + $wechat = Wechat::miniProgram()->subscribe_message; + $delete_method = 'deleteTemplate'; + $result_key = 'priTmplId'; + break; + // case 'WechatOfficialAccount': // 公众号模板消息 + // $requestParams['template_id'] = $template['temp_no']; + // if (!$requestParams['template_id']) { + // $this->error('缺少模板参数,获取失败'); + // } + // $wechat = Wechat::officialAccount()->template_message; // 微信管理 + // $result_key = 'template_id'; + // $delete_method = 'deletePrivateTemplate'; + // break; + case 'WechatOfficialAccount': // 新版公众号模板消息 + $requestParams['template_id'] = $template['temp_no']; + $requestParams['keywords'] = $template['keywords']; + + if (!$requestParams['template_id']) { + $this->error('公众号类目模板库目前不完善,请自行在公众号后台->模板消息->选择模板配置'); + } + if (!$requestParams['keywords']) { + $this->error('缺少模板关键字,获取失败'); + } + + $wechat = new \addons\shopro\library\easywechatPlus\WechatOfficialTemplate(Wechat::officialAccount()); + + $result_key = 'template_id'; + $delete_method = 'deletePrivateTemplate'; + break; + case 'WechatOfficialAccountBizsend': // 公众号订阅消息(待补充) + $requestParams['tid'] = $template['tid']; + $requestParams['kid'] = $template['kid']; + if (!$requestParams['tid'] || !$requestParams['kid']) { + $this->error('缺少模板参数,获取失败'); + } + $wechat = Wechat::officialAccount()->subscribe_message; // 微信管理 + $result_key = 'priTmplId'; + $delete_method = 'deleteTemplate'; + break; + default: + $this->error('当前发送渠道不能获取模板'); + break; + } + + $result = $wechat->addTemplate(...array_values($requestParams)); + + if ($result['errcode'] != 0) { + $this->error('获取失败: errcode:' . $result['errcode'] . '; errmsg:' . $result['errmsg']); + } else { + if ($is_delete) { + // 删除传入的老模板 + if ($template_id) { + $deleteResult = $wechat->{$delete_method}($template_id); + } + // 删除数据库的老模板 + $config = $this->model->where('event', $event)->where('channel', $channel)->find(); + $template_id = $config ? ($config->content['template_id'] ?? null) : null; + if ($template_id) { + $deleteResult = $wechat->{$delete_method}($template_id); + } + } + } + + $this->success('获取成功', null, ($result[$result_key] ?? null)); + } +} \ No newline at end of file diff --git a/application/admin/controller/shopro/notification/Notification.php b/application/admin/controller/shopro/notification/Notification.php new file mode 100644 index 0000000..c4b6774 --- /dev/null +++ b/application/admin/controller/shopro/notification/Notification.php @@ -0,0 +1,128 @@ +model = new NotificationModel; + } + + + /** + * 获取管理员的消息列表 + * + * @return \think\Response + */ + public function index() + { + $admin = auth_admin(); + $admin = Admin::where('id', $admin['id'])->find(); + + $notifiable_type = $admin->getNotifiableType(); + $notifications = NotificationModel::sheepFilter(false) + ->where('notifiable_type', $notifiable_type) + ->where('notifiable_id', $admin['id']) + ->order('createtime', 'desc') + ->paginate($this->request->param('list_rows', 10)); + + $this->success('消息列表', null, $notifications); + } + + + /** + * 指定消息标记已读 + * + * @param string $id + * @return void + */ + public function read($id) + { + $admin = auth_admin(); + $admin = Admin::where('id', $admin['id'])->find(); + + $notifiable_type = $admin->getNotifiableType(); + $notification = NotificationModel::sheepFilter() + ->where('notifiable_type', $notifiable_type) + ->where('notifiable_id', $admin['id']) + ->where('id', $id) + ->find(); + + if (!$notification) { + $this->error(__('No Results were found')); + } + + $notification->read_time = time(); + $notification->save(); + + $this->success('已读成功', null, $notification); + } + + + /** + * 删除已读消息 + * + * @return void + */ + public function delete() + { + $admin = auth_admin(); + $admin = Admin::where('id', $admin['id'])->find(); + + // 将已读的消息全部删除 + $notifiable_type = $admin->getNotifiableType(); + NotificationModel::sheepFilter() + ->where('notifiable_type', $notifiable_type) + ->where('notifiable_id', $admin['id']) + ->whereNotNull('read_time') + ->delete(); + + $this->success('删除成功'); + } + + + + + /** + * 消息类别,以及未读消息数 + * + * @return void + */ + public function notificationType() + { + $admin = auth_admin(); + + $notificationType = NotificationModel::$notificationType; + + $newType = []; + foreach ($notificationType as $type => $name) { + // 未读消息数 + $unread_num = NotificationModel::where('notifiable_type', 'admin')->where('notifiable_id', $admin['id']) + ->notificationType($type) + ->where('read_time', null) + ->order('createtime', 'desc')->count(); + + $newType[] = [ + 'label' => $name, + 'value' => $type, + 'unread_num' => $unread_num + ]; + } + + $result = [ + 'unread_num' => array_sum(array_column($newType, 'unread_num')), + 'notification_type' => $newType + ]; + + $this->success('消息类型', null, $result); + } +} \ No newline at end of file diff --git a/application/admin/controller/shopro/notification/traits/Notification.php b/application/admin/controller/shopro/notification/traits/Notification.php new file mode 100644 index 0000000..7ab3f1c --- /dev/null +++ b/application/admin/controller/shopro/notification/traits/Notification.php @@ -0,0 +1,183 @@ +getNotifications(); + + $receiverNotifications = []; + foreach ($notifications as $notification) { + if (in_array($notification['receiver_type'], $receiverType)) { + $receiverNotifications[] = $notification; + } + } + + return $receiverNotifications; + } + + + + /** + * 根据事件类型获取消息 + * + * @param string $event + * @return void + */ + protected function getNotificationByEvent($event) + { + $notifications = $this->getNotifications(); + + $notifications = array_column($notifications, null, 'event'); + return $notifications[$event] ?? null; + } + + + + /** + * 按照事件类型获取配置分组 + * + * @param string $event + * @return array + */ + protected function getGroupConfigs($event = null) + { + // 获取所有配置 + $configs = $this->model->select(); + $newConfigs = []; + foreach ($configs as $config) { + $newConfigs[$config['event']][$config['channel']] = $config; + } + + return $event ? ($newConfigs[$event] ?? []) : $newConfigs; + } + + + + /** + * 获取所有消息类型 + * + * @return array + */ + protected function getNotifications() + { + $types = []; + foreach ($this->notificationTypes as $key => $class_name) { + $class = new $class_name(); + $currentFields = $class->returnField; + $currentFields['event'] = $class->event; + $currentFields['receiver_type'] = $class->receiver_type; + $currentFields['template'] = $class->template; + + $types[] = $currentFields; + } + + return $types; + } + + + + /** + * 格式化详情返回结果 + * + * @param array $notification + * @param string $event + * @param string $channel + * @return array + */ + protected function formatNotification($notification, $event, $channel) + { + $currentConfigs = $this->getGroupConfigs($event); + $currentConfig = $currentConfigs[$channel] ?? null; + + if (in_array($channel, ['WechatOfficialAccount', 'WechatMiniProgram', 'WechatOfficialAccountBizsend'])) { + $currentTemplate = $notification['template'][$channel] ?? []; + unset($notification['template']); + $notification['wechat'] = $currentTemplate; + } + + $notification['type'] = $currentConfig['type'] ?? 'default'; + $content = $currentConfig['content'] ?? null; + if (!is_array($content)) { + $notification['content_text'] = $content; + } + if ($content && is_array($content)) { + $contentFields = []; + if (isset($content['fields']) && $content['fields']) { // 判断数组是否存在 fields 设置 + $contentFields = array_column($content['fields'], null, 'field'); + } + + $tempFields = array_column($notification['fields'], null, 'field'); + $configField = array_merge($tempFields, $contentFields); + + $content['fields'] = array_values($configField); + $notification['content'] = $content; + } else { + $notification['content'] = [ + 'template_id' => '', + 'fields' => $notification['fields'] + ]; + } + + unset($notification['fields']); + + return $notification; + } + + + + /** + * 格式化微信公众号,小程序默认模板时 自动配置 模板字段 + * + * @return void + */ + protected function formatWechatTemplateFields($event, $channel, $fields) + { + $notification = $this->getNotificationByEvent($event); + + $channelFields = $notification['template'][$channel]['fields'] ?? []; + $channelFields = array_column($channelFields, null, 'field'); + + foreach ($fields as $key => &$field) { + $field_name = $field['field'] ?? ''; + if ($field_name && isset($channelFields[$field_name])) { + $field['template_field'] = $channelFields[$field_name]['template_field'] ?? ''; + } + } + + return $fields; + } +} diff --git a/application/admin/controller/shopro/order/Aftersale.php b/application/admin/controller/shopro/order/Aftersale.php new file mode 100644 index 0000000..6f3bc1f --- /dev/null +++ b/application/admin/controller/shopro/order/Aftersale.php @@ -0,0 +1,338 @@ +model = new OrderAftersaleModel; + $this->orderModel = new OrderModel; + } + + /** + * 售后单列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + // 查询主表是订单表 + $orders = $this->orderModel->withTrashed()->sheepFilter()->with(['user', 'aftersales' => function ($query) { + $query->removeOption('soft_delete'); + }])->paginate(request()->param('list_rows', 10)); + + $this->success('获取成功', null, $orders); + } + + + // 获取数据类型 + public function getType() + { + $type = $this->model->typeList(); + $dispatchStatus = $this->model->dispatchStatusList(); + $aftersaleStatus = $this->model->aftersaleStatusList(); + $refundStatus = $this->model->refundStatusList(); + + $result = [ + 'type' => $type, + 'dispatch_status' => $dispatchStatus, + 'aftersale_status' => $aftersaleStatus, + 'refund_status' => $refundStatus, + ]; + + $data = []; + foreach ($result as $key => $list) { + $data[$key][] = ['name' => '全部', 'type' => 'all']; + + foreach ($list as $k => $v) { + $data[$key][] = [ + 'name' => $v, + 'type' => $k + ]; + } + } + + $this->success('获取成功', null, $data); + } + + + + /** + * 售后单详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $aftersale = $this->model->withTrashed()->with(['user', 'order' => function ($query) { + $query->removeOption('soft_delete'); + }, 'logs'])->where('id', $id)->find(); + if (!$aftersale) { + $this->error(__('No Results were found')); + } + + // 建议退款金额 + $aftersale->suggest_refund_fee = $aftersale->suggest_refund_fee; + + // 多态关联 oper + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'admin' => \app\admin\model\Admin::class, + 'system' => \app\admin\model\Admin::class, + ]; + $aftersale['logs'] = morph_to($aftersale['logs'], $morphs, ['oper_type', 'oper_id']); + + $aftersale = $aftersale->toArray(); + foreach ($aftersale['logs'] as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + $this->success('获取成功', null, $aftersale); + } + + + + /** + * 完成售后 + */ + public function completed($id) + { + $admin = $this->auth->getUserInfo(); + + $aftersale = $this->model->withTrashed()->canOper()->where('id', $id)->find(); + if (!$aftersale) { + $this->error('售后单不存在或不可完成'); + } + + $order = $this->orderModel->withTrashed()->find($aftersale->order_id); + $orderItem = OrderItemModel::find($aftersale->order_item_id); + if (!$order || !$orderItem) { + $this->error('订单或订单商品不存在'); + } + + $aftersale = Db::transaction(function () use ($aftersale, $order, $orderItem, $admin) { + $aftersale->aftersale_status = OrderAftersaleModel::AFTERSALE_STATUS_COMPLETED; // 售后完成 + $aftersale->save(); + // 增加售后单变动记录、 + OrderAftersaleLogModel::add($order, $aftersale, $admin, 'admin', [ + 'log_type' => 'completed', + 'content' => '售后订单已完成', + 'images' => [] + ]); + + $orderItem->aftersale_status = OrderItemModel::AFTERSALE_STATUS_COMPLETED; + $orderItem->save(); + OrderActionModel::add($order, $orderItem, $admin, 'admin', '管理员完成售后'); + + // 售后单完成之后 + $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; + \think\Hook::listen('order_aftersale_completed', $data); + + return $aftersale; + }); + + $this->success('操作成功', null, $aftersale); + } + + + /** + * 拒绝售后 + */ + public function refuse($id = 0) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.refuse'); + + $aftersale = $this->model->withTrashed()->canOper()->where('id', $id)->find(); + if (!$aftersale) { + $this->error('售后单不存在或不可拒绝'); + } + + $order = $this->orderModel->withTrashed()->find($aftersale->order_id); + $orderItem = OrderItemModel::find($aftersale->order_item_id); + if (!$order || !$orderItem) { + $this->error('订单或订单商品不存在'); + } + + $aftersale = Db::transaction(function () use ($aftersale, $order, $orderItem, $params, $admin) { + $aftersale->aftersale_status = OrderAftersaleModel::AFTERSALE_STATUS_REFUSE; // 售后拒绝 + $aftersale->save(); + // 增加售后单变动记录 + OrderAftersaleLogModel::add($order, $aftersale, $admin, 'admin', [ + 'log_type' => 'refuse', + 'content' => $params['refuse_msg'], + 'images' => [] + ]); + + $orderItem->aftersale_status = OrderItemModel::AFTERSALE_STATUS_REFUSE; // 拒绝售后 + $orderItem->save(); + + OrderActionModel::add($order, $orderItem, $admin, 'admin', '管理员拒绝订单售后:' . $params['refuse_msg']); + + // 售后单拒绝后 + $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; + \think\Hook::listen('order_aftersale_refuse', $data); + + return $aftersale; + }); + + $this->success('操作成功', null, $aftersale); + } + + + /** + * 同意退款 + */ + public function refund($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.refund'); + + $refund_money = round(floatval($params['refund_money']), 2); + $refund_type = $params['refund_type'] ?? 'back'; + if ($refund_money <= 0) { + $this->error('请输入正确的退款金额'); + } + + $aftersale = $this->model->withTrashed()->canOper()->where('id', $id)->find(); + if (!$aftersale) { + $this->error('售后单不存在或不可退款'); + } + + $order = $this->orderModel->withTrashed()->with('items')->find($aftersale->order_id); + if (!$order) { + $this->error('订单不存在'); + } + $items = $order->items; + $items = array_column($items, null, 'id'); + + // 当前订单已退款总金额 + $refunded_money = array_sum(array_column($items, 'refund_fee')); + // 剩余可退款金额 + $refund_surplus_money = bcsub($order->pay_fee, (string)$refunded_money, 2); + // 如果退款金额大于订单支付总金额 + if (bccomp((string)$refund_money, $refund_surplus_money, 2) === 1) { + $this->error('退款总金额不能大于实际支付金额'); + } + + $orderItem = $items[$aftersale['order_item_id']]; + + if (!$orderItem || in_array($orderItem['refund_status'], [ + OrderItemModel::REFUND_STATUS_AGREE, + OrderItemModel::REFUND_STATUS_COMPLETED, + ])) { + $this->error('订单商品已退款,不能重复退款'); + } + // var_dump(123);exit; + $aftersale = Db::transaction(function () use ($aftersale, $order, $orderItem, $refund_money, $refund_type, $refund_surplus_money, $admin) { + $aftersale->aftersale_status = OrderAftersaleModel::AFTERSALE_STATUS_COMPLETED; // 售后同意 + $aftersale->refund_status = OrderAftersaleModel::REFUND_STATUS_AGREE; // 同意退款 + $aftersale->refund_fee = $refund_money; // 退款金额 + $aftersale->save(); + + // 增加售后单变动记录 + OrderAftersaleLogModel::add($order, $aftersale, $admin, 'admin', [ + 'log_type' => 'refund', + 'content' => '售后订单已退款', + 'images' => [] + ]); + + $orderItem->aftersale_status = OrderItemModel::AFTERSALE_STATUS_COMPLETED; + $orderItem->save(); + OrderActionModel::add($order, $orderItem, $admin, 'admin', '管理员同意售后退款'); + // var_dump(123);exit; + // 开始退款 + $orderRefund = new OrderRefund($order); + $orderRefund->refund($orderItem, $refund_money, $admin, [ + 'refund_type' => $refund_type, + 'remark' => '管理员同意售后退款' + ]); + + $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; + \think\Hook::listen('order_aftersale_completed', $data); + + return $aftersale; + }); + + $this->success('操作成功', null, $aftersale); + } + + + /** + * 留言 + */ + public function addLog($id = 0) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.add_log'); + + $aftersale = $this->model->withTrashed()->where('id', $id)->find(); + if (!$aftersale) { + $this->error('售后单不存在'); + } + + $order = $this->orderModel->withTrashed()->with('items')->find($aftersale->order_id); + if (!$order) { + $this->error('订单不存在'); + } + + $aftersale = Db::transaction(function () use ($order, $aftersale, $params, $admin) { + if ($aftersale['aftersale_status'] == 0) { + $aftersale->aftersale_status = OrderAftersaleModel::AFTERSALE_STATUS_ING; // 售后处理中 + $aftersale->save(); + } + + // 增加售后单变动记录 + OrderAftersaleLogModel::add($order, $aftersale, $admin, 'admin', [ + 'log_type' => 'add_log', + 'content' => $params['content'], + 'images' => $params['images'] ?? [] + ]); + + return $aftersale; + }); + + $this->success('操作成功', null, $aftersale); + } +} diff --git a/application/admin/controller/shopro/order/Invoice.php b/application/admin/controller/shopro/order/Invoice.php new file mode 100644 index 0000000..750641e --- /dev/null +++ b/application/admin/controller/shopro/order/Invoice.php @@ -0,0 +1,62 @@ +model = new OrderInvoiceModel; + } + + /** + * 发票列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $invoices = $this->model->sheepFilter()->with(['user', 'order', 'order_items']) + ->paginate(request()->param('list_rows', 10))->each(function ($invoice) { + $invoice->order_status = $invoice->order_status; + $invoice->order_status_text = $invoice->order_status_text; + $invoice->order_fee = $invoice->order_fee; + }); + + $this->success('获取成功', null, $invoices); + } + + + + public function confirm($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->param(); + $invoice = $this->model->waiting()->whereIn('id', $id)->find(); + if (!$invoice) { + $this->error(__('No Results were found')); + } + + $invoice->download_urls = $params['download_urls'] ?? null; + $invoice->invoice_amount = $params['invoice_amount']; + $invoice->status = 'finish'; + $invoice->finish_time = time(); + $invoice->save(); + + $this->success('开具成功', null, $invoice); + } +} diff --git a/application/admin/controller/shopro/order/Order.php b/application/admin/controller/shopro/order/Order.php new file mode 100644 index 0000000..55e488a --- /dev/null +++ b/application/admin/controller/shopro/order/Order.php @@ -0,0 +1,1225 @@ +model = new OrderModel; + } + + /** + * 订单列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + if (sheep_config('shop.platform.WechatMiniProgram.status')) { // 如果开启了小程序平台 + // 设置小程序发货信息管理,消息通知跳转地址,有缓存, 不会每次都设置 + $uploadshoppingInfo = new \addons\shopro\library\easywechatPlus\WechatMiniProgramShop(Wechat::miniProgram()); + $uploadshoppingInfo->checkAndSetMessageJumpPath(); + } + + $exportConfig = (new \addons\shopro\library\Export())->getConfig(); + $this->assignconfig("save_type", $exportConfig['save_type'] ?? 'download'); + return $this->view->fetch(); + } + $sort_user_id = $this->request->param('sort_user_id'); + // var_dump($sort_user_id);exit; + $orders = $this->model->withTrashed()->sheepFilter(true,null,$sort_user_id)->with(['user', 'items', 'address', 'activity_orders']) + ->paginate(request()->param('list_rows', 10))->each(function ($order) { + // var_dump($order);exit; + $order->pay_types = $order->pay_types; + $order->pay_types_text = $order->pay_types_text; + $order->items = collection($order->items); + // var_dump($order->items);exit; + $order->items->each(function ($item) use ($order) { + // 处理每个商品的 activity_order + $item->activity_orders = new \think\Collection; + foreach ($order->activity_orders as $activityOrder) { + if ($activityOrder->goods_ids && in_array($item->goods_id, $activityOrder->goods_ids)) { + $item->activity_orders->push($activityOrder); + } + } + + return $item; + }); + })->toArray(); + // var_dump($orders);exit; + foreach ($orders['data'] as &$order) { + $order = $this->model->setOrderItemStatusByOrder($order); + } + + $result = [ + 'orders' => $orders, + ]; + + // 查询各个状态下的订单数量 + $searchStatus = $this->model->searchStatusList(); + // 所有的数量 + $result['all'] = $this->model->withTrashed()->sheepFilter(true, function ($filters) { + unset($filters['status']); + return $filters; + })->count(); + foreach ($searchStatus as $status => $text) { + $result[$status] = $this->model->withTrashed()->sheepFilter(true, function ($filters) use ($status) { + $filters['status'] = $status; + return $filters; + })->count(); + } + + $this->success('获取成功', null, $result); + } + + // jason后台取消订单 + public function cancelOrder() + { + $user = auth_user(); + // var_dump($user);exit; + $id = $this->request->param('id'); + // var_dump($id);exit; + $order = Db::transaction(function () use ($id, $user) { + $order = OrderModel::canCancel()->with(['items', 'invoice'])->lock(true)->where('id', $id)->find(); + // var_dump($order);exit; + if (!$order) { + $this->error(__('No Results were found')); + } + + $orderOper = new OrderOper(); + $order = $orderOper->cancel($order, $user, 'admin'); + + return $order; + }); + // 订单未支付,处理 item 状态 + $order = $order->setOrderItemStatusByOrder($order); // 这里订单转 数组了 + // var_dump($order);exit; + $this->success('取消成功', '',$order); + } + + + // 获取数据类型 + public function getType() + { + $type = $this->model->typeList(); + $payType = (new PayModel)->payTypeList(); + $platform = $this->model->platformList(); + $classify = (new \app\admin\model\shopro\activity\Activity)->classifies(); + $activityType = $classify['activity']; + $promoType = $classify['promo']; + $applyRefundStatus = $this->model->applyRefundStatusList(); + $status = $this->model->searchStatusList(); + + $result = [ + 'type' => $type, + 'pay_type' => $payType, + 'platform' => $platform, + 'activity_type' => $activityType, + 'promo_types' => $promoType, + 'apply_refund_status' => $applyRefundStatus, + 'status' => $status + ]; + + $data = []; + foreach ($result as $key => $list) { + $data[$key][] = ['name' => '全部', 'type' => 'all']; + + foreach ($list as $k => $v) { + $data[$key][] = [ + 'name' => $v, + 'type' => $k + ]; + } + } + + $this->success('获取成功', null, $data); + } + + + + + + /** + * 订单详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + // 更新包裹信息(5分钟缓存) + (new ExpressLib)->updateOrderExpress($id); + + $order = $this->model->withTrashed()->with(['user', 'items', 'address', 'activity_orders', 'pays', 'invoice'])->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + $order->express = $order->express; + if ($order->invoice) { + $order->invoice->order_status = $order->invoice->order_status; + $order->invoice->order_status_text = $order->invoice->order_status_text; + $order->invoice->order_fee = $order->invoice->order_fee; + } + + foreach ($order->activity_orders as $activityOrder) { + // 处理每个活动中参与的商品 + $activityOrder->items = new \think\Collection(); + foreach ($order->items as $item) { + if ($activityOrder->goods_ids && in_array($item->goods_id, $activityOrder->goods_ids)) { + $activityOrder->items->push($item); + } + } + } + + foreach ($order->items as $item) { + // 处理 order_item 建议退款金额 + $item->suggest_refund_fee = $item->suggest_refund_fee; + } + + // 处理未支付订单 item status_code + $order = $order->setOrderItemStatusByOrder($order); + + $this->success('获取成功', null, $order); + } + + + /** + * 批量发货渲染模板 + * + * @return void + */ + public function batchDispatch() + { + return $this->view->fetch(); + } + + + /** + * 批量发货渲染模板 + * + * @return void + */ + public function dispatchList() + { + return $this->view->fetch(); + } + + + + /** + * 手动发货 + * + * @return void + */ + public function customDispatch() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['order_id', 'order_item_ids', 'custom_type', 'custom_content']); + $this->svalidate($params, '.custom_dispatch'); + + Db::transaction(function () use ($params) { + $service = new OrderDispatchService($params); + $service->customDispatch($params); + }); + + $this->success('发货成功'); + } + + + /** + * 发货 + * + * @description + * 支持分包裹发货 + * 支持手动发货 + * 支持上传发货单发货 + * 支持推送api运单发货 默认使用配置项 + * 支持修改发货信息 + * 支持取消发货 + * + * @remark 此处接口设计如此复杂是因为考虑到权限的问题,订单发货权限可以完成所有发货行为 + * + * @param array $action 发货行为(默认:confirm=确认发货, cancel=取消发货, change=修改运单, multiple=解析批量发货单) + * @param int $order_id 订单id + * @param array $order_item_ids 订单商品id + * @param string $method 发货方式(input=手动发货, api=推送运单, upload=上传发货单) + * @param array $sender 发货人信息 + * @param array $express 物流信息 + * + * @return \think\Response + */ + public function dispatch() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['action', 'order_id', 'order_ids', 'order_item_ids', 'method', 'sender', 'express', 'order_express_id']); + + $action = $params['action'] ?? 'confirm'; + if (!in_array($action, ['confirm', 'cancel', 'change', 'multiple'])) { + $this->error('发货参数错误'); + } + + $service = new OrderDispatchService($params); + switch ($action) { + case 'confirm': + $express = $service->confirm($params); + $this->success('发货成功', null, $express); + break; + case 'cancel': + $result = $service->cancel($params); + if ($result) { + $this->success('取消发货成功'); + } + break; + case 'change': + $express = $service->change($params); + $this->success('修改成功', null, $express); + break; + case 'multiple': + $params['file'] = $this->request->file('file'); + $result = $service->multiple($params); + $this->success('待发货列表', null, $result); + break; + } + + $this->error('操作失败'); + } + + + /** + * 获取物流快递信息 + */ + public function updateExpress($order_express_id = 0) + { + $type = $this->request->param('type'); + + // 获取包裹 + $orderExpress = OrderExpressModel::where('id', $order_express_id)->find(); + if (!$orderExpress) { + $this->error('包裹不存在'); + } + + $expressLib = new ExpressLib(); + + try { + if ($type == 'subscribe') { + // 重新订阅 + $expressLib->subscribe([ + 'express_code' => $orderExpress['express_code'], + 'express_no' => $orderExpress['express_no'] + ]); + } else { + // 手动查询 + $result = $expressLib->search([ + 'order_id' => $orderExpress['order_id'], + 'express_code' => $orderExpress['express_code'], + 'express_no' => $orderExpress['express_no'] + ], $orderExpress); + } + } catch (HttpResponseException $e) { + $data = $e->getResponse()->getData(); + $message = $data ? ($data['msg'] ?? '') : $e->getMessage(); + $this->error($message); + } catch (\Exception $e) { + $this->error(($type == 'subscribe' ? '订阅失败:' : '刷新失败:') . $e->getMessage()); + } + + $this->success(($type == 'subscribe' ? '订阅成功' : '刷新成功')); + } + + + /** + * 线下付款,确认收货 + */ + public function offlineConfirm($id) + { + $admin = $this->auth->getUserInfo(); + + $order = OrderModel::offline()->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + + $order = Db::transaction(function () use ($order, $admin) { + // 加锁读订单 + $order = $order->lock(true)->find($order->id); + $user = User::get($order->user_id); + + $payOper = new PayOper($user); + $order = $payOper->offline($order, $order->remain_pay_fee, 'order'); // 订单会变为已支付 paid + + // 触发订单支付完成事件 + $data = ['order' => $order, 'user' => $user]; + \think\Hook::listen('order_offline_paid_after', $data); + + $orderOper = new OrderOper(); + // 确认收货 + $order = $orderOper->confirm($order, [], $admin, 'admin'); + + return $order; + }); + + $this->success('收货成功', $order); + } + + + + /** + * 线下付款,拒收 + */ + public function offlineRefuse($id) + { + $admin = $this->auth->getUserInfo(); + + $order = OrderModel::offline()->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + + $order = Db::transaction(function () use ($order, $admin) { + // 加锁读订单 + $order = $order->lock(true)->find($order->id); + + // 拒收 + $orderOper = new OrderOper(); + $order = $orderOper->refuse($order, $admin, 'admin'); + + // 交易关闭 + $order = $orderOper->close($order, $admin, 'admin', '用户拒绝收货,管理员关闭订单', ['closed_type' => 'refuse']); + return $order; + }); + + $this->success('收货成功', $order); + } + + + /** + * 订单改价,当剩余应支付金额为 0 时,订单将自动支付 + * + * @param int $id + * @return void + */ + public function changeFee($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.change_fee'); + + $order = $this->model->unpaid()->where('id', $id)->find(); + if (!$order) { + $this->error('订单不可改价'); + } + + Db::transaction(function () use ($order, $admin, $params) { + $pay_fee = $params['pay_fee']; + $change_msg = $params['change_msg']; + + $payOper = new PayOper($order->user_id); + $payed_fee = $payOper->getPayedFee($order, 'order'); + + if ($pay_fee < $payed_fee) { + $this->error('改价金额不能低于订单已支付金额'); + } + + // 记录原始值 + $last_pay_fee = $order->pay_fee; // 上次pay_fee,非原始 pay_fee + $order->pay_fee = $pay_fee; + $order->save(); + + OrderActionModel::add($order, null, $admin, 'admin', "应支付金额由 ¥" . $last_pay_fee . " 改为 ¥" . $pay_fee . ",改价原因:" . $change_msg); + + // 检查订单支付状态, 改价可以直接将订单变为已支付 + $order = $payOper->checkAndPaid($order, 'order'); + }); + + $this->success('改价成功'); + } + + + + /** + * 修改收货人信息 + * + * @param Request $request + * @param integer $id + * @return void + */ + public function editConsignee($id) + { + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.edit_consignee'); + + $order = $this->model->withTrashed()->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + + Db::transaction(function () use ($admin, $order, $params) { + $orderAddress = OrderAddressModel::where('order_id', $order->id)->find(); + if (!$orderAddress) { + $this->error(__('No Results were found')); + } + $orderAddress->consignee = $params['consignee']; + $orderAddress->mobile = $params['mobile']; + $orderAddress->province_name = $params['province_name']; + $orderAddress->city_name = $params['city_name']; + $orderAddress->district_name = $params['district_name']; + $orderAddress->address = $params['address']; + $orderAddress->province_id = $params['province_id']; + $orderAddress->city_id = $params['city_id']; + $orderAddress->district_id = $params['district_id']; + $orderAddress->save(); + + OrderActionModel::add($order, null, $admin, 'admin', "修改订单收货人信息"); + }); + + $this->success('收货人信息修改成功'); + } + + + + /** + * 编辑商家备注 + * + * @param Request $request + * @param integer $id + * @return void + */ + public function editMemo($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.edit_memo'); + + $order = $this->model->withTrashed()->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + Db::transaction(function () use ($admin, $order, $params) { + $order->memo = $params['memo']; + $order->save(); + + OrderActionModel::add($order, null, $admin, 'admin', "修改卖家备注:" . $params['memo']); + }); + + $this->success('卖家备注修改成功'); + } + + + /** + * 拒绝用户全额退款申请 + * + * @param Request $request + * @param integer $id + * @return void + */ + public function applyRefundRefuse($id) + { + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + // $this->svalidate($params, '.apply_refund_refuse'); + + $order = $this->model->withTrashed()->paid()->applyRefundIng()->where('id', $id)->find(); + if (!$order) { + $this->error('订单未找到或不可拒绝申请'); + } + Db::transaction(function () use ($admin, $order, $params) { + $order->apply_refund_status = OrderModel::APPLY_REFUND_STATUS_REFUSE; + $order->save(); + + OrderActionModel::add($order, null, $admin, 'admin', "拒绝用户申请全额退款"); + }); + + $this->success('拒绝申请成功'); + } + + + /** + * 全额退款 (必须没有进行过任何退款才能使用) + * + * @param Request $request + * @param integer $id + * @return void + */ + public function fullRefund($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + $admin = Admin::where('id', $admin['id'])->find(); + + $params = $this->request->param(); + $refund_type = $params['refund_type'] ?? 'back'; + // var_dump($refund_type);exit; + Db::transaction(function () use ($admin, $id, $refund_type) { + $order = $this->model->paid()->where('id', $id)->lock(true)->find(); + // var_dump($order);exit; + if (!$order) { + $this->error('订单不存在或不可退款'); + } + // var_dump(123);exit; + $orderRefund = new OrderRefund($order); + $orderRefund->fullRefund($admin, [ + 'refund_type' => $refund_type, + 'remark' => '平台主动全额退款' + ]); + }); + + $this->success('全额退款成功'); + } + + + + /** + * 订单单商品退款 + * + * @param Request $request + * @param integer $id + * @param integer $item_id + * @return void + */ + public function refund($id, $item_id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.refund'); + + $refund_money = round(floatval($params['refund_money']), 2); + $refund_type = $params['refund_type'] ?? 'back'; + if ($refund_money <= 0) { + $this->error('请输入正确的退款金额'); + } + + $order = $this->model->paid()->where('id', $id)->find(); + if (!$order) { + $this->error('订单不存在或不可退款'); + } + + $item = OrderItem::where('order_id', $order->id)->where('id', $item_id)->find(); + if (!$item) { + $this->error(__('No Results were found')); + } + + if (in_array($item['refund_status'], [ + OrderItem::REFUND_STATUS_AGREE, + OrderItem::REFUND_STATUS_COMPLETED, + ])) { + $this->error('订单商品已退款,不能重复退款'); + } + + $payOper = new PayOper($order->user_id); + // 获取订单最大可退款金额(不含积分抵扣金额) + $remain_max_refund_money = $payOper->getRemainRefundMoney($order->id); + + // 如果退款金额大于订单支付总金额 + if (bccomp((string)$refund_money, $remain_max_refund_money, 2) === 1) { + $this->error('退款总金额不能大于实际支付金额'); + } + + Db::transaction(function () use ($admin, $order, $item, $refund_money, $refund_type) { + // 重新锁定读查询 orderItem + $item = OrderItem::where('order_id', $order->id)->lock(true)->where('id', $item->id)->find(); + if (!$item) { + $this->error('订单不存在或不可退款'); + } + + $orderRefund = new OrderRefund($order); + $orderRefund->refund($item, $refund_money, $admin, [ + 'refund_type' => $refund_type, + 'remark' => '平台主动退款' + ]); + }); + + $this->success('退款成功'); + } + + + /** + * 获取订单操作记录 + * + * @param integer $id + * @return void + */ + public function action($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $actions = OrderActionModel::where('order_id', $id)->order('id', 'desc')->select(); + + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'admin' => \app\admin\model\Admin::class, + 'system' => \app\admin\model\Admin::class, + ]; + $actions = morph_to($actions, $morphs, ['oper_type', 'oper_id']); + + foreach ($actions as &$action) { + $action['oper'] = Operator::info($action['oper_type'], $action['oper'] ?? null); + } + + $this->success('获取成功', null, $actions); + } + + + + + public function export() + { + $cellTitles = [ + // 订单表字段 + 'order_id' => 'Id', + 'order_sn' => '订单号', + 'type_text' => '订单类型', + 'user_nickname' => '下单用户', + 'user_mobile' => '手机号', + 'status_text' => '订单状态', + 'pay_text' => '支付状态', + 'pay_types_text' => '支付类型', + 'remark' => '用户备注', + 'memo' => '卖家备注', + 'order_amount' => '订单总金额', + 'score_amount' => '积分支付数量', + 'dispatch_amount' => '运费', + 'pay_fee' => '应付总金额', + 'real_pay_fee' => '实付总金额', + 'remain_pay_fee' => '剩余支付金额', + 'total_discount_fee' => '总优惠金额', + 'coupon_discount_fee' => '优惠券金额', + 'promo_discount_fee' => '营销优惠金额', + 'paid_time' => '支付完成时间', + 'platform_text' => '交易平台', + 'consignee_info' => '收货信息', + 'createtime' => '下单时间', + + // 订单商品表字段 + 'activity_type_text' => '活动', + 'promo_types' => '促销', + 'goods_title' => '商品名称', + 'goods_sku_text' => '商品规格', + 'goods_num' => '购买数量', + 'goods_original_price' => '商品原价', + 'goods_price' => '商品价格', + 'goods_weight' => '商品重量', + 'discount_fee' => '优惠金额', + 'goods_pay_fee' => '商品支付金额', + 'dispatch_type_text' => '发货方式', + 'dispatch_status_text' => '发货状态', + 'aftersale_refund' => '售后/退款', + 'comment_status_text' => '评价状态', + 'refund_fee' => '退款金额', + 'refund_msg' => '退款原因', + 'express_name' => '快递公司', + 'express_no' => '快递单号', + 'virtualfiled' => '虚拟商品购买信息', + ]; + + // 数据总条数 + $total = $this->model->withTrashed()->sheepFilter()->count(); + if ($total <= 0) { + $this->error('导出数据为空'); + } + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => '订单列表', + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => true, + 'sub_start_cell' => 'activity_type_text', + 'sub_field' => 'items' + ]; + + $total_order_amount = 0; + $total_pay_fee = 0; + $total_real_pay_fee = 0; + $total_discount_fee = 0; + $total_score_amount = 0; + $result = $export->export($params, function ($pages) use (&$total_order_amount, &$total_pay_fee, &$total_real_pay_fee, &$total_discount_fee, &$total_score_amount, $total) { + $datas = $this->model->withTrashed()->sheepFilter()->with(['user', 'items' => function ($query) { + $query->with(['express']); + }, 'address']) + ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows']) + ->select(); + + $datas = collection($datas); + $datas = $datas->each(function ($order) { + $order->pay_types_text = $order->pay_types_text; + })->toArray(); + + $newDatas = []; + foreach ($datas as &$order) { + $order = $this->model->setOrderItemStatusByOrder($order); + + // 收货人信息 + $consignee_info = ''; + if ($order['address']) { + $address = $order['address']; + $consignee_info = ($address['consignee'] ? ($address['consignee'] . ':' . $address['mobile'] . '-') : '') . ($address['province_name'] . '-' . $address['city_name'] . '-' . $address['district_name']) . ' ' . $address['address']; + } + + $data = [ + 'order_id' => $order['id'], + 'order_sn' => $order['order_sn'], + 'type_text' => $order['type_text'], + 'user_nickname' => $order['user'] ? $order['user']['nickname'] : '-', + 'user_mobile' => $order['user'] ? $order['user']['mobile'] . ' ' : '-', + 'status_text' => $order['status_text'], + 'pay_text' => in_array($order['status'], [OrderModel::STATUS_PAID, OrderModel::STATUS_COMPLETED]) ? '已支付' : '未支付', + 'pay_types_text' => is_array($order['pay_types_text']) ? join(',', $order['pay_types_text']) : ($order['pay_types_text'] ?: ''), + 'remark' => $order['remark'], + 'memo' => $order['memo'], + 'order_amount' => $order['order_amount'], + 'score_amount' => $order['score_amount'], + 'dispatch_amount' => $order['dispatch_amount'], + 'pay_fee' => $order['pay_fee'], + 'real_pay_fee' => bcsub($order['pay_fee'], $order['remain_pay_fee'], 2), + 'remain_pay_fee' => $order['remain_pay_fee'], + 'total_discount_fee' => $order['total_discount_fee'], + 'coupon_discount_fee' => $order['coupon_discount_fee'], + 'promo_discount_fee' => $order['promo_discount_fee'], + 'paid_time' => $order['paid_time'], + 'platform_text' => $order['platform_text'], + 'consignee_info' => $consignee_info, + 'createtime' => $order['createtime'], + ]; + + $items = []; + foreach ($order['items'] as $item) { + $str = ""; + if (!empty($item['virtualfiled'])) { + foreach (json_decode($item['virtualfiled'], true) as $val) { + $str .= implode(":", $val)."\n"; + } + } + $items[] = [ + 'activity_type_text' => $item['activity_type_text'], + 'promo_types_text' => is_array($item['promo_types_text']) ? join(',', $item['promo_types_text']) : ($item['promo_types_text'] ?: '-'), + 'goods_title' => $item['goods_title'], + 'goods_sku_text' => $item['goods_sku_text'], + 'goods_num' => $item['goods_num'], + 'goods_original_price' => $item['goods_original_price'], + 'goods_price' => $item['goods_price'], + 'goods_weight' => $item['goods_weight'], + 'discount_fee' => $item['discount_fee'], + 'goods_pay_fee' => $item['pay_fee'], + 'dispatch_type_text' => $item['dispatch_type_text'], + 'dispatch_status_text' => $item['dispatch_status_text'], + 'aftersale_refund' => $item['aftersale_status_text'] . '/' . $item['refund_status_text'], + 'comment_status_text' => $item['comment_status_text'], + 'refund_fee' => $item['refund_fee'], + 'refund_msg' => $item['refund_msg'], + 'express_name' => $item['express'] ? $item['express']['express_name'] : '-', + 'express_no' => $item['express'] ? $item['express']['express_no'] . ' ' : '-', + 'virtualfiled' => $str, + ]; + } + + $data['items'] = $items; + + $newDatas[] = $data; + } + + $total_order_amount += array_sum(array_column($newDatas, 'order_amount')); + $total_score_amount += array_sum(array_column($newDatas, 'score_amount')); + $total_pay_fee += array_sum(array_column($newDatas, 'pay_fee')); + $total_real_pay_fee += array_sum(array_column($newDatas, 'real_pay_fee')); + $total_discount_fee += array_sum(array_column($newDatas, 'discount_fee')); + + if ($pages['is_last_page']) { + $newDatas[] = ['order_id' => "订单总数:" . $total . ";订单总金额:¥" . $total_order_amount . ";优惠总金额:¥" . $total_discount_fee . ";应付总金额:¥" . $total_pay_fee . ";实付总金额:¥" . $total_real_pay_fee . ";支付总积分:" . $total_score_amount]; + } + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } + + //酒店订单导出 + public function hotelExport() { + $cellTitles = [ + // 订单表字段 + 'order_id' => '序号', + // 'order_sn' => '订单号', + // 'type_text' => '订单类型', + // 'user_nickname' => '下单用户', + // 'user_mobile' => '手机号', + ## + // 'status_text' => '订单状态', + ## + // 'pay_text' => '支付状态', + // 'pay_types_text' => '支付类型', + // 'remark' => '用户备注', + + // 'order_amount' => '订单总金额', + // 'score_amount' => '积分支付数量', + // 'dispatch_amount' => '运费', + // 'pay_fee' => '应付总金额', + // 'real_pay_fee' => '实付总金额', + // 'remain_pay_fee' => '剩余支付金额', + // 'total_discount_fee' => '总优惠金额', + // 'coupon_discount_fee' => '优惠券金额', + // 'promo_discount_fee' => '营销优惠金额', + // 'paid_time' => '支付完成时间', + // 'platform_text' => '交易平台', + // 'consignee_info' => '收货信息', + // 'createtime' => '下单时间', + + // 订单商品表字段 + 'activity_type_text' => '', + // 'promo_types' => '促销', + 'goods_title' => '商品名称', + + // 'goods_sku_text' => '商品规格', + 'goods_sku_text_fangxing' => '房型', + 'goods_sku_text_jiudianming' => '酒店名', + 'goods_num' => '购买数量', + // 'goods_original_price' => '商品原价', + // 'goods_price' => '商品价格', + // 'goods_weight' => '商品重量', + // 'discount_fee' => '优惠金额', + // 'goods_pay_fee' => '商品支付金额', + // 'dispatch_type_text' => '发货方式', + // 'dispatch_status_text' => '发货状态', + // 'aftersale_refund' => '售后/退款', + // 'comment_status_text' => '评价状态', + // 'refund_fee' => '退款金额', + // 'refund_msg' => '退款原因', + // 'express_name' => '快递公司', + // 'express_no' => '快递单号', + // 'virtualfiled' => '虚拟商品购买信息', + 'virtualfiled_name' => '姓名', + 'virtualfiled_phone' => '手机号', + 'virtualfiled_idcard' => '身份证', + 'virtualfiled_startdate'=> '入住日期', + 'virtualfiled_enddate' => '退房日期', + 'memo' => '卖家备注', + ]; + + // 数据总条数 + $total = $this->model->withTrashed()->sheepFilter()->count(); + if ($total <= 0) { + $this->error('导出数据为空'); + } + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => '订单列表', + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => true, + 'sub_start_cell' => 'activity_type_text', + 'sub_field' => 'items' + ]; + + $total_order_amount = 0; + $total_pay_fee = 0; + $total_real_pay_fee = 0; + $total_discount_fee = 0; + $total_score_amount = 0; + $result = $export->hotel_export($params, function ($pages) use (&$total_order_amount, &$total_pay_fee, &$total_real_pay_fee, &$total_discount_fee, &$total_score_amount, $total) { + $datas = $this->model->withTrashed()->sheepFilter()->with(['user', 'items' => function ($query) { + $query->with(['express']); + }, 'address']) + ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows']) + ->select(); + $isNormal = $this->request->param('isNormal'); + $search_ordertype = array('待发货','待付款','待评价','待收货','交易关闭','交易完成','退款完成','已取消',''); + if($isNormal == 1){ + $search_ordertype = array('待发货','待评价','待收货','交易完成'); + }elseif($isNormal == 2){ + $search_ordertype = array('退款完成'); + } + // var_dump($search);exit; + $datas = collection($datas); + $datas = $datas->each(function ($order) { + $order->pay_types_text = $order->pay_types_text; + })->toArray(); + // var_dump($datas);exit; + $newDatas = [];$j = 1; + foreach ($datas as &$order) { + //筛选订单状态 + if (in_array($order['status_text'], $search_ordertype)){ + $order = $this->model->setOrderItemStatusByOrder($order); + + // 收货人信息 + $consignee_info = ''; + if ($order['address']) { + $address = $order['address']; + $consignee_info = ($address['consignee'] ? ($address['consignee'] . ':' . $address['mobile'] . '-') : '') . ($address['province_name'] . '-' . $address['city_name'] . '-' . $address['district_name']) . ' ' . $address['address']; + } + + $data = [ + 'order_id' => $j, + 'order_sn' => $order['order_sn'], + 'type_text' => $order['type_text'], + 'user_nickname' => $order['user'] ? $order['user']['nickname'] : '-', + 'user_mobile' => $order['user'] ? $order['user']['mobile'] . ' ' : '-', + 'status_text' => $order['status_text'], + 'pay_text' => in_array($order['status'], [OrderModel::STATUS_PAID, OrderModel::STATUS_COMPLETED]) ? '已支付' : '未支付', + 'pay_types_text' => is_array($order['pay_types_text']) ? join(',', $order['pay_types_text']) : ($order['pay_types_text'] ?: ''), + 'remark' => $order['remark'], + 'memo' => $order['memo'], + 'order_amount' => $order['order_amount'], + 'score_amount' => $order['score_amount'], + 'dispatch_amount' => $order['dispatch_amount'], + 'pay_fee' => $order['pay_fee'], + 'real_pay_fee' => bcsub($order['pay_fee'], $order['remain_pay_fee'], 2), + 'remain_pay_fee' => $order['remain_pay_fee'], + 'total_discount_fee' => $order['total_discount_fee'], + 'coupon_discount_fee' => $order['coupon_discount_fee'], + 'promo_discount_fee' => $order['promo_discount_fee'], + 'paid_time' => $order['paid_time'], + 'platform_text' => $order['platform_text'], + 'consignee_info' => $consignee_info, + 'createtime' => $order['createtime'], + ]; + $j++; + $items = []; + foreach ($order['items'] as $item) { + $str = ""; + $virtualfiled_name = ''; + $virtualfiled_phone = ''; + $virtualfiled_idcard = ''; + $virtualfiled_startdate = ''; + $virtualfiled_enddate = ''; + // var_dump(json_decode($item['virtualfiled']));exit; + if (!empty($item['virtualfiled'])) { + foreach (json_decode($item['virtualfiled'], true) as $val) { + // var_dump($val);exit; + if(array_key_exists('name',$val)){ + $virtualfiled_name = $val['name']; + $virtualfiled_phone = $val['phone']; + if(array_key_exists('idcard',$val)){ + // var_dump($val['idcard']);exit; + $virtualfiled_idcard = "'".$val['idcard']; + } + + } + if(array_key_exists('startdate',$val)){ + $virtualfiled_startdate = $val['startdate']; + $virtualfiled_enddate = $val['enddate']; + } + } + } + + $fangxing_jiudian = explode(",", $item['goods_sku_text']); + // var_dump($fangxing_jiudian);exit; + $items[] = [ + 'activity_type_text' => $item['activity_type_text'], + // 'promo_types_text' => is_array($item['promo_types_text']) ? join(',', $item['promo_types_text']) : ($item['promo_types_text'] ?: '-'), + 'goods_title' => $item['goods_title'], + // 'goods_sku_text' => $item['goods_sku_text'], + 'goods_sku_text_fangxing' => $fangxing_jiudian[0], + 'goods_sku_text_jiudianming' => $fangxing_jiudian[1], + 'goods_num' => $item['goods_num'], + // 'goods_original_price' => $item['goods_original_price'], + // 'goods_price' => $item['goods_price'], + // 'goods_weight' => $item['goods_weight'], + // 'discount_fee' => $item['discount_fee'], + // 'goods_pay_fee' => $item['pay_fee'], + // 'dispatch_type_text' => $item['dispatch_type_text'], + // 'dispatch_status_text' => $item['dispatch_status_text'], + // 'aftersale_refund' => $item['aftersale_status_text'] . '/' . $item['refund_status_text'], + // 'comment_status_text' => $item['comment_status_text'], + 'refund_fee' => $item['refund_fee'], + // 'refund_msg' => $item['refund_msg'], + // 'express_name' => $item['express'] ? $item['express']['express_name'] : '-', + // 'express_no' => $item['express'] ? $item['express']['express_no'] . ' ' : '-', + // 'virtualfiled' => $str, + 'virtualfiled_name' => $virtualfiled_name, + 'virtualfiled_phone' => $virtualfiled_phone, + 'virtualfiled_idcard' => $virtualfiled_idcard, + 'virtualfiled_startdate'=> $virtualfiled_startdate, + 'virtualfiled_enddate' => $virtualfiled_enddate + ]; + } + + $data['items'] = $items; + + $newDatas[] = $data; + } + } + $total_order_amount += array_sum(array_column($newDatas, 'order_amount')); + $total_score_amount += array_sum(array_column($newDatas, 'score_amount')); + $total_pay_fee += array_sum(array_column($newDatas, 'pay_fee')); + $total_real_pay_fee += array_sum(array_column($newDatas, 'real_pay_fee')); + $total_discount_fee += array_sum(array_column($newDatas, 'discount_fee')); + // var_dump($newDatas);exit; + // if ($pages['is_last_page']) { + // $newDatas[] = ['order_id' => "订单总数:" . $total . ";订单总金额:¥" . $total_order_amount . ";优惠总金额:¥" . $total_discount_fee . ";应付总金额:¥" . $total_pay_fee . ";实付总金额:¥" . $total_real_pay_fee . ";支付总积分:" . $total_score_amount]; + // } + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } + + + public function exportDelivery() + { + $cellTitles = [ + // 订单表字段 + 'order_id' => 'Id', + 'order_sn' => '订单号', + 'type_text' => '订单类型', + 'consignee_info' => '收货信息', + 'remark' => '用户备注', + 'memo' => '商家备注', + 'createtime' => '下单时间', + + // 订单商品表字段 + 'order_item_id' => '子订单Id', + 'goods_title' => '商品名称', + 'goods_sku_text' => '商品规格', + 'goods_num' => '购买数量', + // 'dispatch_fee' => '发货费用', + 'dispatch_type_text' => '发货方式', + 'dispatch_status_text' => '发货状态', + 'aftersale_refund' => '售后/退款', + 'express_no' => '快递单号', + ]; + + // 数据总条数 + $total = $this->model->sheepFilter()->count(); // nosend 加了 noApplyRefund + if ($total <= 0) { + $this->error('导出数据为空'); + } + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => '订单发货单列表', + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => true, + 'sub_start_cell' => 'order_item_id', + 'sub_field' => 'items' + ]; + + $result = $export->export($params, function ($pages) use (&$total) { + // 未申请全额退款的 + $datas = $this->model->sheepFilter()->with(['user', 'items' => function ($query) { // nosend 加了 noApplyRefund + $query->with(['express']); + }, 'address']) + ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows']) + ->select(); + $datas = collection($datas)->toArray(); + + $newDatas = []; + foreach ($datas as &$order) { + $order = $this->model->setOrderItemStatusByOrder($order); + + if (in_array($order['status_code'], ['groupon_ing', 'groupon_invalid'])) { + // 拼团正在进行中,不发货 + $total--; // total 减少 1 + continue; + } + + // 收货人信息 + $consignee_info = ''; + if ($order['address']) { + $address = $order['address']; + $consignee_info = ($address['consignee'] ? ($address['consignee'] . ':' . $address['mobile'] . '-') : '') . ($address['province_name'] . '-' . $address['city_name'] . '-' . $address['district_name']) . ' ' . $address['address']; + } + + $data = [ + 'order_id' => $order['id'], + 'order_sn' => $order['order_sn'], + 'type_text' => $order['type_text'], + 'consignee_info' => $consignee_info, + 'remark' => $order['remark'], + 'memo' => $order['memo'], + 'createtime' => $order['createtime'] + ]; + + $items = []; + foreach ($order['items'] as $k => $item) { + // 未发货,并且未退款,并且未在申请售后中,并且是快递物流的 + if ( + $item['dispatch_status'] == OrderItem::DISPATCH_STATUS_NOSEND + && !in_array($item['refund_status'], [OrderItem::REFUND_STATUS_AGREE, OrderItem::REFUND_STATUS_COMPLETED]) + && $item['aftersale_status'] != OrderItem::AFTERSALE_STATUS_ING + && $item['dispatch_type'] == 'express' + ) { + $items[] = [ + 'order_item_id' => $item['id'], + 'goods_title' => strpos($item['goods_title'], '=') === 0 ? ' ' . $item['goods_title'] : $item['goods_title'], + 'goods_sku_text' => $item['goods_sku_text'], + 'goods_num' => $item['goods_num'], + // 'dispatch_fee' => $item['dispatch_fee'], + 'dispatch_type_text' => $item['dispatch_type_text'], + 'dispatch_status_text' => $item['dispatch_status_text'], + 'aftersale_refund' => $item['aftersale_status_text'] . '/' . $item['refund_status_text'], + 'express_no' => $item['express'] ? $item['express']['express_no'] . ' ' : '', + ]; + } + } + + $data['items'] = $items; + $newDatas[] = $data; + }; + + if ($pages['is_last_page']) { + $newDatas[] = ['order_id' => "订单总数(仅快递物流的待发货订单):" . $total . ";备注:订单中同一包裹请填写相同运单号"]; + } + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } +} diff --git a/application/admin/controller/shopro/trade/Order.php b/application/admin/controller/shopro/trade/Order.php new file mode 100644 index 0000000..395baca --- /dev/null +++ b/application/admin/controller/shopro/trade/Order.php @@ -0,0 +1,199 @@ +model = new TradeOrderModel; + } + + /** + * 订单列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + $exportConfig = (new \addons\shopro\library\Export())->getConfig(); + $this->assignconfig("save_type", $exportConfig['save_type'] ?? 'download'); + return $this->view->fetch(); + } + + $orders = $this->model->withTrashed()->sheepFilter()->with(['user']) + ->paginate(request()->param('list_rows', 10))->each(function ($order) { + $order->pay_type_text = $order->pay_type_text; + $order->pay_type = $order->pay_type; + })->toArray(); + + $result = [ + 'orders' => $orders, + ]; + + // 查询各个状态下的订单数量 + $searchStatus = $this->model->searchStatusList(); + // 所有的数量 + $result['all'] = $this->model->withTrashed()->sheepFilter(true, function ($filters) { + unset($filters['status']); + return $filters; + })->count(); + foreach ($searchStatus as $status => $text) { + $result[$status] = $this->model->withTrashed()->sheepFilter(true, function ($filters) use ($status) { + $filters['status'] = $status; + return $filters; + })->count(); + } + + $this->success('获取成功', null, $result); + } + + + // 获取数据类型 + public function getType() + { + $type = $this->model->typeList(); + $payType = (new PayModel)->payTypeList(); + $platform = $this->model->platformList(); + $status = $this->model->searchStatusList(); + + $result = [ + 'type' => $type, + 'pay_type' => $payType, + 'platform' => $platform, + 'status' => $status + ]; + + $data = []; + foreach ($result as $key => $list) { + $data[$key][] = ['name' => '全部', 'type' => 'all']; + + foreach ($list as $k => $v) { + $data[$key][] = [ + 'name' => $v, + 'type' => $k + ]; + } + } + + $this->success('获取成功', null, $data); + } + + + + /** + * 订单详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $order = $this->model->withTrashed()->with(['user', 'pays'])->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + + $order->pay_type = $order->pay_type; + $order->pay_type_text = $order->pay_type_text; + + $this->success('获取成功', null, $order); + } + + + + public function export() + { + $cellTitles = [ + // 订单表字段 + 'order_id' => 'Id', + 'order_sn' => '订单号', + 'type_text' => '订单类型', + 'user_nickname' => '下单用户', + 'user_mobile' => '手机号', + 'status_text' => '订单状态', + 'pay_type_text' => '支付类型', + 'remark' => '用户备注', + 'order_amount' => '订单总金额', + 'pay_fee' => '应付总金额', + 'real_pay_fee' => '实付总金额', + 'remain_pay_fee' => '剩余支付金额', + 'paid_time' => '支付完成时间', + 'platform_text' => '交易平台', + 'createtime' => '下单时间', + ]; + + // 数据总条数 + $total = $this->model->withTrashed()->sheepFilter()->count(); + if ($total <= 0) { + $this->error('导出数据为空'); + } + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => '交易订单列表', + 'cell_titles' => $cellTitles, + 'total' => $total, + ]; + + $total_order_amount = 0; + $total_pay_fee = 0; + $total_real_pay_fee = 0; + $result = $export->export($params, function ($pages) use (&$total_order_amount, &$total_pay_fee, &$total_real_pay_fee, $total) { + $datas = $this->model->withTrashed()->sheepFilter()->with(['user']) + ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows']) + ->select(); + + $datas = collection($datas); + $datas = $datas->each(function ($order) { + $order->pay_type_text = $order->pay_type_text; + })->toArray(); + + $newDatas = []; + foreach ($datas as &$order) { + $data = [ + 'order_id' => $order['id'], + 'order_sn' => $order['order_sn'], + 'type_text' => $order['type_text'], + 'user_nickname' => $order['user'] ? $order['user']['nickname'] : '-', + 'user_mobile' => $order['user'] ? $order['user']['mobile'] . ' ' : '-', + 'status_text' => $order['status_text'], + 'pay_type_text' => is_array($order['pay_type_text']) ? join(',', $order['pay_type_text']) : ($order['pay_type_text'] ?: ''), + 'remark' => $order['remark'], + 'order_amount' => $order['order_amount'], + 'pay_fee' => $order['pay_fee'], + 'real_pay_fee' => bcsub($order['pay_fee'], $order['remain_pay_fee'], 2), + 'remain_pay_fee' => $order['remain_pay_fee'], + 'paid_time' => $order['paid_time'], + 'platform_text' => $order['platform_text'], + 'createtime' => $order['createtime'], + ]; + $newDatas[] = $data; + } + + $total_order_amount += array_sum(array_column($newDatas, 'order_amount')); + $total_pay_fee += array_sum(array_column($newDatas, 'pay_fee')); + $total_real_pay_fee += array_sum(array_column($newDatas, 'real_pay_fee')); + + if ($pages['is_last_page']) { + $newDatas[] = ['order_id' => "订单总数:" . $total . ";订单总金额:¥" . $total_order_amount . ";应付总金额:¥" . $total_pay_fee . ";实付总金额:¥" . $total_real_pay_fee]; + } + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } +} diff --git a/application/admin/controller/shopro/traits/SkuPrice.php b/application/admin/controller/shopro/traits/SkuPrice.php new file mode 100644 index 0000000..a76f13b --- /dev/null +++ b/application/admin/controller/shopro/traits/SkuPrice.php @@ -0,0 +1,303 @@ +editMultSku($goods, $type); + } else { + $this->editSimSku($goods, $type); + } + } + + + /** + * 添加编辑单规格 + * + * @param GoodsModel $goods + * @param string $type + * @return void + */ + protected function editSimSku($goods, $type = 'add') + { + $params = $this->request->only([ + 'stock', 'stock_warning', 'sn', 'weight', 'cost_price', 'original_price', 'price' + ]); + + $data = [ + "goods_sku_ids" => null, + "goods_sku_text" => null, + "image" => null, + "goods_id" => $goods->id, + "stock" => $params['stock'] ?? 0, + "stock_warning" => isset($params['stock_warning']) && is_numeric($params['stock_warning']) + ? $params['stock_warning'] : null, + "sn" => $params['sn'] ?? "", + "weight" => isset($params['weight']) ? floatval($params['weight']) : 0, + "cost_price" => $params['cost_price'] ?? 0, + "original_price" => $params['original_price'] ?? 0, + "price" => $params['price'] ?? 0, + "status" => 'up' + ]; + + if ($type == 'edit') { + // 查询 + $skuPrice = SkuPriceModel::where('goods_id', $goods->id)->order('id', 'asc')->find(); + if ($skuPrice) { + // 删除多余的这个商品的其他规格以及规格项(防止多规格改为了单规格,遗留一批多余的 sku_price) + SkuPriceModel::where('goods_id', $goods->id)->where('id', '<>', $skuPrice->id)->delete(); + SkuModel::where('goods_id', $goods->id)->delete(); + } + + unset($data['stock']); // 移除库存(库存只能通过补货增加) + } + + if (!isset($skuPrice) || !$skuPrice) { + $skuPrice = new SkuPriceModel(); + } + + $skuPrice->save($data); + if ($type == 'add') { + // 增加补货记录 + $this->addStockLog($skuPrice, 0, $data['stock'], $type); + + // 检测库存预警 + $this->checkStockWarning($skuPrice, $type); + } + + } + + + /** + * 添加编辑多规格 + * + * @param GoodsModel $goods + * @param string $type + * @return void + */ + protected function editMultSku($goods, $type = 'add') + { + $params = $this->request->only([ + 'skus', 'sku_prices' + ]); + $skus = $params['skus'] ?? []; + $skuPrices = $params['sku_prices'] ?? []; + + $this->checkMultSku($skus, $skuPrices); + + // 编辑保存规格项 + $allChildrenSku = $this->saveSkus($goods, $skus, $type); + + if ($type == 'edit') { + // 编辑旧商品,先删除老的不用的 skuPrice + $oldSkuPriceIds = array_column($skuPrices, 'id'); + // 删除当前商品老的除了在基础上修改的skuPrice + SkuPriceModel::where('goods_id', $goods->id) + ->whereNotIn('id', $oldSkuPriceIds)->delete(); + + // 删除失效的库存预警记录 + $this->delNotStockWarning($oldSkuPriceIds, $goods->id); + } + + $min_key = null; // 最小加个对应的键值 + $min_price = min(array_column($skuPrices, 'price')); // 规格最小价格 + $originPrices = array_filter(array_column($skuPrices, 'original_price')); + $min_original_price = $originPrices ? min($originPrices) : 0; // 规格最小原始价格 + foreach ($skuPrices as $key => &$skuPrice) { + $skuPrice['goods_sku_ids'] = $this->getRealSkuIds($skuPrice['goods_sku_temp_ids'], $allChildrenSku); + $skuPrice['goods_id'] = $goods->id; + $skuPrice['goods_sku_text'] = is_array($skuPrice['goods_sku_text']) ? join(',', $skuPrice['goods_sku_text']) : $skuPrice['goods_sku_text']; + $skuPrice['stock_warning'] = isset($skuPrice['stock_warning']) && is_numeric($skuPrice['stock_warning']) + ? $skuPrice['stock_warning'] : null; // null 为关闭商品库存预警, 采用默认库存预警 + + // 移除无用 属性 + if ($type == 'add') { + // 添加直接移除 id + unset($skuPrice['id']); + } + unset($skuPrice['temp_id']); // 前端临时 id + unset($skuPrice['goods_sku_temp_ids']); // 前端临时规格 id,查找真实 id 用 + unset($skuPrice['createtime'], $skuPrice['updatetime']); // 删除时间 + + $skuPriceModel = new SkuPriceModel(); + if (isset($skuPrice['id']) && $skuPrice['id']) { + // type == 'edit' + unset($skuPrice['stock']); // 编辑商品 不能编辑库存,只能通过补货 + $skuPriceModel = $skuPriceModel->find($skuPrice['id']); + } + + if ($skuPriceModel) { + $skuPriceModel->allowField(true)->save($skuPrice); + + if ($type == 'add') { + // 增加补货记录 + $this->addStockLog($skuPriceModel, 0, $skuPrice['stock'], 'add'); // 记录库存记录 + + // 检测库存预警 + $this->checkStockWarning($skuPriceModel, $type); + } + } + + if (is_null($min_key) && $min_price == $skuPrice['price']) { + $min_key = $key; + } + } + + // 重新赋值最小价格和原价 + $goods->original_price = $skuPrices[$min_key]['original_price'] ?? $min_original_price; // 最小价格规格对应的原价 + $goods->price = $min_price; + $goods->save(); + } + + + /** + * 校验多规格是否填写完整 + * + * @param array $skus + * @param array $skuPrices + * @return void + */ + private function checkMultSku($skus, $skuPrices) + { + if (count($skus) < 1) { + error_stop('请填写规格列表'); + } + foreach ($skus as $key => $sku) { + if (count($sku['children']) <= 0) { + error_stop('主规格至少要有一个子规格'); + } + + // 验证子规格不能为空 + foreach ($sku['children'] as $k => $child) { + if (!isset($child['name']) || empty(trim($child['name']))) { + error_stop('子规格不能为空'); + } + } + } + + if (count($skuPrices) < 1) { + error_stop('请填写规格价格'); + } + + foreach ($skuPrices as &$price) { + // 校验多规格属性 + $this->svalidate($price, '.sku_params'); + } + } + + + /** + * 根据前端临时 temp_id 获取真实的数据库 id + * + * @param array $newGoodsSkuIds + * @param array $allChildrenSku + * @return string + */ + private function getRealSkuIds($newGoodsSkuIds, $allChildrenSku) + { + $newIdsArray = []; + foreach ($newGoodsSkuIds as $id) { + $newIdsArray[] = $allChildrenSku[$id]; + } + return join(',', $newIdsArray); + } + + + /** + * 差异更新 规格规格项(多的删除,少的添加) + * + * @param GoodsModel $goods + * @param array $skus + * @param string $type + * @return array + */ + private function saveSkus($goods, $skus, $type = 'add') + { + $allChildrenSku = []; + + if ($type == 'edit') { + // 删除无用老规格 + // 拿出需要更新的老规格 + $oldSkuIds = []; + foreach ($skus as $key => $sku) { + $oldSkuIds[] = $sku['id']; + + $childSkuIds = []; + if ($sku['children']) { + // 子项 id + $childSkuIds = array_column($sku['children'], 'id'); + } + + $oldSkuIds = array_merge($oldSkuIds, $childSkuIds); + $oldSkuIds = array_unique($oldSkuIds); + } + + // 删除老的除了在基础上修改的规格项 + SkuModel::where('goods_id', $goods->id)->whereNotIn('id', $oldSkuIds)->delete(); + } + + foreach ($skus as $s1 => &$k1) { + //添加主规格 + $current_id = $k1['id'] ?? 0; + if ($k1['id']) { + // 编辑 + SkuModel::where('id', $k1['id'])->update([ + 'name' => $k1['name'], + ]); + } else { + // 新增 + $k1Model = new SkuModel(); + $k1Model->save([ + 'name' => $k1['name'], + 'parent_id' => 0, + 'goods_id' => $goods->id + ]); + $k1['id'] = $current_id = $k1Model->id; + } + + foreach ($k1['children'] as $s2 => &$k2) { + $current_child_id = $k2['id'] ?? 0; + if ($k2['id']) { + // 编辑 + SkuModel::where('id', $k2['id'])->update([ + 'name' => $k2['name'], + ]); + } else { + // 新增 + $k2Model = new SkuModel(); + $k2Model->save([ + 'name' => $k2['name'], + 'parent_id' => $current_id, + 'goods_id' => $goods->id + ]); + $current_child_id = $k2Model->id; + } + + $allChildrenSku[$k2['temp_id']] = $current_child_id; + $k2['id'] = $current_child_id; + $k2['parent_id'] = $current_id; + } + } + + return $allChildrenSku; + } +} \ No newline at end of file diff --git a/application/admin/controller/shopro/user/Coupon.php b/application/admin/controller/shopro/user/Coupon.php new file mode 100644 index 0000000..d595971 --- /dev/null +++ b/application/admin/controller/shopro/user/Coupon.php @@ -0,0 +1,33 @@ +model = new UserCouponModel; + } + + + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + $coupon_id = $this->request->param('coupon_id'); + + $coupons = $this->model->sheepFilter()->with(['user', 'order']) + ->where('coupon_id', $coupon_id) + ->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $coupons); + } +} diff --git a/application/admin/controller/shopro/user/Group.php b/application/admin/controller/shopro/user/Group.php new file mode 100644 index 0000000..631972f --- /dev/null +++ b/application/admin/controller/shopro/user/Group.php @@ -0,0 +1,27 @@ +model = new GroupModel; + } + + + public function select() + { + $list = \app\admin\model\UserGroup::field('id,name,status')->select(); + $this->success('', null, $list); + } +} diff --git a/application/admin/controller/shopro/user/User.php b/application/admin/controller/shopro/user/User.php new file mode 100644 index 0000000..9aecd65 --- /dev/null +++ b/application/admin/controller/shopro/user/User.php @@ -0,0 +1,138 @@ +model = new UserModel; + } + + /** + * 用户列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $data = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $data); + } + + /** + * 用户详情 + * + * @param $id + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $user = $this->model->with(['third_oauth', 'parent_user'])->where('id', $id)->find(); + if (!$user) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $user); + } + + /** + * 更新用户 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + $params = $this->request->only(['username', 'nickname', 'mobile', 'password', 'avatar', 'gender', 'email', 'status']); + + if (empty($params['password'])) unset($params['password']); + if (empty($params['username'])) unset($params['username']); + + $params['id'] = $id; + $this->svalidate($params, '.edit'); + unset($params['id']); + + $user = $this->model->where('id', $id)->find(); + $user->save($params); + + $this->success('更新成功', null, $user); + } + + /** + * 删除用户(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + public function recharge() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['id', 'type', 'amount', 'memo']); + if (!in_array($params['type'], ['money', 'score'])) { + error_stop('参数错误'); + } + + $result = Db::transaction(function () use ($params) { + return WalletService::change($params['id'], $params['type'], $params['amount'], 'admin_recharge', [], $params['memo']); + }); + if ($result) { + $this->success('充值成功'); + } + $this->error('充值失败'); + } + + + /** + * 用户优惠券列表 + */ + public function coupon($id) + { + $userCoupons = UserCouponModel::sheepFilter()->with('coupon')->where('user_id', $id) + ->order('id', 'desc')->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $userCoupons); + } +} diff --git a/application/admin/controller/shopro/user/WalletLog.php b/application/admin/controller/shopro/user/WalletLog.php new file mode 100644 index 0000000..80d21dc --- /dev/null +++ b/application/admin/controller/shopro/user/WalletLog.php @@ -0,0 +1,90 @@ +model = new WalletLogModel; + } + + /** + * 余额记录 + */ + public function money($id) + { + $list_rows = $this->request->param('list_rows', 10); + $walletLogs = WalletLogModel::where('user_id', $id)->money()->order('id', 'desc')->paginate($list_rows); + + // 多态关联 oper + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'admin' => \app\admin\model\Admin::class, + 'system' => \app\admin\model\Admin::class, + ]; + $walletLogs = morph_to($walletLogs, $morphs, ['oper_type', 'oper_id']); + $walletLogs = $walletLogs->toArray(); + + // 解析操作人信息 + foreach ($walletLogs['data'] as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + $this->success('', null, $walletLogs); + } + + /** + * 积分记录 + */ + public function score($id) + { + $list_rows = $this->request->param('list_rows', 10); + $walletLogs = WalletLogModel::where('user_id', $id)->score()->order('id', 'desc')->paginate($list_rows); + + // 多态关联 oper + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'admin' => \app\admin\model\Admin::class, + 'system' => \app\admin\model\Admin::class, + ]; + $walletLogs = morph_to($walletLogs, $morphs, ['oper_type', 'oper_id']); + $walletLogs = $walletLogs->toArray(); + + // 解析操作人信息 + foreach ($walletLogs['data'] as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + $this->success('', null, $walletLogs); + } + + /** + * 佣金记录 + */ + public function commission($id) + { + $list_rows = $this->request->param('list_rows', 10); + $walletLogs = WalletLogModel::where('user_id', $id)->commission()->order('id', 'desc')->paginate($list_rows); + + // 多态关联 oper + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'admin' => \app\admin\model\Admin::class, + 'system' => \app\admin\model\Admin::class, + ]; + $walletLogs = morph_to($walletLogs, $morphs, ['oper_type', 'oper_id']); + $walletLogs = $walletLogs->toArray(); + + // 解析操作人信息 + foreach ($walletLogs['data'] as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + $this->success('', null, $walletLogs); + } +} diff --git a/application/admin/controller/shopro/wechat/Admin.php b/application/admin/controller/shopro/wechat/Admin.php new file mode 100644 index 0000000..fb8b3ec --- /dev/null +++ b/application/admin/controller/shopro/wechat/Admin.php @@ -0,0 +1,269 @@ +wechat = Wechat::officialAccountManage(); + } + + + public function getleagueQrcode() + { + $user_id = $this->request->param('user_id'); + $event = $this->request->param('event'); + if(empty($user_id)) $this->error('error'); +// var_dump($event);exit; + if (!in_array($event, ['bind'])) { + $this->error('参数错误'); + } + + $adminId = $this->auth->id; + // var_dump($this->auth);exit; + $thirdOauth = ClubThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'user_id' => $user_id + ])->find(); + + if ($thirdOauth) { + error_stop('已绑定微信账号', -2, $thirdOauth); + } + + // 二维码和缓存过期时间 + $expireTime = 1 * 60; + + // 事件唯一标识 + $eventId = Random::uuid(); + // var_dump($eventId);exit; + $cacheKey = "wechatAdmin.{$event}.{$eventId}"; + + cache($cacheKey, ['id' => 0], $expireTime); + + try { + $result = $this->wechat->qrcode->temporary($cacheKey, $expireTime); + // var_dump($result);exit; + $qrcode = $this->wechat->qrcode->url($result['ticket']); + // var_dump($qrcode);exit; + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + // return json($qrcode);exit; + // var_dump($qrcode);exit; + $this->success('', null, [ + 'url' => $qrcode, + 'eventId' => $eventId + ]); + } + // 检查扫码结果 + public function checkLeagueScan() + { + $event = $this->request->param('event'); + $eventId = $this->request->param('eventId'); + $user_id = $this->request->param('user_id'); + + if (!in_array($event, ['bind'])) { + error_stop('参数错误'); + } + + $cacheKey = "wechatAdmin.{$event}.{$eventId}"; + + $cacheValue = cache($cacheKey); + // var_dump($cacheValue);exit; + if (empty($cacheValue)) { + error_stop('二维码已过期, 请重新扫码'); + } + + if ($cacheValue['id'] === 0) { + error_stop('等待扫码', -1); + } + + if ($cacheValue['id'] !== 0) { + switch ($event) { + case 'bind': + $adminId = $this->auth->id; + + $thirdOauth = ClubThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + // 'user_id' => $user_id, + 'openid' => $cacheValue['id'], + ])->find(); + + if ($thirdOauth && $thirdOauth->admin_id !== 0) { + error_stop('该微信账号已被绑定'); + } + + if (!$thirdOauth) { + $thirdOauth = ClubThirdOauth::create([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'openid' => $cacheValue['id'], + 'user_id' => $user_id + ]); + } else { + $thirdOauth->admin_id = $adminId; + $thirdOauth->save(); + } + break; + } + $this->success(); + } + } + + public function leagueunbind() + { + // $adminId = $this->auth->id; + $user_id = $this->request->param('user_id'); + $thirdOauth = ClubThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'user_id' => $user_id + ])->find(); + // var_dump($thirdOauth);exit; + if ($thirdOauth) { + // $thirdOauth->save(['openid'=>''],['user_id'=>$user_id]); + $thirdOauth->delete(); + // $thirdOauth->user_id = 0; + // $thirdOauth->save(); + } + $this->success(); + } + + // 获取公众号二维码 + public function getQrcode() + { + + $event = $this->request->param('event'); +// var_dump($event);exit; + if (!in_array($event, ['bind'])) { + $this->error('参数错误'); + } + + $adminId = $this->auth->id; + // var_dump($adminId);exit; + $thirdOauth = ThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'admin_id' => $adminId + ])->find(); + + if ($thirdOauth) { + error_stop('已绑定微信账号', -2, $thirdOauth); + } + + // 二维码和缓存过期时间 + $expireTime = 1 * 60; + + // 事件唯一标识 + $eventId = Random::uuid(); + // var_dump($eventId);exit; + $cacheKey = "wechatAdmin.{$event}.{$eventId}"; + + cache($cacheKey, ['id' => 0], $expireTime); + + try { + $result = $this->wechat->qrcode->temporary($cacheKey, $expireTime); + // var_dump($result);exit; + $qrcode = $this->wechat->qrcode->url($result['ticket']); + // var_dump($qrcode);exit; + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + // return json($qrcode);exit; + // var_dump($qrcode);exit; + $this->success('', null, [ + 'url' => $qrcode, + 'eventId' => $eventId + ]); + } + + // 检查扫码结果 + public function checkScan() + { + $event = $this->request->param('event'); + $eventId = $this->request->param('eventId'); + + if (!in_array($event, ['bind'])) { + error_stop('参数错误'); + } + + $cacheKey = "wechatAdmin.{$event}.{$eventId}"; + + $cacheValue = cache($cacheKey); + // var_dump($cacheValue);exit; + if (empty($cacheValue)) { + error_stop('二维码已过期, 请重新扫码'); + } + + if ($cacheValue['id'] === 0) { + error_stop('等待扫码', -1); + } + + if ($cacheValue['id'] !== 0) { + switch ($event) { + case 'bind': + $adminId = $this->auth->id; + + $thirdOauth = ThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'openid' => $cacheValue['id'], + ])->find(); + + if ($thirdOauth && $thirdOauth->admin_id !== 0) { + error_stop('该微信账号已被绑定'); + } + + if (!$thirdOauth) { + $thirdOauth = ThirdOauth::create([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'openid' => $cacheValue['id'], + 'admin_id' => $adminId + ]); + } else { + $thirdOauth->admin_id = $adminId; + $thirdOauth->save(); + } + break; + } + $this->success(); + } + } + + // 解绑 + public function unbind() + { + $adminId = $this->auth->id; + + $thirdOauth = ThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'admin_id' => $adminId + ])->find(); + + if ($thirdOauth) { + $thirdOauth->admin_id = 0; + $thirdOauth->save(); + } + $this->success(); + } + + +} diff --git a/application/admin/controller/shopro/wechat/Config.php b/application/admin/controller/shopro/wechat/Config.php new file mode 100644 index 0000000..d49d100 --- /dev/null +++ b/application/admin/controller/shopro/wechat/Config.php @@ -0,0 +1,30 @@ +request->isAjax()) { + return $this->view->fetch(); + } + + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('wechat.officialAccount', false); + $configs['server_url'] = request()->domain() . '/addons/shopro/wechat.serve'; + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('wechat.officialAccount', $this->request->param()); + } + + $this->success('操作成功', null, $configs); + } +} diff --git a/application/admin/controller/shopro/wechat/Material.php b/application/admin/controller/shopro/wechat/Material.php new file mode 100644 index 0000000..3905e5b --- /dev/null +++ b/application/admin/controller/shopro/wechat/Material.php @@ -0,0 +1,178 @@ +wechat = Wechat::officialAccountManage(); + $this->model = new \app\admin\model\shopro\wechat\Material; + } + + /** + * 素材列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list_rows = intval($this->request->param('list_rows', 10)); + $page = intval($this->request->param('page', 1)); + $type = $this->request->param('type', 'news'); + $offset = intval(($page - 1) * $list_rows); + + if (in_array($type, ['text', 'link'])) { + $data = $this->model->sheepFilter()->where('type', $type)->paginate(request()->param('list_rows', 10)); + } else { + // 使用微信远程素材列表 + try { + $res = $this->wechat->material->list($type, $offset, $list_rows); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + + $data = [ + 'current_page' => $page, + 'data' => $res['item'], + 'last_page' => intval(ceil($res['total_count'] / $list_rows)), + 'per_page' => $list_rows, + 'total' => intval($res['total_count']), + ]; + } + + $this->success('', null, $data); + } + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->get($id); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + /** + * 添加 + * + * @return \think\Response + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['type', 'content']); + + if ($params['type'] === 'text') { + $params['content'] = urldecode($params['content']); + } + + Db::transaction(function () use ($params) { + $this->model->save($params); + }); + $this->success('保存成功'); + } + + /** + * 编辑 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $material = $this->model->get($id); + $params = $this->request->only(['type', 'content']); + if ($params['type'] === 'text') { + $params['content'] = urldecode($params['content']); + } + + Db::transaction(function () use ($params, $material) { + $material->save($params); + }); + + $this->success('更新成功'); + } + + + /** + * 删除 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + $is_real = $this->request->param('is_real', 0); + Db::transaction(function () use ($id, $is_real) { + $menu = $this->model->get($id); + if ($is_real) { + $menu->force()->delete(); + } else { + $menu->delete(); + } + }); + + $this->success('删除成功'); + } + + /** + * 菜单列表 + * + * @return Response + */ + public function select() + { + $list_rows = intval($this->request->param('list_rows', 10)); + $page = intval($this->request->param('page', 1)); + $type = $this->request->param('type', 'news'); + $offset = intval(($page - 1) * $list_rows); + + if (in_array($type, ['text', 'link'])) { + $data = $this->model->where('type', $type)->order('id desc')->paginate(request()->param('list_rows', 10)); + } else { + // 使用微信远程素材列表 + try { + $res = $this->wechat->material->list($type, $offset, $list_rows); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + + $data = [ + 'current_page' => $page, + 'data' => $res['item'], + 'last_page' => intval(ceil($res['total_count'] / $list_rows)), + 'per_page' => $list_rows, + 'total' => intval($res['total_count']), + ]; + } + $this->success('获取成功', null, $data); + } +} diff --git a/application/admin/controller/shopro/wechat/Menu.php b/application/admin/controller/shopro/wechat/Menu.php new file mode 100644 index 0000000..7a7bbab --- /dev/null +++ b/application/admin/controller/shopro/wechat/Menu.php @@ -0,0 +1,223 @@ +wechat = Wechat::officialAccountManage(); + $this->model = new \app\admin\model\shopro\wechat\Menu; + } + + /** + * 公众号配置 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $current = $this->getCurrentMenu(); + $data = $this->model->sheepFilter()->paginate(request()->param('list_rows', 10)); + + $this->success('操作成功', null, ['current' => $current, 'list' => $data]); + } + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->get($id); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + /** + * 添加菜单 + * + * @param int $publish 发布状态:0=不发布,1=直接发布 + * @return \think\Response + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $publish = $this->request->param('publish', 0); + $params = $this->request->only(['name', 'rules']); + // 参数校验 + $this->svalidate($params, '.add'); + + Db::transaction(function () use ($params, $publish) { + $menu = $this->model->save($params); + if ($menu && $publish) { + $this->publishMenu($this->model->id); + } + return $menu; + }); + + $this->success('保存成功'); + } + + /** + * 编辑 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $menu = $this->model->get($id); + + $publish = $this->request->param('publish', 0); + $params = $this->request->only(['name', 'rules']); + + // 参数校验 + $this->svalidate($params); + + $menu = Db::transaction(function () use ($params, $menu, $publish) { + $menu->save($params); + if ($publish) { + $this->publishMenu($menu->id); + } + return $menu; + }); + + $this->success('更新成功'); + } + + + /** + * 删除 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + Db::transaction(function () use ($id) { + $menu = $this->model->get($id); + $menu->delete(); + }); + + $this->success('删除成功'); + } + + + /** + * 发布菜单 + * + * @param $id + * @return \think\Response + */ + public function publish($id) + { + Db::transaction(function () use ($id) { + return $this->publishMenu($id); + }); + + $this->success('发布成功'); + } + + /** + * 复制菜单 + * + * @return Response + */ + public function copy($id = 0) + { + if ($id == 0) { + $data = [ + 'name' => '复制 当前菜单', + 'rules' => $this->getCurrentMenu(), + ]; + } else { + $menu = $this->model->get($id); + $data = [ + 'name' => '复制 ' . $menu->name, + 'rules' => $menu->rules + ]; + } + + $menu = $this->model->save($data); + $this->success('复制成功'); + } + + // 发布菜单 + private function publishMenu($id) + { + $menu = $this->model->get($id); + + if ($this->setCurrentMenu($menu->rules)) { + $this->model->where('id', '<>', $menu->id)->update(['status' => 0]); + + return $menu->save([ + 'status' => 1, + 'publishtime' => time() + ]); + } + return false; + } + + + // 获取当前菜单 + private function getCurrentMenu() + { + try { + $currentMenu = $this->wechat->menu->current(); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + if (isset($currentMenu['selfmenu_info']['button'])) { + $buttons = $currentMenu['selfmenu_info']['button']; + foreach ($buttons as &$button) { + if (isset($button['sub_button'])) { + $button['sub_button'] = $button['sub_button']['list']; + } + } + return $buttons; + } else { + return []; + } + } + + // 设置菜单 + private function setCurrentMenu($rules) + { + try { + $result = $this->wechat->menu->create($rules); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + + if (isset($result['errcode']) && $result['errcode'] === 0) { + return true; + } else { + $this->error($result['errmsg'] ?? '发布失败'); + } + } +} diff --git a/application/admin/controller/shopro/wechat/Reply.php b/application/admin/controller/shopro/wechat/Reply.php new file mode 100644 index 0000000..65856b8 --- /dev/null +++ b/application/admin/controller/shopro/wechat/Reply.php @@ -0,0 +1,143 @@ +model = new \app\admin\model\shopro\wechat\Reply; + } + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $group = $this->request->param('group', 'keywords'); + $data = $this->model->sheepFilter()->where('group', $group)->select(); + $this->success('操作成功', null, $data); + } + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['group', 'keywords', 'type', 'status', 'content']); + + Db::transaction(function () use ($params) { + $result = $this->model->save($params); + if($result) { + $reply = $this->model; + if($reply->group !== 'keywords' && $reply->status === 'enable') { + $this->model->where('group', $reply->group)->where('id', '<>', $reply->id)->enable()->update(['status' => 'disabled']); + } + } + return $result; + }); + + $this->success('保存成功'); + } + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->get($id); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + $reply = $this->model->get($id); + $params = $this->request->only(['keywords', 'type', 'status', 'content']); + + // 参数校验 + // $this->svalidate($params); + + $result = Db::transaction(function () use ($params, $reply) { + $result = $reply->save($params); + if($result) { + if($reply->group !== 'keywords' && $reply->status === 'enable') { + $this->model->where('group', $reply->group)->where('id', '<>', $reply->id)->enable()->update(['status' => 'disabled']); + } + } + return $result; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/admin/controller/user/Club.php b/application/admin/controller/user/Club.php new file mode 100644 index 0000000..32da93a --- /dev/null +++ b/application/admin/controller/user/Club.php @@ -0,0 +1,160 @@ +model = new \app\admin\model\user\Club; + + } + + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if (false === $this->request->isAjax()) { + return $this->view->fetch(); + } + //如果发送的来源是 Selectpage,则转发到 Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + [$where, $sort, $order, $offset, $limit] = $this->buildparams(); + $list = $this->model + ->where($where) + ->order($sort, $order) + ->paginate($limit); + $result = ['total' => $list->total(), 'rows' => $list->items()]; + return json($result); + } + + public function show($ids = null){ + $row = $this->model->get($ids); + $this->view->assign('row', $row); + return $this->view->fetch(); + } + /** + * 编辑 + */ + public function edit($ids = null) + { + // if ($this->request->isPost()) { + // $this->token(); + // } + + // if ($this->request->param('player_id')){ + // $ids = $this->request->param('player_id'); + // } + + $row = $this->model->get($ids); + // var_dump( $rows);exit; + if ($this->request->isAjax()) { + + $audit_status = $this->request->post('status'); + $reason = $this->request->post('reject_reason', null); + + + if ($audit_status != 0 && $reason == null){ + $this->error("请填写审核内容!"); + } + + switch ($audit_status){ + case 0: + $audit_status = 9; //通过 + + // $user = new User(); + // $user_res = $user->where('id',$row['user_id'])->find(); + // // var_dump($user_res->nickname);exit; + // if (!empty($user_res->mobile)) { + // $alisms = new \addons\alisms\library\Alisms(); + // $config = get_addon_config('alisms'); + // $template = "SMS_465371899"; + // $sign = $config['sign']; + // // var_dump($sign);exit; + // $param = [ + // 'nickname' => $user_res->nickname, + // // 'content' => "已经过确认和审核,你可以进行赛事报名。" + // ]; + // $res = $alisms->mobile($user_res->mobile) + // ->template($template) + // ->sign($sign) + // ->param($param) + // ->send(); + // // var_dump($res);exit; + // } + + + $this->model->update(['id' => $ids, 'status' => $audit_status, 'reject_reason' => '']); + break; + case 1: + $audit_status = 2; //退回修改 + $this->model->update(['id' => $ids, 'status' => $audit_status, 'reject_reason' => $reason]); + break; + case 2: + $audit_status = 3; //拒绝 + $this->model->update(['id' => $ids, 'status' => $audit_status, 'reject_reason' => $reason]); + break; + } + + // $this->model->update(['id' => $ids, 'status' => $audit_status]); + + + + + $this->success(); + + } + // if ($row['status'] == 9 ){ + // $row['status'] = 0; + // } + // if ($row['status'] == 2 ){ + // $row['status'] = 1; + // } + // if ($row['status'] == 3 ){ + // $row['status'] = 2; + // } + if ($this->request->param('player_id')){ + $row['is_show'] = 1; + } + $this->modelValidate = true; + if (!$row) { + $this->error(__('No Results were found')); + } + // $this->view->assign('groupList', build_select('row[group_id]', \app\admin\model\UserGroup::column('id,name'), $row['member']['group_id'], ['class' => 'form-control selectpicker'])); + $this->view->assign('row', $row); + return $this->view->fetch(); + } + + public function save_asfc_cert() + { + $id = $this->request->post("id"); + $photo = $this->request->post("style_photo"); + $row = $this->model->update(['id' => $id, 'asfc_cert' => $photo]); + + if ($row) { + $this->success("保存成功"); + } else { + $this->error("保存失败"); + } + + } + +} diff --git a/application/admin/controller/user/Group.php b/application/admin/controller/user/Group.php new file mode 100644 index 0000000..b4c38c1 --- /dev/null +++ b/application/admin/controller/user/Group.php @@ -0,0 +1,52 @@ +model = model('UserGroup'); + $this->view->assign("statusList", $this->model->getStatusList()); + } + + public function add() + { + if ($this->request->isPost()) { + $this->token(); + } + $nodeList = \app\admin\model\UserRule::getTreeList(); + $this->assign("nodeList", $nodeList); + return parent::add(); + } + + public function edit($ids = null) + { + if ($this->request->isPost()) { + $this->token(); + } + $row = $this->model->get($ids); + if (!$row) { + $this->error(__('No Results were found')); + } + $rules = explode(',', $row['rules']); + $nodeList = \app\admin\model\UserRule::getTreeList($rules); + $this->assign("nodeList", $nodeList); + return parent::edit($ids); + } + +} diff --git a/application/admin/controller/user/Player.php b/application/admin/controller/user/Player.php new file mode 100644 index 0000000..0fbd6f5 --- /dev/null +++ b/application/admin/controller/user/Player.php @@ -0,0 +1,235 @@ +model = model('Players'); + } + + public function index() + { + + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + $this->model->query("UPDATE peewee_players SET age = FLOOR(TIMESTAMPDIFF(SECOND, birthday, NOW())/(60*60*24*365))"); + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $list = $this->model +// ->alias('players') +// ->join('user b', 'players.member_id = b.id') + ->with('member') + ->where($where) + ->order("player_status asc") + ->paginate($limit); + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + if ($this->request->isPost()) { + $this->token(); + } + + if ($this->request->param('player_id')){ + $ids = $this->request->param('player_id'); + } + + $row = $this->model + ->with('member')->find($ids); + + // if (strpos($row['player_pic'], 'https://ydool2017.oss-cn-shanghai.aliyuncs.com/') === false) { + // // $headers = get_headers('https://ydool2017.oss-cn-shanghai.aliyuncs.com/'.$row['card_front_view']); + // // if (!is_array($headers) || strpos($headers[0], '200 OK') === false) { + // $row['player_pic'] = "https://www.fpvone.cn".$row['player_pic']; + // // var_dump($row['card_front_view']);exit; + // // } + // } + + // if (strpos($row['card_front_view'], 'https://ydool2017.oss-cn-shanghai.aliyuncs.com/') === false) { + // // $headers = get_headers('https://ydool2017.oss-cn-shanghai.aliyuncs.com/'.$row['card_front_view']); + // // if (!is_array($headers) || strpos($headers[0], '200 OK') === false) { + // $row['card_front_view'] = "https://www.fpvone.cn".$row['card_front_view']; + // // var_dump($row['card_front_view']);exit; + // // } + // } + + // if (strpos($row['card_back_view'], 'https://ydool2017.oss-cn-shanghai.aliyuncs.com/') === false) { + // // $headers = get_headers('https://ydool2017.oss-cn-shanghai.aliyuncs.com/'.$row['card_back_view']); + // // if (!is_array($headers) || strpos($headers[0], '200 OK') === false) { + // $row['card_back_view'] = "https://www.fpvone.cn".$row['card_back_view']; + // // } + // } + + if ($this->request->isAjax()) { + + $audit_status = $this->request->post('audit_status'); + $reason = $this->request->post('reason', null); + $real_name = $this->request->post('row.real_name'); + if(empty($real_name)) {$this->error('姓名不能为空');} + // $real_name = input('post.row.real_name'); + // var_dump($real_name);exit; + // var_dump($_POST['row']['real_name']);exit; + + if ($audit_status != 0 && $reason == null){ + $this->error("请填写审核内容!"); + } + + //判断审核表里是否有该数据 + $playerCheck = new PlayerCheck(); + $check_info = $playerCheck->where(['player_id' => $ids])->find(); + + if (empty($check_info)){ + $player_info = $this->model->find($ids); + + $add_check_player['member_id'] = $player_info->member_id; + $add_check_player['player_id'] = $ids; + $add_check_player['before_status'] = $player_info->player_status; + $add_check_player['after_status'] = $audit_status; + $add_check_player['mark'] = $reason; + $add_check_player['created_at'] = date("Y-m-d H:i:s", time()); + + }else{ + $player_info = $this->model->find($ids); + + $add_check_player['member_id'] = $player_info->member_id; + $add_check_player['player_id'] = $ids; + $add_check_player['before_status'] = $check_info->before_status; + $add_check_player['after_status'] = $audit_status; + $add_check_player['mark'] = $reason; + $add_check_player['created_at'] = $check_info->created_at; + $add_check_player['updated_at'] = date("Y-m-d H:i:s", time()); + + } + // var_dump($audit_status);exit; + // 启动事务 + Db::startTrans(); + try { + $playerCheck->insert($add_check_player); + switch ($audit_status){ + case 0: + $audit_status = 9; + break; + case 1: + $audit_status = 2; + break; + default: + $audit_status = -1; + break; + } + + $this->model->update(['id' => $player_info->id, 'player_status' => $audit_status, 'real_name' => $real_name]); + + if (!empty($player_info->phone)) { + $alisms = new \addons\alisms\library\Alisms(); + $config = get_addon_config('alisms'); + $template = "SMS_465344628"; + $sign = $config['sign']; + + if ($audit_status == 9) { + $param = [ + 'name' => $player_info->real_name, + 'content' => "已经过确认和审核。" + ]; + } else { + $param = [ + 'name' => $player_info->real_name, + 'content' => "未通过审核,请根据审核内容修改后重新提交。" + ]; + } + // var_dump($player_info->real_name);exit; + // $player_info->phone = '13626832346'; + $res = $alisms->mobile($player_info->phone) + ->template($template) + ->sign($sign) + ->param($param) + ->send(); + // var_dump($res);exit; + } + } catch (\Exception $e) { + // 回滚事务 + Db::rollback(); + $this->error($e->getMessage()); + } + + $this->success(); + + } + + if ($this->request->param('player_id')){ + $row['is_show'] = 1; + } + $this->modelValidate = true; + if (!$row) { + $this->error(__('No Results were found')); + } + $this->view->assign('groupList', build_select('row[group_id]', \app\admin\model\UserGroup::column('id,name'), $row['member']['group_id'], ['class' => 'form-control selectpicker'])); + $this->view->assign('row', $row); + return $this->view->fetch(); + } + + public function save_style_photo() + { + $id = $this->request->post("id"); + $photo = $this->request->post("style_photo"); + + $row = $this->model->update(['id' => $id, 'style_photo' => $photo]); + + if ($row) { + $this->success("保存成功"); + } else { + $this->error("保存失败"); + } + + } + + public function save_player_pic() + { + $id = $this->request->post("id"); + $photo = $this->request->post("style_photo"); + $row = $this->model->update(['id' => $id, 'player_pic' => $photo]); + + if ($row) { + $this->success("保存成功"); + } else { + $this->error("保存失败"); + } + + } + +} \ No newline at end of file diff --git a/application/admin/controller/user/Rule.php b/application/admin/controller/user/Rule.php new file mode 100644 index 0000000..611d687 --- /dev/null +++ b/application/admin/controller/user/Rule.php @@ -0,0 +1,108 @@ +model = model('UserRule'); + $this->view->assign("statusList", $this->model->getStatusList()); + // 必须将结果集转换为数组 + $ruleList = collection($this->model->order('weigh', 'desc')->select())->toArray(); + foreach ($ruleList as $k => &$v) { + $v['title'] = __($v['title']); + $v['remark'] = __($v['remark']); + } + unset($v); + Tree::instance()->init($ruleList); + $this->rulelist = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0), 'title'); + $ruledata = [0 => __('None')]; + foreach ($this->rulelist as $k => &$v) { + if (!$v['ismenu']) { + continue; + } + $ruledata[$v['id']] = $v['title']; + } + $this->view->assign('ruledata', $ruledata); + } + + /** + * 查看 + */ + public function index() + { + if ($this->request->isAjax()) { + $list = $this->rulelist; + $total = count($this->rulelist); + + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $this->token(); + } + return parent::add(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + if ($this->request->isPost()) { + $this->token(); + } + return parent::edit($ids); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + if ($ids) { + $delIds = []; + foreach (explode(',', $ids) as $k => $v) { + $delIds = array_merge($delIds, Tree::instance()->getChildrenIds($v, true)); + } + $delIds = array_unique($delIds); + $count = $this->model->where('id', 'in', $delIds)->delete(); + if ($count) { + $this->success(); + } + } + $this->error(); + } + +} diff --git a/application/admin/controller/user/User.php b/application/admin/controller/user/User.php new file mode 100644 index 0000000..e511661 --- /dev/null +++ b/application/admin/controller/user/User.php @@ -0,0 +1,107 @@ +model = model('User'); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + ->with('group') + ->with('players') + ->where($where) + ->order($sort, $order) + ->paginate($limit); + foreach ($list as $k => $v) { + $v->avatar = $v->avatar ? cdnurl($v->avatar, true) : letter_avatar($v->nickname); + $v->hidden(['password', 'salt']); + } + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + + /** + * 添加 + */ + public function add() + { + if ($this->request->isPost()) { + $this->token(); + } + return parent::add(); + } + + /** + * 编辑 + */ + public function edit($ids = null) + { + if ($this->request->isPost()) { + $this->token(); + } + $row = $this->model->get($ids); + $this->modelValidate = true; + if (!$row) { + $this->error(__('No Results were found')); + } + $this->view->assign('groupList', build_select('row[group_id]', \app\admin\model\UserGroup::column('id,name'), $row['group_id'], ['class' => 'form-control selectpicker'])); + return parent::edit($ids); + } + + /** + * 删除 + */ + public function del($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ? $ids : $this->request->post("ids"); + $row = $this->model->get($ids); + $this->modelValidate = true; + if (!$row) { + $this->error(__('No Results were found')); + } + Auth::instance()->delete($row['id']); + $this->success(); + } + +} diff --git a/application/admin/lang/zh-cn.php b/application/admin/lang/zh-cn.php new file mode 100644 index 0000000..fa9bce3 --- /dev/null +++ b/application/admin/lang/zh-cn.php @@ -0,0 +1,228 @@ + '会员ID', + 'Username' => '用户名', + 'Nickname' => '昵称', + 'Password' => '密码', + 'Sign up' => '注 册', + 'Sign in' => '登 录', + 'Sign out' => '退 出', + 'Keep login' => '保持会话', + 'Guest' => '游客', + 'Welcome' => '%s,你好!', + 'View' => '查看', + 'Add' => '添加', + 'Edit' => '编辑', + 'Del' => '删除', + 'Delete' => '删除', + 'Import' => '导入', + 'Export' => '导出', + 'All' => '全部', + 'Detail' => '详情', + 'Multi' => '批量更新', + 'Setting' => '配置', + 'Move' => '移动', + 'Name' => '名称', + 'Status' => '状态', + 'Weigh' => '权重', + 'Operate' => '操作', + 'Warning' => '温馨提示', + 'Default' => '默认', + 'Article' => '文章', + 'Page' => '单页', + 'OK' => '确定', + 'Apply' => '应用', + 'Cancel' => '取消', + 'Clear' => '清空', + 'Custom Range' => '自定义', + 'Today' => '今天', + 'Yesterday' => '昨天', + 'Last 7 days' => '最近7天', + 'Last 30 days' => '最近30天', + 'Last month' => '上月', + 'This month' => '本月', + 'Loading' => '加载中', + 'Money' => '余额', + 'Score' => '积分', + 'More' => '更多', + 'Yes' => '是', + 'No' => '否', + 'Normal' => '正常', + 'Hidden' => '隐藏', + 'Locked' => '锁定', + 'Submit' => '提交', + 'Reset' => '重置', + 'Execute' => '执行', + 'Close' => '关闭', + 'Choose' => '选择', + 'Go' => '跳转', + 'Search' => '搜索', + 'Refresh' => '刷新', + 'Install' => '安装', + 'Uninstall' => '卸载', + 'First' => '首页', + 'Previous' => '上一页', + 'Next' => '下一页', + 'Last' => '末页', + 'None' => '无', + 'Home' => '主页', + 'Online' => '在线', + 'Login' => '登录', + 'Logout' => '退出', + 'Profile' => '个人资料', + 'Index' => '首页', + 'Hot' => '热门', + 'Recommend' => '推荐', + 'Upload' => '上传', + 'Uploading' => '上传中', + 'Code' => '编号', + 'Message' => '内容', + 'Line' => '行号', + 'File' => '文件', + 'Menu' => '菜单', + 'Type' => '类型', + 'Title' => '标题', + 'Content' => '内容', + 'Append' => '追加', + 'Select' => '选择', + 'Memo' => '备注', + 'Parent' => '父级', + 'Params' => '参数', + 'Permission' => '权限', + 'Check all' => '选中全部', + 'Expand all' => '展开全部', + 'Begin time' => '开始时间', + 'End time' => '结束时间', + 'Create time' => '创建时间', + 'Update time' => '更新时间', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Deletetime' => '删除时间', + 'Flag' => '标志', + 'Drag to sort' => '拖动进行排序', + 'Redirect now' => '立即跳转', + 'Key' => '键', + 'Value' => '值', + 'Common search' => '普通搜索', + 'Search %s' => '搜索 %s', + 'View %s' => '查看 %s', + '%d second%s ago' => '%d秒前', + '%d minute%s ago' => '%d分钟前', + '%d hour%s ago' => '%d小时前', + '%d day%s ago' => '%d天前', + '%d week%s ago' => '%d周前', + '%d month%s ago' => '%d月前', + '%d year%s ago' => '%d年前', + '%d second%s after' => '%d秒后', + '%d minute%s after' => '%d分钟后', + '%d hour%s after' => '%d小时后', + '%d day%s after' => '%d天后', + '%d week%s after' => '%d周后', + '%d month%s after' => '%d月后', + '%d year%s after' => '%d年后', + 'Set to normal' => '设为正常', + 'Set to hidden' => '设为隐藏', + 'Recycle bin' => '回收站', + 'Restore' => '还原', + 'Restore all' => '还原全部', + 'Destroy' => '销毁', + 'Destroy all' => '清空回收站', + 'Nothing need restore' => '没有需要还原的数据', + //提示 + 'Go back' => '返回首页', + 'Jump now' => '立即跳转', + 'Click to search %s' => '点击搜索 %s', + 'Click to toggle' => '点击切换', + 'Operation completed' => '操作成功!', + 'Operation failed' => '操作失败!', + 'Unknown data format' => '未知的数据格式!', + 'Network error' => '网络错误!', + 'Invalid parameters' => '未知参数', + 'No results were found' => '记录未找到', + 'No rows were inserted' => '未插入任何行', + 'No rows were deleted' => '未删除任何行', + 'No rows were updated' => '未更新任何行', + 'Parameter %s can not be empty' => '参数%s不能为空', + 'Are you sure you want to delete the %s selected item?' => '确定删除选中的 %s 项?', + 'Are you sure you want to delete this item?' => '确定删除此项?', + 'Are you sure you want to delete or turncate?' => '确定删除或清空?', + 'Are you sure you want to truncate?' => '确定清空?', + 'Token verification error' => 'Token验证错误!', + 'You have no permission' => '你没有权限访问', + 'Please enter your username' => '请输入你的用户名', + 'Please enter your password' => '请输入你的密码', + 'Please login first' => '请登录后操作', + 'Uploaded successful' => '上传成功', + 'You can upload up to %d file%s' => '你最多还可以上传%d个文件', + 'You can choose up to %d file%s' => '你最多还可以选择%d个文件', + 'Chunk file write error' => '分片写入失败', + 'Chunk file info error' => '分片文件错误', + 'Chunk file merge error' => '分片合并错误', + 'Chunk file disabled' => '未开启分片上传功能', + 'Cancel upload' => '取消上传', + 'Upload canceled' => '上传已取消', + 'No file upload or server upload limit exceeded' => '未上传文件或超出服务器上传限制', + 'Uploaded file format is limited' => '上传文件格式受限制', + 'Uploaded file is not a valid image' => '上传文件不是有效的图片文件', + 'Are you sure you want to cancel this upload?' => '确定取消上传?', + 'Remove file' => '移除文件', + 'You can only upload a maximum of %s files' => '你最多允许上传 %s 个文件', + 'You can\'t upload files of this type' => '不允许上传的文件类型', + 'Server responded with %s code' => '服务端响应(Code:%s)', + 'File is too big (%sMiB), Max filesize: %sMiB' => '当前上传(%sM),最大允许上传文件大小:%sM', + 'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中', + 'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转', + 'Click to uncheck all' => '点击取消全部', + 'Multiple selection mode: %s checked' => '跨页选择模式,已选 %s 项', + //菜单 + 'Dashboard' => '控制台', + 'General' => '常规管理', + 'Category' => '分类管理', + 'Addon' => '插件管理', + 'Auth' => '权限管理', + 'Config' => '系统配置', + 'Attachment' => '附件管理', + 'Admin' => '管理员管理', + 'Admin log' => '管理员日志', + 'Group' => '角色组', + 'Rule' => '菜单规则', + 'User' => '会员管理', + 'User group' => '会员分组', + 'User rule' => '会员规则', + 'Select attachment' => '选择附件', + 'Update profile' => '更新个人信息', + 'Local install' => '本地安装', + 'Update state' => '禁用启用', + 'Admin group' => '超级管理组', + 'Second group' => '二级管理组', + 'Third group' => '三级管理组', + 'Second group 2' => '二级管理组2', + 'Third group 2' => '三级管理组2', + 'Dashboard tips' => '用于展示当前系统中的统计数据、统计报表及重要实时数据', + 'Config tips' => '可以在此增改系统的变量和分组,也可以自定义分组和变量', + 'Category tips' => '分类类型请在常规管理->系统配置->字典配置中添加', + 'Attachment tips' => '主要用于管理上传到服务器或第三方存储的数据', + 'Addon tips' => '可在线安装、卸载、禁用、启用、配置、升级插件,插件升级前请做好备份。', + 'Admin tips' => '一个管理员可以有多个角色组,左侧的菜单根据管理员所拥有的权限进行生成', + 'Admin log tips' => '管理员可以查看自己所拥有的权限的管理员日志', + 'Group tips' => '角色组可以有多个,角色有上下级层级关系,如果子角色有角色组和管理员的权限则可以派生属于自己组别的下级角色组或管理员', + 'Rule tips' => '菜单规则通常对应一个控制器的方法,同时菜单栏数据也从规则中获取', + 'Access is allowed only to the super management group' => '仅超级管理组能访问', + 'Local addon' => '本地插件', + // 前台菜单 + 'Frontend' => '前台', + 'API Interface' => 'API接口', + 'User Module' => '会员模块', + 'Register' => '注册', + '1' => '申请中', + '-1' => '申请不通过', + '9' => '正式飞手', + // '10' =>'正常', + // '11' =>'关闭', + 'User Center' => '会员中心', + 'User Center' => '会员中心', + 'User Center' => '会员中心', + 'User Center' => '会员中心', + 'User Center' => '会员中心', +]; diff --git a/application/admin/lang/zh-cn/addon.php b/application/admin/lang/zh-cn/addon.php new file mode 100644 index 0000000..6c999d2 --- /dev/null +++ b/application/admin/lang/zh-cn/addon.php @@ -0,0 +1,118 @@ + 'ID', + 'Title' => '名称', + 'Value' => '配置值', + 'Array key' => '键', + 'Array value' => '值', + 'File' => '文件', + 'Donate' => '打赏作者', + 'Warmtips' => '温馨提示', + 'Pay now' => '立即支付', + 'Local install' => '本地安装', + 'Refresh addon cache' => '刷新插件缓存', + 'Userinfo' => '会员信息', + 'Reload authorization' => '刷新授权', + 'Online store' => '在线商店', + 'Local addon' => '本地插件', + 'Conflict tips' => '此插件中发现和现有系统中部分文件发现冲突!以下文件将会被影响,请备份好相关文件后再继续操作', + 'Login tips' => '此处登录账号为FastAdmin官网账号', + 'Logined tips' => '你好!%s
    当前你已经登录,将同步保存你的购买记录', + 'Pay tips' => '扫码支付后如果仍然无法安装,请不要重复支付,请稍后再重试安装!', + 'Pay successful tips' => '购买成功!请点击继续安装按钮完成安装!', + 'Pay click tips' => '请点击这里在新窗口中进行支付!', + 'Pay new window tips' => '请在新弹出的窗口中进行支付,支付完成后再重新点击安装按钮进行安装!', + 'Upgrade tips' => '确认升级《%s》

    1、请务必做好代码和数据库备份!备份!备份!
    2、升级后如出现冗余数据,请根据需要移除即可!
    3、不建议在生产环境升级,请在本地完成升级测试

    如有重要数据请备份后再操作!', + 'Offline installed tips' => '安装成功!清除浏览器缓存和框架缓存后生效!', + 'Online installed tips' => '安装成功!清除浏览器缓存和框架缓存后生效!', + 'Not login tips' => '你当前未登录FastAdmin,请登录后操作!', + 'Please login and try to install' => '请登录FastAdmin后再进行离线安装!', + 'Not installed tips' => '请安装后再访问插件前台页面!', + 'Not enabled tips' => '插件已经禁用,请启用后再访问插件前台页面!', + 'New version tips' => '发现新版本:%s 点击查看更新日志', + 'Testdata tips' => '你还可以继续导入测试数据!', + 'Import testdata' => '导入测试数据', + 'Skip testdata' => '暂不导入', + 'Store not available tips' => '插件市场暂不可用,是否切换到本地插件?', + 'Switch to the local' => '切换到本地插件', + 'try to reload' => '重新尝试加载', + 'Please disable the add before trying to upgrade' => '请先禁用插件再进行升级', + 'Please disable the add before trying to uninstall' => '请先禁用插件再进行卸载', + 'Login now' => '立即登录', + 'Continue install' => '继续安装', + 'View addon home page' => '查看插件介绍和帮助', + 'View addon index page' => '查看插件前台首页', + 'View addon screenshots' => '点击查看插件截图', + 'Click to toggle status' => '点击切换插件状态', + 'Click to contact developer' => '点击与插件开发者取得联系', + 'Continue installation' => '继续安装', + 'My addons' => '我购买的插件', + 'Index' => '前台', + 'All' => '全部', + 'Uncategoried' => '未归类', + 'Recommend' => '推荐', + 'Hot' => '热门', + 'New' => '新', + 'Paying' => '付费', + 'Free' => '免费', + 'Sale' => '折扣', + 'No image' => '暂无缩略图', + 'Price' => '价格', + 'Downloads' => '下载', + 'Author' => '作者', + 'Identify' => '标识', + 'Homepage' => '主页', + 'Intro' => '介绍', + 'Version' => '版本', + 'New version' => '新版本', + 'Createtime' => '添加时间', + 'Releasetime' => '更新时间', + 'Detail' => '插件详情', + 'Document' => '文档', + 'Demo' => '演示', + 'Feedback' => '反馈BUG', + 'Install' => '安装', + 'Uninstall' => '卸载', + 'Upgrade' => '升级', + 'Setting' => '配置', + 'Disable' => '禁用', + 'Enable' => '启用', + 'Your username or email' => '你的手机号、用户名或邮箱', + 'Your password' => '你的密码', + 'Login FastAdmin' => '登录', + 'Login' => '登录', + 'Logout' => '退出登录', + 'Register' => '注册账号', + 'You\'re not login' => '当前未登录', + 'Continue uninstall' => '继续卸载', + 'Continue operate' => '继续操作', + 'Install successful' => '安装成功', + 'Uninstall successful' => '卸载成功', + 'Operate successful' => '操作成功', + 'Import successful' => '导入测试数据成功!清除浏览器缓存和框架缓存后生效!', + 'Initialize successful' => '初始化成功', + 'Initialize template not found' => '初始化模板未找到', + 'Addon name incorrect' => '插件名称不正确', + 'Addon info file was not found' => '插件配置文件未找到', + 'Addon info file data incorrect' => '插件配置信息不正确', + 'Addon already exists' => '插件已经存在', + 'Addon not exists' => '插件不存在', + 'Addon package download failed' => '插件下载失败', + 'Conflicting file found' => '发现冲突文件', + 'Invalid addon package' => '未验证的插件', + 'No initialize method' => '未找到初始化方法', + 'No permission to write temporary files' => '没有权限写入临时文件', + 'The addon file does not exist' => '插件主启动程序不存在', + 'The configuration file content is incorrect' => '配置文件不完整', + 'Unable to open the zip file' => '无法打开ZIP文件', + 'Unable to extract the file' => '无法解压ZIP文件', + 'Unable to open file \'%s\' for writing' => '文件(%s)没有写入权限', + 'Are you sure you want to unstall %s?' => '确认卸载《%s》?', + 'Are you sure you want to refresh authorization?' => '确认刷新应用插件授权?', + 'Delete all the addon file and cannot be recovered!' => '卸载将会删除所有插件文件且不可找回!!!', + 'Delete all the addon database and cannot be recovered!' => '删除所有插件相关数据表且不可找回!!!', + 'Please backup important data manually before uninstall!' => '如有重要数据请备份后再操作!!!', + 'The following data tables will be deleted' => '以下插件数据表将会被删除', + 'The Addon did not create a data table' => '插件未创建任何数据表', +]; diff --git a/application/admin/lang/zh-cn/ajax.php b/application/admin/lang/zh-cn/ajax.php new file mode 100644 index 0000000..0b67a5f --- /dev/null +++ b/application/admin/lang/zh-cn/ajax.php @@ -0,0 +1,3 @@ + '电子邮箱', + 'Mobile' => '手机号', + 'Group' => '所属组别', + 'Loginfailure' => '登录失败次数', + 'Login time' => '最后登录', + 'The parent group exceeds permission limit' => '父组别超出权限范围', + 'Please input correct username' => '用户名只能由3-30位数字、字母、下划线组合', + 'Username must be 3 to 30 characters' => '用户名只能由3-30位数字、字母、下划线组合', + 'Please input correct password' => '密码长度必须在6-30位之间,不能包含空格', + 'Password must be 6 to 30 characters' => '密码长度必须在6-30位之间,不能包含空格', +]; diff --git a/application/admin/lang/zh-cn/auth/group.php b/application/admin/lang/zh-cn/auth/group.php new file mode 100644 index 0000000..9deec57 --- /dev/null +++ b/application/admin/lang/zh-cn/auth/group.php @@ -0,0 +1,12 @@ + '父组别不能是自身的子组别', + 'The parent group can not found' => '父组别未找到', + 'Group not found' => '组别未找到', + 'Can not change the parent to child' => '父组别不能是它的子组别', + 'Can not change the parent to self' => '父组别不能是它自己', + 'You can not delete group that contain child group and administrators' => '你不能删除含有子组和管理员的组', + 'The parent group exceeds permission limit' => '父组别超出权限范围', + 'The parent group can not be its own child or itself' => '父组别不能是它的子组别及本身', +]; diff --git a/application/admin/lang/zh-cn/auth/rule.php b/application/admin/lang/zh-cn/auth/rule.php new file mode 100644 index 0000000..053d11d --- /dev/null +++ b/application/admin/lang/zh-cn/auth/rule.php @@ -0,0 +1,28 @@ + '显示全部', + 'Condition' => '规则条件', + 'Remark' => '备注', + 'Icon' => '图标', + 'Alert' => '警告', + 'Name' => '规则', + 'Controller/Action' => '控制器名/方法名', + 'Ismenu' => '菜单', + 'Menutype' => '菜单类型', + 'Addtabs' => '选项卡(默认)', + 'Dialog' => '弹窗', + 'Ajax' => 'Ajax请求', + 'Blank' => '链接', + 'Extend' => '扩展属性', + 'Search icon' => '搜索图标', + 'Toggle menu visible' => '点击切换菜单显示', + 'Toggle sub menu' => '点击切换子菜单', + 'Menu tips' => '父级菜单无需匹配控制器和方法,子级菜单请使用控制器名', + 'Node tips' => '控制器/方法名,如果有目录请使用 目录名/控制器名/方法名', + 'Url tips' => '一般情况下留空即可,如果是外部链接或相对链接请输入', + 'The non-menu rule must have parent' => '非菜单规则节点必须有父级', + 'Can not change the parent to child' => '父级不能是它的子级', + 'Can not change the parent to self' => '父级不能是它自己', + 'Name only supports letters, numbers, underscore and slash' => 'URL规则只能是小写字母、数字、下划线和/组成', +]; diff --git a/application/admin/lang/zh-cn/category.php b/application/admin/lang/zh-cn/category.php new file mode 100644 index 0000000..2092274 --- /dev/null +++ b/application/admin/lang/zh-cn/category.php @@ -0,0 +1,18 @@ + 'ID', + 'Pid' => '父ID', + 'Type' => '类型', + 'All' => '全部', + 'Image' => '图片', + 'Keywords' => '关键字', + 'Description' => '描述', + 'Diyname' => '自定义名称', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Weigh' => '权重', + 'Category warmtips' => '温馨提示:栏目类型请前往常规管理->系统配置->字典配置中进行管理', + 'Can not change the parent to child or itself' => '父组别不能是它的子组别或它自己', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/cms/ajax.php b/application/admin/lang/zh-cn/cms/ajax.php new file mode 100644 index 0000000..a50c5ff --- /dev/null +++ b/application/admin/lang/zh-cn/cms/ajax.php @@ -0,0 +1,7 @@ + '请输入检测内容', + 'The content is not legal' => '发现违禁词', + 'The content is legal' => '未发现违禁词', +]; \ No newline at end of file diff --git a/application/admin/lang/zh-cn/cms/archives.php b/application/admin/lang/zh-cn/cms/archives.php new file mode 100644 index 0000000..7a082e4 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/archives.php @@ -0,0 +1,85 @@ + '栏目', + 'Channel_id' => '栏目', + 'Channel_ids' => '副栏目集合', + 'Channel_name' => '栏目名称', + 'Channel list' => '栏目列表', + 'Addon list' => '副表列表', + 'Model' => '模型', + 'Model_id' => '模型ID', + 'User_id' => '发布会员', + 'Admin_id' => '发布管理员', + 'Special_id' => '所属专题', + 'Special_ids' => '所属专题', + 'Title' => '文档标题', + 'Seotitle' => 'SEO标题', + 'Flag' => '标志', + 'Top' => '置顶', + 'Image' => '缩略图', + 'Images' => '组图', + 'Keywords' => '关键字', + 'Description' => '描述', + 'Price' => '价格', + 'Spiders' => '来访', + 'Tags' => 'TAG', + 'Outlink' => '跳转链接', + 'Weigh' => '权重', + 'Views' => '浏览', + 'Comments' => '评论', + 'Likes' => '点赞', + 'Dislikes' => '点踩', + 'Diyname' => '自定义名称', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Publishtime' => '发布时间', + 'Deletetime' => '删除时间', + 'Isguest' => '游客访问', + 'Iscomment' => '允许评论', + 'Recycle bin' => '回收站', + 'Restore' => '还原', + 'Restore all' => '还原全部', + 'Destroy' => '销毁', + 'Destroy all' => '清空回收站', + 'Nothing need restore' => '没有需要还原的数据', + 'Move tips' => '只能将数据移动到相同模型的栏目下,不同模型的数据移动将被忽略', + 'Are you sure you want to truncate?' => '确认清空回收站?', + 'Can not be only digital' => '不能仅为数字', + 'Please input character or digital' => '请输入字母、数字或下划线', + 'Check content is legal' => '检查内容是否有违禁词', + 'Get the keyword and description' => '提取关键字和描述', + 'Get first image from content' => '提取内容第1张图作为缩略图', + 'Please select channel' => '请选择分类', + 'Please select special' => '请选择专题', + 'Please select at least one row' => '请选择需要操作的数据', + 'Set special' => '加入专题', + 'The data already exist' => '已经存在', + 'Banned words' => '违禁词', + 'Status' => '状态', + 'Status normal' => '正常', + 'Status hidden' => '待审核', + 'Status rejected' => '已拒绝', + 'Status pulloff' => '已下线', + 'Set flag' => '修改标志信息', + 'Set to hidden' => '设为待审核', + 'Set to rejected' => '设为已拒绝', + 'Set to pulloff' => '设为已下线', + 'Join to special' => '加入专题', + 'Join to tag' => '加入标签', + 'Copy selected' => '复制选择的行', + 'Are you sure you want to copy %s records?' => '确认复制 %s 条记录?', + 'Publish and continue' => '发布并新增', + 'Array key' => '键', + 'Array value' => '值', + 'Publish' => '发布', + 'Player_Number' => '飞手编号', + 'Name' => '姓名', + 'Mobile' => '手机号', + 'Registration_Time' => '报名时间', + 'Process_Time' => '审核时间', + 'STATE' => '状态', + 'Message' => '短信', + + +]; diff --git a/application/admin/lang/zh-cn/cms/autolink.php b/application/admin/lang/zh-cn/cms/autolink.php new file mode 100644 index 0000000..57bb6a6 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/autolink.php @@ -0,0 +1,14 @@ + '标题', + 'Url' => '链接', + 'Target' => '打开方式', + 'Weigh' => '排序', + 'Views' => '点击次数', + 'Blank' => '新窗口', + 'Self' => '原窗口', + 'Createtime' => '添加时间', + 'Updatetime' => '更新时间', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/cms/block.php b/application/admin/lang/zh-cn/cms/block.php new file mode 100644 index 0000000..24f4c59 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/block.php @@ -0,0 +1,16 @@ + '类型', + 'Name' => '名称', + 'Title' => '标题', + 'Image' => '图片', + 'Url' => '链接', + 'Parsetpl' => '解析模板', + 'Content' => '内容', + 'Createtime' => '添加时间', + 'Updatetime' => '更新时间', + 'Begintime' => '开始时间', + 'Endtime' => '结束时间', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/cms/channel.php b/application/admin/lang/zh-cn/cms/channel.php new file mode 100644 index 0000000..ae4173e --- /dev/null +++ b/application/admin/lang/zh-cn/cms/channel.php @@ -0,0 +1,46 @@ + '类型', + 'Model_id' => '模型', + 'Model_name' => '模型名称', + 'Parent_id' => '父ID', + 'Parent_ids' => '父ID集合', + 'Child_ids' => '子ID集合', + 'Name' => '名称', + 'Image' => '图片', + 'Seotitle' => 'SEO标题', + 'Keywords' => '关键字', + 'Description' => '描述', + 'Diyname' => '自定义URL名称', + 'Outlink' => '跳转链接', + 'Items' => '文档数', + 'Channels' => '栏目数', + 'Authorization' => '授权', + 'Save' => '保存', + 'Weigh' => '权重', + 'Spiders' => '来访', + 'Channeltpl' => '频道页模板', + 'Listtpl' => '列表页模板', + 'Showtpl' => '详情页模板', + 'Pagesize' => '分页大小', + 'Listtype' => '列表数据类型', + 'VIP Limit' => 'VIP限制', + 'Remove' => '移除', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'The data already exist' => '已经存在', + 'You must choose at least one channel' => '必须选择一个栏目', + 'Are you sure you want to remove this item?' => '确认要取消此管理员的所有栏目授权?', + 'Can not be only digital' => '不能仅为数字', + 'Please input character or digital' => '请输入字母、数字或下划线', + 'Toggle all' => '显示全部', + 'Toggle menu visible' => '点击切换菜单显示', + 'Toggle sub menu' => '点击切换子菜单显示', + 'Iscontribute' => '允许投稿', + 'Isnav' => '导航显示', + 'Status' => '状态', + 'Channel' => '频道封面', + 'List' => '列表栏目', + 'Link' => '跳转链接' +]; diff --git a/application/admin/lang/zh-cn/cms/comment.php b/application/admin/lang/zh-cn/cms/comment.php new file mode 100644 index 0000000..0ba3af6 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/comment.php @@ -0,0 +1,21 @@ + 'ID', + 'Type' => '类型', + 'Archives' => '文档', + 'Page' => '单页', + 'Special' => '专题', + 'Aid' => '关联ID', + 'Pid' => '父ID', + 'User_id' => '会员ID', + 'Content' => '内容', + 'Comments' => '评论数', + 'Ip' => 'IP', + 'Useragent' => 'User Agent', + 'Subscribe' => '订阅', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Deletetime' => '删除时间', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/cms/config.php b/application/admin/lang/zh-cn/cms/config.php new file mode 100644 index 0000000..94b8134 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/config.php @@ -0,0 +1,6 @@ + '键', + 'Array value' => '值', +]; diff --git a/application/admin/lang/zh-cn/cms/diydata.php b/application/admin/lang/zh-cn/cms/diydata.php new file mode 100644 index 0000000..614717c --- /dev/null +++ b/application/admin/lang/zh-cn/cms/diydata.php @@ -0,0 +1,15 @@ + '会员ID', + 'Createtime' => '添加时间', + 'Updatetime' => '更新时间', + 'Memo' => '备注', + 'Rejected' => '已拒绝', + 'Array key' => '键名', + 'Array value' => '键值', + 'Status' => '状态', + 'Set to rejected' => '设置为已拒绝', + 'Can not be only digital' => '不能仅为数字', + 'Please input character or digital' => '请输入字母、数字或下划线' +]; diff --git a/application/admin/lang/zh-cn/cms/diyform.php b/application/admin/lang/zh-cn/cms/diyform.php new file mode 100644 index 0000000..4eaec67 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/diyform.php @@ -0,0 +1,31 @@ + '类型', + 'Name' => '名称', + 'Table' => '表名', + 'Seotitle' => 'SEO标题', + 'Keywords' => '关键字', + 'Description' => '描述', + 'Successtips' => '成功提交提示文字', + 'Redirecturl' => '成功后跳转链接', + 'Diyname' => '自定义URL名称', + 'Needlogin' => '发布是否需要登录', + 'Isedit' => '是否允许编辑', + 'Iscaptcha' => '是否开启验证码', + 'Isguest' => '是否允许游客访问', + 'Usermode' => '会员筛选模式', + 'Statusmode' => '状态筛选模式', + 'Formtpl' => '表单模板', + 'Posttpl' => '表单页模板', + 'Listtpl' => '列表页模板', + 'Showtpl' => '详情页模板', + 'Createtime' => '添加时间', + 'Updatetime' => '更新时间', + 'Status' => '状态', + 'Spiders' => '来访', + 'Redirecturl tips' => '为空时将跳转到列表页', + 'Can not be only digital' => '不能仅为数字', + 'Please input character or digital' => '请输入字母、数字或下划线', + 'The data already exist' => '已经存在' +]; diff --git a/application/admin/lang/zh-cn/cms/fields.php b/application/admin/lang/zh-cn/cms/fields.php new file mode 100644 index 0000000..5495c2a --- /dev/null +++ b/application/admin/lang/zh-cn/cms/fields.php @@ -0,0 +1,64 @@ + '模型ID', + 'Name' => '名称', + 'Type' => '类型', + 'Title' => '标题', + 'Seotitle' => 'SEO标题', + 'Content' => '内容正文', + 'Content List' => '数据列表', + 'Filterlist' => '筛选列表', + 'Rule' => '验证规则', + 'Validate Msg' => '错误消息', + 'Validate Ok' => '成功消息', + 'Validate Tip' => '提示消息', + 'Extend' => '扩展信息', + 'Weigh' => '权重', + 'Setting' => '字段设置', + 'Length' => '字段长度', + 'Decimals' => '小数点长度', + 'Minimum' => '最少选择', + 'Maximum' => '最大选择', + 'Defaultvalue' => '默认值', + 'Iscontribute' => '是否可投稿', + 'Ispublish' => '是否后台发布', + 'Isorder' => '是否可排序', + 'Isfilter' => '是否列表筛选', + 'String' => '字符', + 'Text' => '文本', + 'Editor' => '编辑器', + 'Number' => '数字', + 'Date' => '日期', + 'Time' => '时间', + 'Datetime' => '日期时间', + 'Datetimerange' => '日期时间区间', + 'Image' => '图片', + 'Images' => '图片(多)', + 'File' => '文件', + 'Files' => '文件(多)', + 'Select' => '列表', + 'Selects' => '列表(多选)', + 'Switch' => '开关', + 'Checkbox' => '复选', + 'Radio' => '单选', + 'City' => '城市地区', + 'Selectpage' => '关联字段', + 'Selectpages' => '关联字段(多选)', + 'Custom' => '自定义', + 'Array' => '数组', + 'Array key' => '键名', + 'Array value' => '键值', + 'Createtime' => '添加时间', + 'Updatetime' => '更新时间', + 'Please input character or digital' => '请输入字母、数字或下划线', + 'Extend tips' => '扩展属性支持{id}、{name}、{group}、{title}、{value}、{content}、{rule}替换', + 'Please select table' => '关联表', + 'Selectpage table' => '关联表', + 'Selectpage primarykey' => '存储字段', + 'Selectpage field' => '显示字段', + 'Selectpage conditions' => '筛选条件', + 'Field title' => '字段名', + 'Field value' => '字段值', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/cms/modelx.php b/application/admin/lang/zh-cn/cms/modelx.php new file mode 100644 index 0000000..9454bb1 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/modelx.php @@ -0,0 +1,23 @@ + '模型名称', + 'Table' => '表名', + 'Fields' => '字段列表', + 'Channeltpl' => '栏目页模板', + 'Listtpl' => '列表页模板', + 'Showtpl' => '详情页模板', + 'Posttpl' => '会员投稿模板', + 'Please Select' => '请选择', + 'Download' => '下载', + 'News' => '新闻', + 'Product' => '产品', + 'Common' => '常规(无自定义字段,需自行手动添加)', + 'Please input character or digital' => '请输入字母、数字或下划线', + 'Main list' => '主表列表', + 'Addon list' => '副表列表', + 'Duplicate' => '复制模型', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Setting' => '配置' +]; diff --git a/application/admin/lang/zh-cn/cms/order.php b/application/admin/lang/zh-cn/cms/order.php new file mode 100644 index 0000000..874d877 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/order.php @@ -0,0 +1,25 @@ + '订单ID', + 'User_id' => '会员ID', + 'Archives_id' => '文档ID', + 'Archives_title' => '文档标题', + 'Title' => '订单标题', + 'Amount' => '订单金额', + 'Payamount' => '支付金额', + 'Paytype' => '支付类型', + 'Paytime' => '支付时间', + 'Ip' => 'IP地址', + 'Useragent' => 'UserAgent', + 'Memo' => '备注', + 'Createtime' => '添加时间', + 'Updatetime' => '更新时间', + 'Status' => '状态', + 'Paid' => '已支付', + 'Expired' => '已过期', + 'Created' => '未支付', + 'Set to created' => '设为未支付', + 'Set to paid' => '设为已支付', + 'Set to expired' => '设为已过期', +]; diff --git a/application/admin/lang/zh-cn/cms/page.php b/application/admin/lang/zh-cn/cms/page.php new file mode 100644 index 0000000..d41c416 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/page.php @@ -0,0 +1,33 @@ + 'ID', + 'Category_id' => '分类ID', + 'Type' => '类型', + 'Title' => '标题', + 'Seotitle' => 'SEO标题', + 'Keywords' => '关键字', + 'Description' => '描述', + 'Flag' => '标志', + 'Image' => '图片', + 'Content' => '内容', + 'Icon' => '图标', + 'Views' => '浏览', + 'Comments' => '评论', + 'Spiders' => '来访', + 'Diyname' => '自定义URL名称', + 'Showtpl' => '视图模板', + 'Iscomment' => '允许评论', + 'Parsetpl' => '解析模板', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Deletetime' => '删除时间', + 'Check content is legal' => '检查内容是否有违禁词', + 'Get the keyword and description' => '提取关键字和描述', + 'The data already exist' => '已经存在', + 'Can not be only digital' => '不能仅为数字', + 'Please input character or digital' => '请输入字母、数字或下划线', + 'Weigh' => '权重', + 'Select' => '选择', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/cms/search_log.php b/application/admin/lang/zh-cn/cms/search_log.php new file mode 100644 index 0000000..8a19f69 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/search_log.php @@ -0,0 +1,8 @@ + '关键字', + 'Nums' => '搜索次数', + 'Createtime' => '搜索时间', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/cms/special.php b/application/admin/lang/zh-cn/cms/special.php new file mode 100644 index 0000000..72fdfc4 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/special.php @@ -0,0 +1,31 @@ + '标题', + 'Tag_ids' => '标签筛选集合', + 'Archives' => '文档集合', + 'Andor' => '标签筛选条件', + 'And' => '与', + 'Or' => '或', + 'Flag' => '标志', + 'Label' => '标题标签', + 'Image' => '图片', + 'Banner' => 'Banner图片', + 'Diyname' => '自定义URL名称', + 'Seotitle' => 'SEO标题', + 'Keywords' => '关键字', + 'Description' => '描述', + 'Intro' => '专题介绍', + 'Views' => '浏览', + 'Comments' => '评论', + 'Spiders' => '来访', + 'Iscomment' => '允许评论', + 'Createtime' => '添加时间', + 'Updatetime' => '更新时间', + 'Deletetime' => '删除时间', + 'Template' => '专题模板', + 'The data already exist' => '已经存在', + 'Can not be only digital' => '不能仅为数字', + 'Please input character or digital' => '请输入字母、数字或下划线', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/cms/spider_log.php b/application/admin/lang/zh-cn/cms/spider_log.php new file mode 100644 index 0000000..1f2fd25 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/spider_log.php @@ -0,0 +1,20 @@ + '类型', + 'Aid' => '关联ID', + 'Name' => '名称', + 'Url' => '来访页面', + 'Nums' => '来访次数', + 'Lastdata' => '最近5次来访时间', + 'Firsttime' => '首次来访时间', + 'Lasttime' => '最新来访时间', + 'Index' => '首页', + 'Archives' => '文档', + 'Channel' => '栏目', + 'Page' => '单页', + 'Diyform' => '自定义表单', + 'Special' => '专题', + 'Tag' => '标签', + 'User' => '会员主页', +]; diff --git a/application/admin/lang/zh-cn/cms/tag.php b/application/admin/lang/zh-cn/cms/tag.php new file mode 100644 index 0000000..4f6bff8 --- /dev/null +++ b/application/admin/lang/zh-cn/cms/tag.php @@ -0,0 +1,12 @@ + '标签名称', + 'Archives' => '文档ID集合', + 'Nums' => '文档数', + 'Seotitle' => 'SEO标题', + 'Keywords' => '关键字', + 'Description' => '描述', + 'Spiders' => '来访', + 'Autolink' => '自动内链', +]; diff --git a/application/admin/lang/zh-cn/command.php b/application/admin/lang/zh-cn/command.php new file mode 100644 index 0000000..b010250 --- /dev/null +++ b/application/admin/lang/zh-cn/command.php @@ -0,0 +1,16 @@ + 'ID', + 'Type' => '类型', + 'Params' => '参数', + 'Command' => '命令', + 'Content' => '返回结果', + 'Executetime' => '执行时间', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Execute again' => '再次执行', + 'Successed' => '成功', + 'Failured' => '失败', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/config.php b/application/admin/lang/zh-cn/config.php new file mode 100644 index 0000000..e9757af --- /dev/null +++ b/application/admin/lang/zh-cn/config.php @@ -0,0 +1,9 @@ + '变量名称', + 'intro' => '描述', + 'group' => '分组', + 'type' => '类型', + 'value' => '变量值' +]; diff --git a/application/admin/lang/zh-cn/countryflag.php b/application/admin/lang/zh-cn/countryflag.php new file mode 100644 index 0000000..9d08189 --- /dev/null +++ b/application/admin/lang/zh-cn/countryflag.php @@ -0,0 +1,6 @@ + '国籍', + 'National_flag' => '国旗' +]; diff --git a/application/admin/lang/zh-cn/dashboard.php b/application/admin/lang/zh-cn/dashboard.php new file mode 100644 index 0000000..66ce16e --- /dev/null +++ b/application/admin/lang/zh-cn/dashboard.php @@ -0,0 +1,50 @@ + '自定义', + 'Pid' => '父ID', + 'Type' => '栏目类型', + 'Image' => '图片', + 'Total user' => '总会员数', + 'Total addon' => '总插件数', + 'Total category' => '总分类数', + 'Total attachment' => '总附件数', + 'Total admin' => '总管理员数', + 'Today user signup' => '今日注册', + 'Today user login' => '今日登录', + 'Today order' => '今日订单', + 'Unsettle order' => '未处理订单', + 'Three dnu' => '三日新增', + 'Seven dnu' => '七日新增', + 'Seven dau' => '七日活跃', + 'Thirty dau' => '月活跃', + 'Custom zone' => '这里是你的自定义数据', + 'Register user' => '注册用户数', + 'Real time' => '实时', + 'Category count' => '分类统计', + 'Working addon count' => '运行中的插件', + 'Category count tips' => '当前分类总记录数', + 'Working addon count tips' => '当前运行中的插件数', + 'Database count' => '数据库统计', + 'Database table nums' => '数据表数量', + 'Database size' => '占用空间', + 'Attachment count' => '附件统计', + 'Attachment nums' => '附件数量', + 'Attachment size' => '附件大小', + 'Attachment count tips' => '当前上传的附件数量', + 'Picture count' => '图片统计', + 'Picture nums' => '图片数量', + 'Picture size' => '图片大小', + 'Server info' => '服务器信息', + 'PHP version' => 'PHP版本', + 'Sapi name' => '运行方式', + 'Debug mode' => '调试模式', + 'Software' => '环境信息', + 'Upload mode' => '上传模式', + 'Upload url' => '上传URL', + 'Upload cdn url' => '上传CDN', + 'Cdn url' => '静态资源CDN', + 'Timezone' => '时区', + 'Language' => '语言', + 'View more' => '查看更多', +]; diff --git a/application/admin/lang/zh-cn/docs.php b/application/admin/lang/zh-cn/docs.php new file mode 100644 index 0000000..a4c5793 --- /dev/null +++ b/application/admin/lang/zh-cn/docs.php @@ -0,0 +1,19 @@ + '名称', + 'title' => '标题', + 'keywords' => '关键字', + 'description' => '描述', + 'type' => '分类', + 'sort' => '排序', + 'relative' => '文件位置', + 'contributeurl' => '贡献URL', + 'source' => 'MD源内容', + 'preview' => '预览', + 'order' => '排序', + 'isnew' => '新标识', + 'markdown file' => 'MD文件名称', + 'refresh docs' => '刷新缓存', + 'export html' => '导出HTML', +]; diff --git a/application/admin/lang/zh-cn/fastim/config.php b/application/admin/lang/zh-cn/fastim/config.php new file mode 100644 index 0000000..d7f2b2d --- /dev/null +++ b/application/admin/lang/zh-cn/fastim/config.php @@ -0,0 +1,36 @@ + '通用配置', + 'Privacy' => '隐私配置', + 'CSR' => '客服配置', + 'UniPush Switch' => '启用 UniPush', + 'Message Push' => '消息推送配置', + 'Package Name Android' => 'Android包名', + 'Package Name IOS' => 'IOS包名', + 'Im name' => '应用名称', + 'Send Message button' => '发送消息按键', + 'invite Group Member No Confirm' => '免确认入群人数', + 'Press ESC to close the window' => '按ESC键关闭窗口', + 'New message window jitter' => '新消息窗口抖动', + 'New message playing tone' => '新消息播放提示音', + 'New message push notice' => '新消息推送通知', + 'Window display push notification' => '窗口显示时推送通知', + 'H5 Url' => 'H5端地址', + 'Temporary session' => '临时会话', + 'Display input status' => '展示输入状态', + 'Post login status' => '登录后状态', + 'Auto reply when busy' => '忙碌时的自动回复', + 'Add me as a friend' => '加我为好友时', + 'Mobile privacy' => '手机号隐私性', + 'Email privacy' => '邮箱隐私性', + 'Company privacy' => '公司隐私性', + 'Age privacy' => '年龄和生日隐私性', + 'Occupation privacy' => '职业隐私性', + 'Trajectory Save Cycle' => '轨迹保存方案', + 'Open CSR' => '开启客服功能', + 'new User Tip' => '新用户一句话欢迎文案', + 'Welcome New User Msg' => '新用户欢迎消息(默认)', + 'Distribution Group Select' => '客服分配分组选择', + 'Distribution Type' => '客服分配方式', + 'No CSR Tip' => '无客服在线提示', +]; \ No newline at end of file diff --git a/application/admin/lang/zh-cn/fastim/csr_group.php b/application/admin/lang/zh-cn/fastim/csr_group.php new file mode 100644 index 0000000..e21ca1c --- /dev/null +++ b/application/admin/lang/zh-cn/fastim/csr_group.php @@ -0,0 +1,14 @@ + 'ID', + 'Name' => '分组名称', + 'CSR number' => '客服数', + 'Note' => '备注', + 'Weigh' => '权重', + 'Status' => '状态', + 'Status 0' => '隐藏', + 'Status 1' => '正常', + 'Createtime' => '创建时间', + 'Deletetime' => '删除时间' +]; diff --git a/application/admin/lang/zh-cn/fastim/fast_reply.php b/application/admin/lang/zh-cn/fastim/fast_reply.php new file mode 100644 index 0000000..82cf3f7 --- /dev/null +++ b/application/admin/lang/zh-cn/fastim/fast_reply.php @@ -0,0 +1,16 @@ + 'ID', + 'User_id' => '所属客服', + 'Title' => '标题', + 'Content' => '回复内容', + 'Status' => '状态', + 'Status 0' => '关闭', + 'Status 1' => '启用', + 'Createtime' => '创建时间', + 'Deletetime' => '删除时间', + 'user.nickname' => '昵称', + 'Set to status 0' => '设置为关闭', + 'Set to status 1' => '设置为开启', +]; diff --git a/application/admin/lang/zh-cn/fastim/groupchat.php b/application/admin/lang/zh-cn/fastim/groupchat.php new file mode 100644 index 0000000..97c4465 --- /dev/null +++ b/application/admin/lang/zh-cn/fastim/groupchat.php @@ -0,0 +1,25 @@ + 'ID', + 'Leader' => '群主', + 'Avatar' => '头像', + 'Nickname' => '群名称', + 'Bio' => '群介绍', + 'Max_user_count' => '最大成员数量', + 'Add_mode' => '加群模式', + 'Add_mode 0' => '需管理员审核', + 'Add_mode 1' => '无需审核', + 'Invite_join_group' => '邀请模式', + 'Invite_join_group 0' => '成员邀请好友无需审核', + 'Invite_join_group 1' => '需要审核', + 'Speak' => '禁止成员发言', + 'Speak 0' => '允许发言', + 'Speak 1' => '禁止发言', + 'History_message' => '历史消息', + 'History_message 0' => '不允许新入群用户查看', + 'History_message 1' => '允许新入群用户查看', + 'Createtime' => '创建时间', + 'Deletetime' => '删除时间', + 'Fastimuser.nickname' => '昵称' +]; diff --git a/application/admin/lang/zh-cn/fastim/kbs.php b/application/admin/lang/zh-cn/fastim/kbs.php new file mode 100644 index 0000000..b0c73e3 --- /dev/null +++ b/application/admin/lang/zh-cn/fastim/kbs.php @@ -0,0 +1,20 @@ + 'ID', + 'Title' => '标题', + 'Keyword' => '关键词', + 'Will be used to match user statements' => '将用于匹配用户发言', + 'Content' => '内容', + 'Views' => '浏览量', + 'Likes' => '有帮助数', + 'Dislikes' => '无帮助数', + 'Note' => '备注', + 'Status' => '状态', + 'Status 0' => '隐藏', + 'Status 1' => '正常', + 'Weigh' => '权重', + 'Updatetime' => '更新时间', + 'Createtime' => '创建时间', + 'Deletetime' => '删除时间' +]; diff --git a/application/admin/lang/zh-cn/fastim/records.php b/application/admin/lang/zh-cn/fastim/records.php new file mode 100644 index 0000000..fee6153 --- /dev/null +++ b/application/admin/lang/zh-cn/fastim/records.php @@ -0,0 +1,36 @@ + 'ID', + 'Type' => '消息类型', + 'Type default' => '普通', + 'Type image' => '图片', + 'Type audio' => '音频', + 'Type video' => '视频', + 'Type file' => '文件', + 'Type link' => '链接', + 'Type system' => '系统', + 'Type group_apply' => '申请入群', + 'Type group_notice' => '群通知', + 'Type group_invitation' => '邀请入群', + 'Type friend_apply' => '好友申请', + 'Type kbs_list' => '知识库消息', + 'Type group_chat_notice' => '群公告', + 'Type voice' => '语音', + 'Group_id' => '群聊ID', + 'Group' => '群聊', + 'Session_id' => '会话ID', + 'Sender_id' => '发送人', + 'Recipient_id' => '接受人', + 'Message' => '消息内容(原始)', + 'FormatMessage' => '消息内容', + 'Read_number' => '群消息阅读数', + 'Status' => '状态', + 'Status 0' => '未读', + 'Status 1' => '已读', + 'Status 2' => '发送中', + 'Status 3' => '隐藏状态', + 'Status 4' => '失败', + 'Createtime' => '创建时间', + 'Deleteuser' => '删除消息用户' +]; diff --git a/application/admin/lang/zh-cn/fastim/report.php b/application/admin/lang/zh-cn/fastim/report.php new file mode 100644 index 0000000..d2a8e78 --- /dev/null +++ b/application/admin/lang/zh-cn/fastim/report.php @@ -0,0 +1,22 @@ + 'ID', + 'Type' => '类型', + 'Type user' => '举报用户', + 'Type group' => '举报群聊', + 'Type feedback' => '问题反馈', + 'Session_id' => '会话ID', + 'Report_id' => '举报人', + 'User_id' => '被举报对象', + 'Describe' => '举报详情', + 'Mobile' => '联系方式', + 'Reportimage' => '举报证据', + 'Status' => '状态', + 'Status 0' => '未处理', + 'Status 1' => '已处理', + 'Createtime' => '创建时间', + 'User.nickname' => '昵称', + 'Set to status 0' => '设置为未处理', + 'Set to status 1' => '设置为已处理', +]; diff --git a/application/admin/lang/zh-cn/fastim/session.php b/application/admin/lang/zh-cn/fastim/session.php new file mode 100644 index 0000000..f82f3dd --- /dev/null +++ b/application/admin/lang/zh-cn/fastim/session.php @@ -0,0 +1,18 @@ + 'ID', + 'Type' => '类型', + 'Type single' => '单聊', + 'Type group' => '群聊', + 'Type service' => '服务号', + 'User_one' => '用户', + 'User_two' => '会话对象', + 'Service' => '服务号会话对象', + 'Chat_id' => '群聊或服务号id', + 'Createtime' => '创建时间', + 'User.nickname' => '昵称', + 'Service 1' => '新朋友', + 'Service 2' => '群通知', + 'Service 3' => '智能客服小蜜', +]; diff --git a/application/admin/lang/zh-cn/fastim/user.php b/application/admin/lang/zh-cn/fastim/user.php new file mode 100644 index 0000000..17d1b79 --- /dev/null +++ b/application/admin/lang/zh-cn/fastim/user.php @@ -0,0 +1,41 @@ + 'ID', + 'Type' => '用户类型', + 'Type tourist' => '游客', + 'Type user' => '会员', + 'Type csr' => '客服', + 'Group_id' => '客服组', + 'Welcome Msg' => '新用户欢迎消息', + 'Welcome Msg Tip' => '客服接入新用户时自动发送的欢迎消息', + 'User_id' => '绑定用户', + 'Admin_id' => '绑定管理员', + 'Avatar' => '头像', + 'Nickname' => '昵称', + 'Referrer' => '用户来路', + 'Mobile' => '手机', + 'FaUser.Mobile' => '绑定用户手机', + 'Email' => '邮箱', + 'Gender' => '性别', + 'Gender secrecy' => '保密', + 'Gender male' => '男', + 'Gender female' => '女', + 'Age' => '年龄', + 'Birthday' => '生日', + 'Bio' => '格言', + 'Occupation' => '职业', + 'Company' => '公司', + 'Status' => '状态', + 'Status 0' => '离线', + 'Status 1' => '在线', + 'Status 2' => '忙碌', + 'Status 3' => '隐身', + 'Note' => '客服备注', + 'Token' => 'Token', + 'Loginip' => '登录ip', + 'Wechat_openid' => '微信openid', + 'Createtime' => '创建时间', + 'FaUser.nickname' => '绑定用户', + 'Admin.nickname' => '绑定管理员' +]; diff --git a/application/admin/lang/zh-cn/general/attachment.php b/application/admin/lang/zh-cn/general/attachment.php new file mode 100644 index 0000000..1dc6cae --- /dev/null +++ b/application/admin/lang/zh-cn/general/attachment.php @@ -0,0 +1,41 @@ + 'ID', + 'Admin_id' => '管理员ID', + 'User_id' => '会员ID', + 'Url' => '物理路径', + 'Imagewidth' => '宽度', + 'Imageheight' => '高度', + 'Imagetype' => '图片类型', + 'Imageframes' => '图片帧数', + 'Preview' => '预览', + 'Filename' => '文件名', + 'Filesize' => '文件大小', + 'Mimetype' => 'Mime类型', + 'Image' => '图片', + 'Audio' => '音频', + 'Video' => '视频', + 'Text' => '文档', + 'Application' => '应用', + 'Zip' => '压缩包', + 'Extparam' => '透传数据', + 'Createtime' => '创建日期', + 'Uploadtime' => '上传时间', + 'Storage' => '存储引擎', + 'Category1' => '分类一', + 'Category2' => '分类二', + 'Custom' => '自定义', + 'Unclassed' => '未归类', + 'Category' => '类别', + 'Classify' => '归类', + 'Filter Type' => '类型筛选', + 'Upload to third' => '上传到第三方', + 'Upload to local' => '上传到本地', + 'Upload to third by chunk' => '上传到第三方(分片模式)', + 'Upload to local by chunk' => '上传到本地(分片模式)', + 'Please enter a new name' => '请输入新的类别名称', + 'Please select category' => '请选择一个类别', + 'Category not found' => '指定的类别未找到', + 'Upload from editor' => '从编辑器上传' +]; diff --git a/application/admin/lang/zh-cn/general/config.php b/application/admin/lang/zh-cn/general/config.php new file mode 100644 index 0000000..0da3f0e --- /dev/null +++ b/application/admin/lang/zh-cn/general/config.php @@ -0,0 +1,83 @@ + '变量名', + 'Tip' => '提示信息', + 'Group' => '分组', + 'Type' => '类型', + 'Title' => '变量标题', + 'Value' => '变量值', + 'Basic' => '基础配置', + 'Email' => '邮件配置', + 'Attachment' => '附件配置', + 'Dictionary' => '字典配置', + 'User' => '会员配置', + 'Example' => '示例分组', + 'Extend' => '扩展属性', + 'String' => '字符', + 'Password' => '密码', + 'Text' => '文本', + 'Editor' => '编辑器', + 'Number' => '数字', + 'Date' => '日期', + 'Time' => '时间', + 'Datetime' => '日期时间', + 'Datetimerange' => '日期时间区间', + 'Image' => '图片', + 'Images' => '图片(多)', + 'File' => '文件', + 'Files' => '文件(多)', + 'Select' => '列表', + 'Selects' => '列表(多选)', + 'Switch' => '开关', + 'Checkbox' => '复选', + 'Radio' => '单选', + 'Array' => '数组', + 'Array key' => '键名', + 'Array value' => '键值', + 'City' => '城市地区', + 'Selectpage' => '关联表', + 'Selectpages' => '关联表(多选)', + 'Custom' => '自定义', + 'Please select table' => '关联表', + 'Selectpage table' => '关联表', + 'Selectpage primarykey' => '存储字段', + 'Selectpage field' => '显示字段', + 'Selectpage conditions' => '筛选条件', + 'Field title' => '字段名', + 'Field value' => '字段值', + 'Content' => '数据列表', + 'Rule' => '校验规则', + 'Visible condition' => '可见条件', + 'Site name' => '站点名称', + 'Beian' => '备案号', + 'Cdn url' => 'CDN地址', + 'Version' => '版本号', + 'Timezone' => '时区', + 'Forbidden ip' => '禁止IP', + 'Languages' => '语言', + 'Fixed page' => '后台固定页', + 'Category type' => '分类类型', + 'Config group' => '配置分组', + 'Attachment category' => '附件类别', + 'Category1' => '分类一', + 'Category2' => '分类二', + 'Rule tips' => '校验规则使用请参考Nice-validator文档', + 'Extend tips' => '扩展属性支持{id}、{name}、{group}、{title}、{value}、{content}、{rule}替换', + 'Mail type' => '邮件发送方式', + 'Mail smtp host' => 'SMTP服务器', + 'Mail smtp port' => 'SMTP端口', + 'Mail smtp user' => 'SMTP用户名', + 'Mail smtp password' => 'SMTP密码', + 'Mail vertify type' => 'SMTP验证方式', + 'Mail from' => '发件人邮箱', + 'Site name incorrect' => '网站名称错误', + 'Name already exist' => '变量名称已经存在', + 'Add new config' => '点击添加新的配置', + 'Send a test message' => '发送测试邮件', + 'Only work at development environment' => '只允许在开发环境开操作', + 'This is a test mail content' => '这是一封来自%s的校验邮件,用于校验邮件配置是否正常!', + 'This is a test mail' => '这是一封来自%s的邮件', + 'Please input your email' => '请输入测试接收者邮箱', + 'Please input correct email' => '请输入正确的邮箱地址', +]; diff --git a/application/admin/lang/zh-cn/general/profile.php b/application/admin/lang/zh-cn/general/profile.php new file mode 100644 index 0000000..fd69956 --- /dev/null +++ b/application/admin/lang/zh-cn/general/profile.php @@ -0,0 +1,14 @@ + '链接', + 'Userame' => '用户名', + 'Createtime' => '操作时间', + 'Click to edit' => '点击编辑', + 'Admin log' => '操作日志', + 'Leave password blank if dont want to change' => '不修改密码请留空', + 'Please input correct email' => '请输入正确的Email地址', + 'Please input correct password' => '密码长度必须在6-30位之间,不能包含空格', + 'Password must be 6 to 30 characters' => '密码长度必须在6-30位之间,不能包含空格', + 'Email already exists' => '邮箱已经存在', +]; diff --git a/application/admin/lang/zh-cn/index.php b/application/admin/lang/zh-cn/index.php new file mode 100644 index 0000000..316de2a --- /dev/null +++ b/application/admin/lang/zh-cn/index.php @@ -0,0 +1,64 @@ + '标题', + 'Search menu' => '搜索菜单', + 'Layout Options' => '布局设定', + 'Fixed Layout' => '固定布局', + 'You can\'t use fixed and boxed layouts together' => '盒子模型和固定布局不能同时启作用', + 'Boxed Layout' => '盒子布局', + 'Activate the boxed layout' => '盒子布局最大宽度将被限定为1250px', + 'Toggle Sidebar' => '收起菜单栏', + 'Toggle the left sidebar\'s state (open or collapse)' => '切换菜单栏的展开或收起', + 'Sidebar Expand on Hover' => '菜单栏自动展开', + 'Let the sidebar mini expand on hover' => '鼠标移到菜单栏自动展开', + 'Toggle Right Sidebar Slide' => '切换右侧操作栏', + 'Toggle between slide over content and push content effects' => '切换右侧操作栏覆盖或独占', + 'Toggle Right Sidebar Skin' => '切换右侧操作栏背景', + 'Toggle between dark and light skins for the right sidebar' => '将右侧操作栏背景亮色或深色切换', + 'Multiple nav' => '多级菜单导航', + 'Toggle the top menu state (multiple or single)' => '切换顶部菜单为多级菜单导航模式', + 'Multiple tab' => '多选项卡', + 'Always show multiple tab when multiple nav is set' => '当配置为多级菜单导航时是否启用多选项卡', + 'Show sub menu' => '显示菜单栏子菜单', + 'Always show sub menu' => '菜单栏子菜单将始终显示', + 'Disable top menu badge' => '禁用顶部彩色小角标', + 'Disable top menu badge without left menu' => '左边菜单栏的彩色小角标不受影响', + 'Skins' => '皮肤', + 'You\'ve logged in, do not login again' => '你已经登录,无需重复登录', + 'Username or password can not be empty' => '用户名密码不能为空', + 'Username or password is incorrect' => '用户名或密码不正确', + 'Username is incorrect' => '用户名不正确', + 'Password is incorrect' => '密码不正确', + 'Admin is forbidden' => '管理员已经被禁止登录', + 'Please try again after 1 day' => '请于1天后再尝试登录', + 'Login successful' => '登录成功!', + 'Logout successful' => '退出成功!', + 'Verification code is incorrect' => '验证码不正确', + 'Wipe cache completed' => '清除缓存成功', + 'Wipe cache failed' => '清除缓存失败', + 'Wipe cache' => '清除缓存', + 'Wipe all cache' => '一键清除缓存', + 'Wipe content cache' => '清除内容缓存', + 'Wipe template cache' => '清除模板缓存', + 'Wipe addons cache' => '清除插件缓存', + 'Wipe browser cache' => '清除浏览器缓存', + 'Wipe browser cache tips' => '清除浏览器端静态JS、CSS、图片等资源', + 'Check for updates' => '检测更新', + 'Discover new version' => '发现新版本', + 'Go to download' => '去下载更新', + 'Currently is the latest version' => '当前已经是最新版本', + 'Ignore this version' => '忽略此次更新', + 'Do not remind again' => '不再提示', + 'Your current version' => '你的版本是', + 'New version' => '新版本', + 'Release notes' => '更新说明', + 'Latest news' => '最新消息', + 'View more' => '查看更多', + 'Links' => '相关链接', + 'Docs' => '官方文档', + 'Forum' => '交流社区', + 'QQ qun' => 'QQ交流群', + 'Captcha' => '验证码', + 'Security tips' => ' 安全提示:为了你的后台安全,请勿将后台管理入口设置为admin或admin.php', +]; diff --git a/application/admin/lang/zh-cn/renzheng.php b/application/admin/lang/zh-cn/renzheng.php new file mode 100644 index 0000000..ca5d8ed --- /dev/null +++ b/application/admin/lang/zh-cn/renzheng.php @@ -0,0 +1,5 @@ + '用户昵称', + 'Starttime' => '开始时间', + 'Endtime' => '结束时间', + 'Vip_number' => '会员卡号', + 'Status' => '状态 0停用 1正常' +]; diff --git a/application/admin/lang/zh-cn/shopro/vip/paylog.php b/application/admin/lang/zh-cn/shopro/vip/paylog.php new file mode 100644 index 0000000..7a113d7 --- /dev/null +++ b/application/admin/lang/zh-cn/shopro/vip/paylog.php @@ -0,0 +1,9 @@ + '用户昵称', + 'Paytime' => '支付时间', + 'Vip_fee' => '卡费金额', + 'Range' => '有效期', + 'Wechat_ordernum' => '微信订单号' +]; diff --git a/application/admin/lang/zh-cn/third.php b/application/admin/lang/zh-cn/third.php new file mode 100644 index 0000000..a3aa09c --- /dev/null +++ b/application/admin/lang/zh-cn/third.php @@ -0,0 +1,17 @@ + 'ID', + 'User_id' => '会员ID', + 'Platform' => '平台', + 'Apptype' => '类型', + 'Unionid' => 'UnionID', + 'Openid' => 'OpenID', + 'Openname' => '第三方会员昵称', + 'Access_token' => 'AccessToken', + 'Expires_in' => '有效期', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Logintime' => '登录时间', + 'Expiretime' => '过期时间' +]; diff --git a/application/admin/lang/zh-cn/user/club.php b/application/admin/lang/zh-cn/user/club.php new file mode 100644 index 0000000..cc2d2e0 --- /dev/null +++ b/application/admin/lang/zh-cn/user/club.php @@ -0,0 +1,7 @@ + '名称', + 'Competitors' => '参赛主体', + 'Competition_slogan' => '口号' +]; diff --git a/application/admin/lang/zh-cn/user/group.php b/application/admin/lang/zh-cn/user/group.php new file mode 100644 index 0000000..af5284e --- /dev/null +++ b/application/admin/lang/zh-cn/user/group.php @@ -0,0 +1,10 @@ + '组名', + 'Rules' => '权限节点', + 'Change password' => '修改密码', + 'Createtime' => '添加时间', + 'Updatetime' => '更新时间', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/user/player.php b/application/admin/lang/zh-cn/user/player.php new file mode 100644 index 0000000..658e101 --- /dev/null +++ b/application/admin/lang/zh-cn/user/player.php @@ -0,0 +1,42 @@ + 'ID', + 'Group_id' => '组别ID', + 'Member_number' => '飞手ID', + 'Username' => '用户名', + 'Realname' => '飞手姓名', + 'Nickname' => '昵称', + 'Password' => '密码', + 'Avatar' => '头像', + 'Salt' => '密码盐', + 'Email' => '电子邮箱', + 'Mobile' => '手机号', + 'Avatar' => '头像', + 'Level' => '等级', + 'Gender' => '性别', + 'Male' => '男', + 'FeMale' => '女', + 'Birthday' => '生日', + 'Bio' => '格言', + 'Score' => '积分', + 'Successions' => '连续登录天数', + 'Maxsuccessions' => '最大连续登录天数', + 'Prevtime' => '上次登录时间', + 'Logintime' => '登录时间', + 'Loginip' => '登录IP', + 'Loginfailure' => '失败次数', + 'Joinip' => '加入IP', + 'Jointime' => '加入时间', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Token' => 'Token', + 'Status' => '状态', + 'Leave password blank if dont want to change' => '不修改密码请留空', +]; diff --git a/application/admin/lang/zh-cn/user/rule.php b/application/admin/lang/zh-cn/user/rule.php new file mode 100644 index 0000000..0d01117 --- /dev/null +++ b/application/admin/lang/zh-cn/user/rule.php @@ -0,0 +1,16 @@ + '父ID', + 'Name' => '规则', + 'Title' => '标题', + 'Remark' => '备注', + 'Ismenu' => '是否菜单', + 'Change password' => '修改密码', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Menu tips' => '规则任意,请不可重复,仅做层级显示,无需匹配控制器和方法', + 'Node tips' => '模块/控制器/方法名', + 'Weigh' => '权重', + 'Status' => '状态' +]; diff --git a/application/admin/lang/zh-cn/user/user.php b/application/admin/lang/zh-cn/user/user.php new file mode 100644 index 0000000..9bbd611 --- /dev/null +++ b/application/admin/lang/zh-cn/user/user.php @@ -0,0 +1,34 @@ + 'ID', + 'Group_id' => '组别ID', + 'Member_number' => '飞手ID', + 'Username' => '用户名', + 'Nickname' => '飞手姓名', + 'Password' => '密码', + 'Salt' => '密码盐', + 'Email' => '电子邮箱', + 'Mobile' => '手机号', + 'Avatar' => '头像', + 'Level' => '等级', + 'Gender' => '性别', + 'Male' => '男', + 'FeMale' => '女', + 'Birthday' => '生日', + 'Bio' => '格言', + 'Score' => '积分', + 'Successions' => '连续登录天数', + 'Maxsuccessions' => '最大连续登录天数', + 'Prevtime' => '上次登录时间', + 'Logintime' => '登录时间', + 'Loginip' => '登录IP', + 'Loginfailure' => '失败次数', + 'Joinip' => '加入IP', + 'Jointime' => '加入时间', + 'Createtime' => '创建时间', + 'Updatetime' => '更新时间', + 'Token' => 'Token', + 'Status' => '状态', + 'Leave password blank if dont want to change' => '不修改密码请留空', +]; diff --git a/application/admin/library/Auth.php b/application/admin/library/Auth.php new file mode 100644 index 0000000..be2b60a --- /dev/null +++ b/application/admin/library/Auth.php @@ -0,0 +1,530 @@ + $username]); + if (!$admin) { + $this->setError('Username is incorrect'); + return false; + } + if ($admin['status'] == 'hidden') { + $this->setError('Admin is forbidden'); + return false; + } + if (Config::get('fastadmin.login_failure_retry') && $admin->loginfailure >= 10 && time() - $admin->updatetime < 86400) { + $this->setError('Please try again after 1 day'); + return false; + } + if ($admin->password != md5(md5($password) . $admin->salt)) { + $admin->loginfailure++; + $admin->save(); + $this->setError('Password is incorrect'); + return false; + } + $admin->loginfailure = 0; + $admin->logintime = time(); + $admin->loginip = request()->ip(); + $admin->token = Random::uuid(); + $admin->save(); + Session::set("admin", $admin->toArray()); + $this->keeplogin($keeptime); + return true; + } + + /** + * 退出登录 + */ + public function logout() + { + $admin = Admin::get(intval($this->id)); + if ($admin) { + $admin->token = ''; + $admin->save(); + } + $this->logined = false; //重置登录状态 + Session::delete("admin"); + Cookie::delete("keeplogin"); + return true; + } + + /** + * 自动登录 + * @return boolean + */ + public function autologin() + { + $keeplogin = Cookie::get('keeplogin'); + if (!$keeplogin) { + return false; + } + list($id, $keeptime, $expiretime, $key) = explode('|', $keeplogin); + if ($id && $keeptime && $expiretime && $key && $expiretime > time()) { + $admin = Admin::get($id); + if (!$admin || !$admin->token) { + return false; + } + //token有变更 + if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token . config('token.key'))) { + return false; + } + $ip = request()->ip(); + //IP有变动 + if ($admin->loginip != $ip) { + return false; + } + Session::set("admin", $admin->toArray()); + //刷新自动登录的时效 + $this->keeplogin($keeptime); + return true; + } else { + return false; + } + } + + /** + * 刷新保持登录的Cookie + * + * @param int $keeptime + * @return boolean + */ + protected function keeplogin($keeptime = 0) + { + if ($keeptime) { + $expiretime = time() + $keeptime; + $key = md5(md5($this->id) . md5($keeptime) . md5($expiretime) . $this->token . config('token.key')); + $data = [$this->id, $keeptime, $expiretime, $key]; + Cookie::set('keeplogin', implode('|', $data), 86400 * 7); + return true; + } + return false; + } + + public function check($name, $uid = '', $relation = 'or', $mode = 'url') + { + $uid = $uid ? $uid : $this->id; + return parent::check($name, $uid, $relation, $mode); + } + + /** + * 检测当前控制器和方法是否匹配传递的数组 + * + * @param array $arr 需要验证权限的数组 + * @return bool + */ + public function match($arr = []) + { + $request = Request::instance(); + $arr = is_array($arr) ? $arr : explode(',', $arr); + if (!$arr) { + return false; + } + + $arr = array_map('strtolower', $arr); + // 是否存在 + if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) { + return true; + } + + // 没找到匹配 + return false; + } + + /** + * 检测是否登录 + * + * @return boolean + */ + public function isLogin() + { + if ($this->logined) { + return true; + } + $admin = Session::get('admin'); + if (!$admin) { + return false; + } + //判断是否同一时间同一账号只能在一个地方登录 + if (Config::get('fastadmin.login_unique')) { + $my = Admin::get($admin['id']); + if (!$my || $my['token'] != $admin['token']) { + $this->logined = false; //重置登录状态 + Session::delete("admin"); + Cookie::delete("keeplogin"); + return false; + } + } + //判断管理员IP是否变动 + if (Config::get('fastadmin.loginip_check')) { + if (!isset($admin['loginip']) || $admin['loginip'] != request()->ip()) { + $this->logout(); + return false; + } + } + $this->logined = true; + return true; + } + + /** + * 获取当前请求的URI + * @return string + */ + public function getRequestUri() + { + return $this->requestUri; + } + + /** + * 设置当前请求的URI + * @param string $uri + */ + public function setRequestUri($uri) + { + $this->requestUri = $uri; + } + + public function getGroups($uid = null) + { + $uid = is_null($uid) ? $this->id : $uid; + return parent::getGroups($uid); + } + + public function getRuleList($uid = null) + { + $uid = is_null($uid) ? $this->id : $uid; + return parent::getRuleList($uid); + } + + public function getUserInfo($uid = null) + { + $uid = is_null($uid) ? $this->id : $uid; + + return $uid != $this->id ? Admin::get(intval($uid)) : Session::get('admin'); + } + + public function getRuleIds($uid = null) + { + $uid = is_null($uid) ? $this->id : $uid; + return parent::getRuleIds($uid); + } + + public function isSuperAdmin() + { + return in_array('*', $this->getRuleIds()) ? true : false; + } + + /** + * 获取管理员所属于的分组ID + * @param int $uid + * @return array + */ + public function getGroupIds($uid = null) + { + $groups = $this->getGroups($uid); + $groupIds = []; + foreach ($groups as $K => $v) { + $groupIds[] = (int)$v['group_id']; + } + return $groupIds; + } + + /** + * 取出当前管理员所拥有权限的分组 + * @param boolean $withself 是否包含当前所在的分组 + * @return array + */ + public function getChildrenGroupIds($withself = false) + { + //取出当前管理员所有的分组 + $groups = $this->getGroups(); + $groupIds = []; + foreach ($groups as $k => $v) { + $groupIds[] = $v['id']; + } + $originGroupIds = $groupIds; + foreach ($groups as $k => $v) { + if (in_array($v['pid'], $originGroupIds)) { + $groupIds = array_diff($groupIds, [$v['id']]); + unset($groups[$k]); + } + } + // 取出所有分组 + $groupList = \app\admin\model\AuthGroup::where(['status' => 'normal'])->select(); + $objList = []; + foreach ($groups as $k => $v) { + if ($v['rules'] === '*') { + $objList = $groupList; + break; + } + // 取出包含自己的所有子节点 + $childrenList = Tree::instance()->init($groupList, 'pid')->getChildren($v['id'], true); + $obj = Tree::instance()->init($childrenList, 'pid')->getTreeArray($v['pid']); + $objList = array_merge($objList, Tree::instance()->getTreeList($obj)); + } + $childrenGroupIds = []; + foreach ($objList as $k => $v) { + $childrenGroupIds[] = $v['id']; + } + if (!$withself) { + $childrenGroupIds = array_diff($childrenGroupIds, $groupIds); + } + return $childrenGroupIds; + } + + /** + * 取出当前管理员所拥有权限的管理员 + * @param boolean $withself 是否包含自身 + * @return array + */ + public function getChildrenAdminIds($withself = false) + { + $childrenAdminIds = []; + if (!$this->isSuperAdmin()) { + $groupIds = $this->getChildrenGroupIds(false); + $authGroupList = \app\admin\model\AuthGroupAccess:: + field('uid,group_id') + ->where('group_id', 'in', $groupIds) + ->select(); + foreach ($authGroupList as $k => $v) { + $childrenAdminIds[] = $v['uid']; + } + } else { + //超级管理员拥有所有人的权限 + $childrenAdminIds = Admin::column('id'); + } + if ($withself) { + if (!in_array($this->id, $childrenAdminIds)) { + $childrenAdminIds[] = $this->id; + } + } else { + $childrenAdminIds = array_diff($childrenAdminIds, [$this->id]); + } + return $childrenAdminIds; + } + + /** + * 获得面包屑导航 + * @param string $path + * @return array + */ + public function getBreadCrumb($path = '') + { + if ($this->breadcrumb || !$path) { + return $this->breadcrumb; + } + $titleArr = []; + $menuArr = []; + $urlArr = explode('/', $path); + foreach ($urlArr as $index => $item) { + $pathArr[implode('/', array_slice($urlArr, 0, $index + 1))] = $index; + } + if (!$this->rules && $this->id) { + $this->getRuleList(); + } + foreach ($this->rules as $rule) { + if (isset($pathArr[$rule['name']])) { + $rule['title'] = __($rule['title']); + $rule['url'] = url($rule['name']); + $titleArr[$pathArr[$rule['name']]] = $rule['title']; + $menuArr[$pathArr[$rule['name']]] = $rule; + } + + } + ksort($menuArr); + $this->breadcrumb = $menuArr; + return $this->breadcrumb; + } + + /** + * 获取左侧和顶部菜单栏 + * + * @param array $params URL对应的badge数据 + * @param string $fixedPage 默认页 + * @return array + */ + public function getSidebar($params = [], $fixedPage = 'dashboard') + { + // 边栏开始 + Hook::listen("admin_sidebar_begin", $params); + $colorArr = ['red', 'green', 'yellow', 'blue', 'teal', 'orange', 'purple']; + $colorNums = count($colorArr); + $badgeList = []; + $module = request()->module(); + // 生成菜单的badge + foreach ($params as $k => $v) { + $url = $k; + if (is_array($v)) { + $nums = isset($v[0]) ? $v[0] : 0; + $color = isset($v[1]) ? $v[1] : $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums]; + $class = isset($v[2]) ? $v[2] : 'label'; + } else { + $nums = $v; + $color = $colorArr[(is_numeric($nums) ? $nums : strlen($nums)) % $colorNums]; + $class = 'label'; + } + //必须nums大于0才显示 + if ($nums) { + $badgeList[$url] = '' . $nums . ''; + } + } + + // 读取管理员当前拥有的权限节点 + $userRule = $this->getRuleList(); + $selected = $referer = []; + $refererUrl = Session::get('referer'); + // 必须将结果集转换为数组 + $ruleList = collection(\app\admin\model\AuthRule::where('status', 'normal') + ->where('ismenu', 1) + ->order('weigh', 'desc') + ->cache("__menu__") + ->select())->toArray(); + $indexRuleList = \app\admin\model\AuthRule::where('status', 'normal') + ->where('ismenu', 0) + ->where('name', 'like', '%/index') + ->column('name,pid'); + $pidArr = array_unique(array_filter(array_column($ruleList, 'pid'))); + foreach ($ruleList as $k => &$v) { + if (!in_array($v['name'], $userRule)) { + unset($ruleList[$k]); + continue; + } + $indexRuleName = $v['name'] . '/index'; + if (isset($indexRuleList[$indexRuleName]) && !in_array($indexRuleName, $userRule)) { + unset($ruleList[$k]); + continue; + } + $v['icon'] = $v['icon'] . ' fa-fw'; + $v['url'] = isset($v['url']) && $v['url'] ? $v['url'] : '/' . $module . '/' . $v['name']; + $v['badge'] = isset($badgeList[$v['name']]) ? $badgeList[$v['name']] : ''; + $v['title'] = __($v['title']); + $v['url'] = preg_match("/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i", $v['url']) ? $v['url'] : url($v['url']); + $v['menuclass'] = in_array($v['menutype'], ['dialog', 'ajax']) ? 'btn-' . $v['menutype'] : ''; + $v['menutabs'] = !$v['menutype'] || in_array($v['menutype'], ['default', 'addtabs']) ? 'addtabs="' . $v['id'] . '"' : ''; + $selected = $v['name'] == $fixedPage ? $v : $selected; + $referer = $v['url'] == $refererUrl ? $v : $referer; + } + $lastArr = array_unique(array_filter(array_column($ruleList, 'pid'))); + $pidDiffArr = array_diff($pidArr, $lastArr); + foreach ($ruleList as $index => $item) { + if (in_array($item['id'], $pidDiffArr)) { + unset($ruleList[$index]); + } + } + if ($selected == $referer) { + $referer = []; + } + + $select_id = $referer ? $referer['id'] : ($selected ? $selected['id'] : 0); + $menu = $nav = ''; + $showSubmenu = config('fastadmin.show_submenu'); + if (Config::get('fastadmin.multiplenav')) { + $topList = []; + foreach ($ruleList as $index => $item) { + if (!$item['pid']) { + $topList[] = $item; + } + } + $selectParentIds = []; + $tree = Tree::instance(); + $tree->init($ruleList); + if ($select_id) { + $selectParentIds = $tree->getParentsIds($select_id, true); + } + foreach ($topList as $index => $item) { + $childList = Tree::instance()->getTreeMenu( + $item['id'], + '
  • @title @caret @badge @childlist
  • ', + $select_id, + '', + 'ul', + 'class="treeview-menu' . ($showSubmenu ? ' menu-open' : '') . '"' + ); + $current = in_array($item['id'], $selectParentIds); + $url = $childList ? 'javascript:;' : $item['url']; + $addtabs = $childList || !$url ? "" : (stripos($url, "?") !== false ? "&" : "?") . "ref=" . ($item['menutype'] ? $item['menutype'] : 'addtabs'); + $childList = str_replace( + '" pid="' . $item['id'] . '"', + ' ' . ($current ? '' : 'hidden') . '" pid="' . $item['id'] . '"', + $childList + ); + $nav .= '
  • ' . $item['title'] . '
  • '; + $menu .= $childList; + } + } else { + // 构造菜单数据 + Tree::instance()->init($ruleList); + $menu = Tree::instance()->getTreeMenu( + 0, + '
  • @title @caret @badge @childlist
  • ', + $select_id, + '', + 'ul', + 'class="treeview-menu' . ($showSubmenu ? ' menu-open' : '') . '"' + ); + if ($selected) { + $nav .= ''; + } + if ($referer) { + $nav .= ''; + } + } + + return [$menu, $nav, $selected, $referer]; + } + + /** + * 设置错误信息 + * + * @param string $error 错误信息 + * @return Auth + */ + public function setError($error) + { + $this->_error = $error; + return $this; + } + + /** + * 获取错误信息 + * @return string + */ + public function getError() + { + return $this->_error ? __($this->_error) : ''; + } +} diff --git a/application/admin/library/traits/Backend.php b/application/admin/library/traits/Backend.php new file mode 100644 index 0000000..fc7032b --- /dev/null +++ b/application/admin/library/traits/Backend.php @@ -0,0 +1,481 @@ +excludeFields)) { + foreach ($this->excludeFields as $field) { + if (array_key_exists($field, $params)) { + unset($params[$field]); + } + } + } else if (array_key_exists($this->excludeFields, $params)) { + unset($params[$this->excludeFields]); + } + return $params; + } + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if (false === $this->request->isAjax()) { + return $this->view->fetch(); + } + //如果发送的来源是 Selectpage,则转发到 Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + [$where, $sort, $order, $offset, $limit] = $this->buildparams(); + $list = $this->model + ->where($where) + ->order($sort, $order) + ->paginate($limit); + $result = ['total' => $list->total(), 'rows' => $list->items()]; + return json($result); + } + + /** + * 回收站 + * + * @return string|Json + * @throws \think\Exception + */ + public function recyclebin() + { + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if (false === $this->request->isAjax()) { + return $this->view->fetch(); + } + [$where, $sort, $order, $offset, $limit] = $this->buildparams(); + $list = $this->model + ->onlyTrashed() + ->where($where) + ->order($sort, $order) + ->paginate($limit); + $result = ['total' => $list->total(), 'rows' => $list->items()]; + return json($result); + } + + /** + * 添加 + * + * @return string + * @throws \think\Exception + */ + public function add() + { + if (false === $this->request->isPost()) { + return $this->view->fetch(); + } + $params = $this->request->post('row/a'); + if (empty($params)) { + $this->error(__('Parameter %s can not be empty', '')); + } + $params = $this->preExcludeFields($params); + + if ($this->dataLimit && $this->dataLimitFieldAutoFill) { + $params[$this->dataLimitField] = $this->auth->id; + } + $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()->validate($validate); + } + $result = $this->model->allowField(true)->save($params); + Db::commit(); + } catch (ValidateException|PDOException|Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($result === false) { + $this->error(__('No rows were inserted')); + } + $this->success(); + } + + /** + * 编辑 + * + * @param $ids + * @return string + * @throws DbException + * @throws \think\Exception + */ + 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) && !in_array($row[$this->dataLimitField], $adminIds)) { + $this->error(__('You have no permission')); + } + if (false === $this->request->isPost()) { + $this->view->assign('row', $row); + return $this->view->fetch(); + } + $params = $this->request->post('row/a'); + if (empty($params)) { + $this->error(__('Parameter %s can not be empty', '')); + } + $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()->validate($validate); + } + $result = $row->allowField(true)->save($params); + Db::commit(); + } catch (ValidateException|PDOException|Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if (false === $result) { + $this->error(__('No rows were updated')); + } + $this->success(); + } + + /** + * 删除 + * + * @param $ids + * @return void + * @throws DbException + * @throws DataNotFoundException + * @throws ModelNotFoundException + */ + public function del($ids = null) + { + if (false === $this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ?: $this->request->post("ids"); + if (empty($ids)) { + $this->error(__('Parameter %s can not be empty', 'ids')); + } + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $list = $this->model->where($pk, 'in', $ids)->select(); + + $count = 0; + Db::startTrans(); + try { + foreach ($list as $item) { + $count += $item->delete(); + } + Db::commit(); + } catch (PDOException|Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($count) { + $this->success(); + } + $this->error(__('No rows were deleted')); + } + + /** + * 真实删除 + * + * @param $ids + * @return void + */ + public function destroy($ids = null) + { + if (false === $this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $ids = $ids ?: $this->request->post('ids'); + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + if ($ids) { + $this->model->where($pk, 'in', $ids); + } + $count = 0; + Db::startTrans(); + try { + $list = $this->model->onlyTrashed()->select(); + foreach ($list as $item) { + $count += $item->delete(true); + } + Db::commit(); + } catch (PDOException|Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($count) { + $this->success(); + } + $this->error(__('No rows were deleted')); + } + + /** + * 还原 + * + * @param $ids + * @return void + */ + public function restore($ids = null) + { + if (false === $this->request->isPost()) { + $this->error(__('Invalid parameters')); + } + $ids = $ids ?: $this->request->post('ids'); + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + if ($ids) { + $this->model->where($pk, 'in', $ids); + } + $count = 0; + Db::startTrans(); + try { + $list = $this->model->onlyTrashed()->select(); + foreach ($list as $item) { + $count += $item->restore(); + } + Db::commit(); + } catch (PDOException|Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($count) { + $this->success(); + } + $this->error(__('No rows were updated')); + } + + /** + * 批量更新 + * + * @param $ids + * @return void + */ + public function multi($ids = null) + { + if (false === $this->request->isPost()) { + $this->error(__('Invalid parameters')); + } + $ids = $ids ?: $this->request->post('ids'); + if (empty($ids)) { + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + if (false === $this->request->has('params')) { + $this->error(__('No rows were updated')); + } + parse_str($this->request->post('params'), $values); + $values = $this->auth->isSuperAdmin() ? $values : array_intersect_key($values, array_flip(is_array($this->multiFields) ? $this->multiFields : explode(',', $this->multiFields))); + if (empty($values)) { + $this->error(__('You have no permission')); + } + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $count = 0; + Db::startTrans(); + try { + $list = $this->model->where($this->model->getPk(), 'in', $ids)->select(); + foreach ($list as $item) { + $count += $item->allowField(true)->isUpdate(true)->save($values); + } + Db::commit(); + } catch (PDOException|Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + if ($count) { + $this->success(); + } + $this->error(__('No rows were updated')); + } + + /** + * 导入 + * + * @return void + * @throws PDOException + * @throws BindParamException + */ + protected function import() + { + $file = $this->request->request('file'); + if (!$file) { + $this->error(__('Parameter %s can not be empty', 'file')); + } + $filePath = ROOT_PATH . DS . 'public' . DS . $file; + if (!is_file($filePath)) { + $this->error(__('No results were found')); + } + //实例化reader + $ext = pathinfo($filePath, PATHINFO_EXTENSION); + if (!in_array($ext, ['csv', 'xls', 'xlsx'])) { + $this->error(__('Unknown data format')); + } + if ($ext === 'csv') { + $file = fopen($filePath, 'r'); + $filePath = tempnam(sys_get_temp_dir(), 'import_csv'); + $fp = fopen($filePath, 'w'); + $n = 0; + while ($line = fgets($file)) { + $line = rtrim($line, "\n\r\0"); + $encoding = mb_detect_encoding($line, ['utf-8', 'gbk', 'latin1', 'big5']); + if ($encoding !== 'utf-8') { + $line = mb_convert_encoding($line, 'utf-8', $encoding); + } + if ($n == 0 || preg_match('/^".*"$/', $line)) { + fwrite($fp, $line . "\n"); + } else { + fwrite($fp, '"' . str_replace(['"', ','], ['""', '","'], $line) . "\"\n"); + } + $n++; + } + fclose($file) || fclose($fp); + + $reader = new Csv(); + } elseif ($ext === 'xls') { + $reader = new Xls(); + } else { + $reader = new Xlsx(); + } + + //导入文件首行类型,默认是注释,如果需要使用字段名称请使用name + $importHeadType = isset($this->importHeadType) ? $this->importHeadType : 'comment'; + + $table = $this->model->getQuery()->getTable(); + $database = \think\Config::get('database.database'); + $fieldArr = []; + $list = db()->query("SELECT COLUMN_NAME,COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?", [$table, $database]); + foreach ($list as $k => $v) { + if ($importHeadType == 'comment') { + $v['COLUMN_COMMENT'] = explode(':', $v['COLUMN_COMMENT'])[0]; //字段备注有:时截取 + $fieldArr[$v['COLUMN_COMMENT']] = $v['COLUMN_NAME']; + } else { + $fieldArr[$v['COLUMN_NAME']] = $v['COLUMN_NAME']; + } + } + + //加载文件 + $insert = []; + try { + if (!$PHPExcel = $reader->load($filePath)) { + $this->error(__('Unknown data format')); + } + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $allColumn = $currentSheet->getHighestDataColumn(); //取得最大的列号 + $allRow = $currentSheet->getHighestRow(); //取得一共有多少行 + $maxColumnNumber = Coordinate::columnIndexFromString($allColumn); + $fields = []; + for ($currentRow = 1; $currentRow <= 1; $currentRow++) { + for ($currentColumn = 1; $currentColumn <= $maxColumnNumber; $currentColumn++) { + $val = $currentSheet->getCellByColumnAndRow($currentColumn, $currentRow)->getValue(); + $fields[] = $val; + } + } + + for ($currentRow = 2; $currentRow <= $allRow; $currentRow++) { + $values = []; + for ($currentColumn = 1; $currentColumn <= $maxColumnNumber; $currentColumn++) { + $val = $currentSheet->getCellByColumnAndRow($currentColumn, $currentRow)->getValue(); + $values[] = is_null($val) ? '' : $val; + } + $row = []; + $temp = array_combine($fields, $values); + foreach ($temp as $k => $v) { + if (isset($fieldArr[$k]) && $k !== '') { + $row[$fieldArr[$k]] = $v; + } + } + if ($row) { + $insert[] = $row; + } + } + } catch (Exception $exception) { + $this->error($exception->getMessage()); + } + if (!$insert) { + $this->error(__('No rows were updated')); + } + + try { + //是否包含admin_id字段 + $has_admin_id = false; + foreach ($fieldArr as $name => $key) { + if ($key == 'admin_id') { + $has_admin_id = true; + break; + } + } + if ($has_admin_id) { + $auth = Auth::instance(); + foreach ($insert as &$val) { + if (!isset($val['admin_id']) || empty($val['admin_id'])) { + $val['admin_id'] = $auth->isLogin() ? $auth->id : 0; + } + } + } + $this->model->saveAll($insert); + } catch (PDOException $exception) { + $msg = $exception->getMessage(); + if (preg_match("/.+Integrity constraint violation: 1062 Duplicate entry '(.+)' for key '(.+)'/is", $msg, $matches)) { + $msg = "导入失败,包含【{$matches[1]}】的记录已存在"; + }; + $this->error($msg); + } catch (Exception $e) { + $this->error($e->getMessage()); + } + + $this->success(); + } +} diff --git a/application/admin/model/Addonnews.php b/application/admin/model/Addonnews.php new file mode 100644 index 0000000..5758878 --- /dev/null +++ b/application/admin/model/Addonnews.php @@ -0,0 +1,20 @@ +encryptPassword($NewPassword); + $ret = $this->where(['id' => $uid])->update(['password' => $passwd]); + return $ret; + } + + // 密码加密 + protected function encryptPassword($password, $salt = '', $encrypt = 'md5') + { + return $encrypt($password . $salt); + } + +} diff --git a/application/admin/model/AdminLog.php b/application/admin/model/AdminLog.php new file mode 100644 index 0000000..faf7e56 --- /dev/null +++ b/application/admin/model/AdminLog.php @@ -0,0 +1,114 @@ +isLogin() ? $auth->id : 0; + $username = $auth->isLogin() ? $auth->username : __('Unknown'); + + $controllername = Loader::parseName(request()->controller()); + $actionname = strtolower(request()->action()); + $path = str_replace('.', '/', $controllername) . '/' . $actionname; + if (self::$ignoreRegex) { + foreach (self::$ignoreRegex as $index => $item) { + if (preg_match($item, $path)) { + return; + } + } + } + $content = $content ? $content : self::$content; + if (!$content) { + $content = request()->param('', null, 'trim,strip_tags,htmlspecialchars'); + $content = self::getPureContent($content); + } + $title = $title ? $title : self::$title; + if (!$title) { + $title = []; + $breadcrumb = Auth::instance()->getBreadcrumb($path); + foreach ($breadcrumb as $k => $v) { + $title[] = $v['title']; + } + $title = implode(' / ', $title); + } + self::create([ + 'title' => $title, + 'content' => !is_scalar($content) ? json_encode($content, JSON_UNESCAPED_UNICODE) : $content, + 'url' => substr(request()->url(), 0, 1500), + 'admin_id' => $admin_id, + 'username' => $username, + 'useragent' => substr(request()->server('HTTP_USER_AGENT'), 0, 255), + 'ip' => request()->ip() + ]); + } + + /** + * 获取已屏蔽关键信息的数据 + * @param $content + * @return false|string + */ + protected static function getPureContent($content) + { + if (!is_array($content)) { + return $content; + } + foreach ($content as $index => &$item) { + if (preg_match("/(password|salt|token)/i", $index)) { + $item = "***"; + } else { + if (is_array($item)) { + $item = self::getPureContent($item); + } + } + } + return $content; + } + + public function admin() + { + return $this->belongsTo('Admin', 'admin_id')->setEagerlyType(0); + } +} diff --git a/application/admin/model/AuthGroup.php b/application/admin/model/AuthGroup.php new file mode 100644 index 0000000..bfbaec3 --- /dev/null +++ b/application/admin/model/AuthGroup.php @@ -0,0 +1,21 @@ + __('Addtabs'), 'dialog' => __('Dialog'), 'ajax' => __('Ajax'), 'blank' => __('Blank')]; + } + + public function setPyAttr($value, $data) + { + if (isset($data['title']) && $data['title']) { + return self::$pinyin->abbr(__($data['title'])); + } + return ''; + } + + public function setPinyinAttr($value, $data) + { + if (isset($data['title']) && $data['title']) { + return self::$pinyin->permalink(__($data['title']), ''); + } + return ''; + } +} diff --git a/application/admin/model/Club.php b/application/admin/model/Club.php new file mode 100644 index 0000000..469b5f1 --- /dev/null +++ b/application/admin/model/Club.php @@ -0,0 +1,29 @@ + __('Successed'), 'failured' => __('Failured')]; + } + + + public function getExecutetimeTextAttr($value, $data) + { + $value = $value ? $value : $data['executetime']; + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + public function getTypeTextAttr($value, $data) + { + $value = $value ? $value : $data['type']; + $list = ['crud' => '一键生成CRUD', 'menu' => '一键生成菜单', 'min' => '一键压缩打包', 'api' => '一键生成文档']; + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + protected function setExecutetimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + +} diff --git a/application/admin/model/Countryflag.php b/application/admin/model/Countryflag.php new file mode 100644 index 0000000..b9c8469 --- /dev/null +++ b/application/admin/model/Countryflag.php @@ -0,0 +1,40 @@ +belongsTo('Players','player_id', 'id', [], 'LEFT')->setEagerlyType(1); + } + + public function players() + { + return $this->belongsTo('Players','player_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + public function club() + { + return $this->belongsTo('Club','club_id', 'id', [], 'LEFT')->setEagerlyType(1); + } + + public function clubs() + { + return $this->belongsTo('Club','club_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + +} \ No newline at end of file diff --git a/application/admin/model/MatchRanking b/application/admin/model/MatchRanking new file mode 100644 index 0000000..e69de29 diff --git a/application/admin/model/MatchRanking.php b/application/admin/model/MatchRanking.php new file mode 100644 index 0000000..9a5b5a0 --- /dev/null +++ b/application/admin/model/MatchRanking.php @@ -0,0 +1,21 @@ +origin; + } + + +} diff --git a/application/admin/model/Players.php b/application/admin/model/Players.php new file mode 100644 index 0000000..a635748 --- /dev/null +++ b/application/admin/model/Players.php @@ -0,0 +1,108 @@ +origin; + } + + protected static function init() + { + self::beforeUpdate(function ($row) { + $changed = $row->getChangedData(); + //如果有修改密码 + if (isset($changed['password'])) { + if ($changed['password']) { + $salt = \fast\Random::alnum(); + $row->password = \app\common\library\Auth::instance()->getEncryptPassword($changed['password'], $salt); + $row->salt = $salt; + } else { + unset($row->password); + } + } + }); + + + self::beforeUpdate(function ($row) { + $changedata = $row->getChangedData(); + $origin = $row->getOriginData(); + if (isset($changedata['money']) && (function_exists('bccomp') ? bccomp($changedata['money'], $origin['money'], 2) !== 0 : (double)$changedata['money'] !== (double)$origin['money'])) { + MoneyLog::create(['user_id' => $row['id'], 'money' => $changedata['money'] - $origin['money'], 'before' => $origin['money'], 'after' => $changedata['money'], 'memo' => '管理员变更金额']); + } + if (isset($changedata['score']) && (int)$changedata['score'] !== (int)$origin['score']) { + ScoreLog::create(['user_id' => $row['id'], 'score' => $changedata['score'] - $origin['score'], 'before' => $origin['score'], 'after' => $changedata['score'], 'memo' => '管理员变更积分']); + } + }); + } + + public function getGenderList() + { + return ['1' => __('Male'), '0' => __('Female')]; + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + + public function getPrevtimeTextAttr($value, $data) + { + $value = $value ? $value : ($data['prevtime'] ?? ""); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + public function getLogintimeTextAttr($value, $data) + { + $value = $value ? $value : ($data['logintime'] ?? ""); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + public function getJointimeTextAttr($value, $data) + { + $value = $value ? $value : ($data['jointime'] ?? ""); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + protected function setPrevtimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setLogintimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setJointimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setBirthdayAttr($value) + { + return $value ? $value : null; + } + + public function member() + { + return $this->belongsTo('User', 'member_id', 'id',[],'LEFT')->setEagerlyType(0); + } + +} diff --git a/application/admin/model/Renzheng.php b/application/admin/model/Renzheng.php new file mode 100644 index 0000000..44f5d7a --- /dev/null +++ b/application/admin/model/Renzheng.php @@ -0,0 +1,49 @@ + __('Status 11')]; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + + +} diff --git a/application/admin/model/Renzheng/Php.php b/application/admin/model/Renzheng/Php.php new file mode 100644 index 0000000..8c75f20 --- /dev/null +++ b/application/admin/model/Renzheng/Php.php @@ -0,0 +1,49 @@ + __('Status 11')]; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + + +} diff --git a/application/admin/model/Third.php b/application/admin/model/Third.php new file mode 100644 index 0000000..66d3be0 --- /dev/null +++ b/application/admin/model/Third.php @@ -0,0 +1,56 @@ +belongsTo("User", 'user_id', 'id')->setEagerlyType(0); + } +} diff --git a/application/admin/model/User.php b/application/admin/model/User.php new file mode 100644 index 0000000..b5b2cb4 --- /dev/null +++ b/application/admin/model/User.php @@ -0,0 +1,119 @@ +origin; + } + + protected static function init() + { + self::beforeUpdate(function ($row) { + $changed = $row->getChangedData(); + //如果有修改密码 + if (isset($changed['password'])) { + if ($changed['password']) { + $salt = \fast\Random::alnum(); + $row->password = \app\common\library\Auth::instance()->getEncryptPassword($changed['password'], $salt); + $row->salt = $salt; + } else { + unset($row->password); + } + } + }); + + + self::beforeUpdate(function ($row) { + $changedata = $row->getChangedData(); + $origin = $row->getOriginData(); + if (isset($changedata['money']) && (function_exists('bccomp') ? bccomp($changedata['money'], $origin['money'], 2) !== 0 : (double)$changedata['money'] !== (double)$origin['money'])) { + MoneyLog::create(['user_id' => $row['id'], 'money' => $changedata['money'] - $origin['money'], 'before' => $origin['money'], 'after' => $changedata['money'], 'memo' => '管理员变更金额']); + } + if (isset($changedata['score']) && (int)$changedata['score'] !== (int)$origin['score']) { + ScoreLog::create(['user_id' => $row['id'], 'score' => $changedata['score'] - $origin['score'], 'before' => $origin['score'], 'after' => $changedata['score'], 'memo' => '管理员变更积分']); + } + }); + } + + public function getGenderList() + { + return ['1' => __('Male'), '0' => __('Female')]; + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + + public function getPrevtimeTextAttr($value, $data) + { + $value = $value ? $value : ($data['prevtime'] ?? ""); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + public function getLogintimeTextAttr($value, $data) + { + $value = $value ? $value : ($data['logintime'] ?? ""); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + public function getJointimeTextAttr($value, $data) + { + $value = $value ? $value : ($data['jointime'] ?? ""); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + protected function setPrevtimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setLogintimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setJointimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + protected function setBirthdayAttr($value) + { + return $value ? $value : null; + } + + public function group() + { + return $this->belongsTo('UserGroup', 'group_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + public function players() + { + return $this->belongsTo('Players','id', 'member_id', [], 'RIGHT'); + } + +} diff --git a/application/admin/model/UserGroup.php b/application/admin/model/UserGroup.php new file mode 100644 index 0000000..8ce26bc --- /dev/null +++ b/application/admin/model/UserGroup.php @@ -0,0 +1,34 @@ + __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + +} diff --git a/application/admin/model/UserRule.php b/application/admin/model/UserRule.php new file mode 100644 index 0000000..683e605 --- /dev/null +++ b/application/admin/model/UserRule.php @@ -0,0 +1,67 @@ +getPk(); + $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]); + }); + } + + public function getTitleAttr($value, $data) + { + return __($value); + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public static function getTreeList($selected = []) + { + $ruleList = collection(self::where('status', 'normal')->order('weigh desc,id desc')->select())->toArray(); + $nodeList = []; + Tree::instance()->init($ruleList); + $ruleList = Tree::instance()->getTreeList(Tree::instance()->getTreeArray(0), 'name'); + $hasChildrens = []; + foreach ($ruleList as $k => $v) + { + if ($v['haschild']) + $hasChildrens[] = $v['id']; + } + foreach ($ruleList as $k => $v) { + $state = array('selected' => in_array($v['id'], $selected) && !in_array($v['id'], $hasChildrens)); + $nodeList[] = array('id' => $v['id'], 'parent' => $v['pid'] ? $v['pid'] : '#', 'text' => __($v['title']), 'type' => 'menu', 'state' => $state); + } + return $nodeList; + } + +} diff --git a/application/admin/model/Userwechat.php b/application/admin/model/Userwechat.php new file mode 100644 index 0000000..274de4b --- /dev/null +++ b/application/admin/model/Userwechat.php @@ -0,0 +1,24 @@ + __('Status 11')]; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + + +} diff --git a/application/admin/model/cms/Archives.php b/application/admin/model/cms/Archives.php new file mode 100644 index 0000000..ec84395 --- /dev/null +++ b/application/admin/model/cms/Archives.php @@ -0,0 +1,331 @@ +buildUrl($value, $data); + } + + public function getFullurlAttr($value, $data) + { + return $this->buildUrl($value, $data, true); + } + + private function buildUrl($value, $data, $domain = false) + { + $diyname = isset($data['diyname']) && $data['diyname'] ? $data['diyname'] : $data['id']; + $catename = isset($this->channel) && $this->channel ? $this->channel->diyname : 'all'; + $cateid = isset($this->channel) && $this->channel ? $this->channel->id : 0; + $time = $data['publishtime'] ?? time(); + $vars = [ + ':id' => $data['id'], + ':eid' => $this->getEidAttr($data['id'], $data), + ':diyname' => $diyname, + ':channel' => $data['channel_id'], + ':catename' => $catename, + ':cateid' => $cateid, + ':year' => date("Y", $time), + ':month' => date("m", $time), + ':day' => date("d", $time), + ]; + return addon_url('cms/archives/index', $vars, static::$config['urlsuffix'], true); + } + + public function getOriginData() + { + return $this->origin; + } + + /** + * 批量设置数据 + * @param $data + * @return $this + */ + public function setData($data) + { + if (is_object($data)) { + $data = get_object_vars($data); + } + $this->data = array_merge($this->data, $data); + return $this; + } + + protected static function init() + { + self::$config = $config = get_addon_config('cms'); + self::beforeInsert(function ($row) { + if (!isset($row['admin_id']) || !$row['admin_id']) { + $admin_id = session('admin.id'); + $row['admin_id'] = $admin_id ? $admin_id : 0; + } + }); + self::afterInsert(function ($row) { + $pk = $row->getPk(); + $channel = Channel::get($row['channel_id']); + $row->getQuery()->where($pk, $row[$pk])->update(['model_id' => $channel ? $channel['model_id'] : 0]); + }); + self::beforeWrite(function ($row) { + //在更新之前对数组进行处理 + foreach ($row->getData() as $k => $value) { + if (is_array($value) && is_array(reset($value))) { + $value = json_encode(self::getArrayData($value), JSON_UNESCAPED_UNICODE); + } else { + $value = is_array($value) ? implode(',', $value) : $value; + } + $row->setAttr($k, $value); + } + $changedData = $row->getChangedData(); + if (isset($changedData['flag'])) { + $row['weigh'] = in_array('top', explode(',', $changedData['flag'])) + ? ($row['weigh'] == 0 ? 9999 : $row['weigh']) + : ($row['weigh'] == 9999 ? 0 : $row['weigh']); + } + + if (isset($row['content']) && !self::$config['realtimereplacelink']) { + $row['content'] = Service::autolinks($row['content']); + } + }); + self::afterWrite(function ($row) use ($config) { + if (isset($row['channel_id'])) { + //在更新成功后刷新副表、TAGS表数据、栏目表 + $channel = Channel::get($row->channel_id); + if ($channel) { + $model = Modelx::get($channel['model_id']); + if ($model) { + $values = array_intersect_key($row->getData(), array_flip($model->fields)); + $values['id'] = $row['id']; + if (isset($row['content'])) { + $values['content'] = $row['content']; + } + //更新副表 + $addonTbl = \think\Db::name($model['table']); + if ($addonTbl->find($row['id'])) { + $addonTbl->update($values); + } else { + $addonTbl->insert($values, true); + } + } + } + } + if (isset($row['tags'])) { + \addons\cms\model\Tag::refresh($row['tags'], $row['id']); + } + $changedData = $row->getChangedData(); + if (isset($changedData['status']) && $changedData['status'] == 'normal') { + if (isset($row['user_id']) && isset($config['score']['postarchives'])) { + //增加积分 + User::score($config['score']['postarchives'], $row['user_id'], '发布文章'); + } + //推送到熊掌号和百度站长 + if ($config['baidupush']) { + $urls = [$row->fullurl]; + \think\Hook::listen("baidupush", $urls); + } + } + if (isset($changedData['channel_id']) || isset($changedData['channel_ids'])) { + //有更新栏目和副栏目需要刷新统计数据 + $refreshIds = []; + $originData = $row->getOriginData(); + if (isset($changedData['channel_id'])) { + $refreshIds = array_merge($refreshIds, [$originData['channel_id'] ?? 0, $changedData['channel_id']]); + } + if (isset($changedData['channel_ids'])) { + $refreshIds = array_merge($refreshIds, explode(',', $originData['channel_ids'] ?? ''), explode(',', $changedData['channel_ids'])); + } + $refreshIds = array_filter(array_unique($refreshIds)); + Channel::refreshItems($refreshIds); + } + if ($config['searchtype'] == 'xunsearch') { + //更新全文搜索 + FulltextSearch::update($row->id); + } + }); + self::afterDelete(function ($row) use ($config) { + $data = Archives::withTrashed()->find($row['id']); + if ($data) { + if ($row['status'] == 'normal' && isset($config['score']['postarchives'])) { + User::score(-$config['score']['postarchives'], $row['user_id'], '删除文章'); + } + if ($config['searchtype'] == 'xunsearch') { + FulltextSearch::del($row); + } + } else { + //删除相关TAG + \addons\cms\model\Tag::refresh('', $row['id']); + } + //删除评论 + Comment::deleteByType('archives', $row['id'], !$data); + + //刷新统计数据 + $refreshIds = array_merge([$row['channel_id']], explode(',', $row['channel_ids'])); + $refreshIds = array_filter(array_unique($refreshIds)); + Channel::refreshItems($refreshIds); + }); + } + + public function getFlagList() + { + $config = get_addon_config('cms'); + return $config['flagtype']; + } + + public function getStatusList() + { + return ['normal' => __('Status Normal'), 'hidden' => __('Status Hidden'), 'rejected' => __('Status rejected'), 'pulloff' => __('Status pulloff')]; + } + + public function getStyleBoldAttr($value, $data) + { + return in_array('b', explode('|', $data['style'])); + } + + public function getStyleColorAttr($value, $data) + { + $result = preg_match("/(#([0-9a-z]{6}))/i", $data['style'], $matches); + return $result ? $matches[1] : ''; + } + + public function getFlagTextAttr($value, $data) + { + $value = $value ? $value : $data['flag']; + $valueArr = $value ? explode(',', $value) : []; + $list = $this->getFlagList(); + return implode(',', array_intersect_key($list, array_flip($valueArr))); + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getPublishtimeTextAttr($value, $data) + { + $value = $value ? $value : $data['publishtime']; + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + protected function setPublishtimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : ($value ? $value : null); + } + + protected function setCreatetimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : ($value ? $value : null); + } + + protected function setKeywordsAttr($value) + { + return str_replace([" ", "\r\n", "\r", "\n"], "", strip_tags($value)); + } + + protected function setDescriptionAttr($value) + { + return str_replace([" ", "\r\n", "\r", "\n"], "", strip_tags($value)); + } + + public static function getArrayData($data) + { + if (!isset($data['value'])) { + $result = []; + foreach ($data as $index => $datum) { + $result['field'][$index] = $datum['key']; + $result['value'][$index] = $datum['value']; + } + $data = $result; + } + $fieldarr = $valuearr = []; + $field = isset($data['field']) ? $data['field'] : (isset($data['key']) ? $data['key'] : []); + $value = isset($data['value']) ? $data['value'] : []; + foreach ($field as $m => $n) { + if ($n != '') { + $fieldarr[] = $field[$m]; + $valuearr[] = $value[$m]; + } + } + return $fieldarr ? array_combine($fieldarr, $valuearr) : []; + } + + public function channel() + { + return $this->belongsTo('Channel', 'channel_id', '', [], 'LEFT')->setEagerlyType(0); + } + + public function special() + { + return $this->belongsTo('Special', 'special_id', '', [], 'LEFT')->setEagerlyType(0); + } + + /** + * 关联模型 + */ + public function model() + { + return $this->belongsTo("Modelx", 'model_id')->setEagerlyType(1); + } + + /** + * 关联模型 + */ + public function user() + { + return $this->belongsTo("\\app\\common\\model\\User", 'user_id', 'id', [], 'LEFT')->setEagerlyType(1); + } + + /** + * 关联模型 + */ + public function admin() + { + return $this->belongsTo("\\app\\admin\\model\\Admin", 'admin_id', 'id', [], 'LEFT')->setEagerlyType(1); + } +} diff --git a/application/admin/model/cms/Autolink.php b/application/admin/model/cms/Autolink.php new file mode 100644 index 0000000..4465a74 --- /dev/null +++ b/application/admin/model/cms/Autolink.php @@ -0,0 +1,60 @@ +getPk(); + $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]); + }); + } + + public function getTargetList() + { + return ['self' => __('Self'), 'blank' => __('Blank')]; + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getTargetTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['target']) ? $data['target'] : ''); + $list = $this->getTargetList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + +} diff --git a/application/admin/model/cms/Block.php b/application/admin/model/cms/Block.php new file mode 100644 index 0000000..abaf048 --- /dev/null +++ b/application/admin/model/cms/Block.php @@ -0,0 +1,81 @@ +save(['weigh' => $row['id']]); + }); + self::beforeWrite(function ($row) { + //在更新之前对数组进行处理 + foreach ($row->getData() as $k => $value) { + if (is_array($value) && is_array(reset($value))) { + $value = json_encode(self::getArrayData($value), JSON_UNESCAPED_UNICODE); + } else { + $value = is_array($value) ? implode(',', $value) : $value; + } + $row->setAttr($k, $value); + } + if (isset($row['begintime']) && isset($row['endtime']) && $row['begintime'] > $row['endtime']) { + exception("结束时间必须大于开始时间"); + } + }); + } + + public function getBegintimeTextAttr($value, $data) + { + $value = $value ? $value : $data['begintime']; + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + protected function setBegintimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : ($value ? $value : null); + } + + public function getEndtimeTextAttr($value, $data) + { + $value = $value ? $value : $data['endtime']; + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + protected function setEndtimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : ($value ? $value : null); + } + + public function getNameList() + { + return ['indexfocus' => 'PC首页焦点图', 'downloadfocus' => 'PC下载频道页焦点图', 'newsfocus' => 'PC资讯频道页焦点图', 'productfocus' => 'PC产品频道页焦点图', 'uniappfocus' => 'UniAPP焦点图', 'wxappfocus' => '原生微信小程序焦点图']; + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } +} diff --git a/application/admin/model/cms/Channel.php b/application/admin/model/cms/Channel.php new file mode 100644 index 0000000..03c3cfb --- /dev/null +++ b/application/admin/model/cms/Channel.php @@ -0,0 +1,325 @@ +origin; + } + + public function getUrlAttr($value, $data) + { + return $this->buildUrl($value, $data); + } + + public function getFullurlAttr($value, $data) + { + return $this->buildUrl($value, $data, true); + } + + private function buildUrl($value, $data, $domain = false) + { + $diyname = isset($data['diyname']) && $data['diyname'] ? $data['diyname'] : $data['id']; + $cateid = $data['id'] ?? 0; + $catename = isset($data['diyname']) && $data['diyname'] ? $data['diyname'] : 'all'; + $time = $data['createtime'] ?? time(); + + $vars = [ + ':id' => $data['id'], + ':diyname' => $diyname, + ':channel' => $cateid, + ':catename' => $catename, + ':cateid' => $cateid, + ':year' => date("Y", $time), + ':month' => date("m", $time), + ':day' => date("d", $time) + ]; + if (isset($data['type']) && isset($data['outlink']) && $data['type'] == 'link') { + return $this->getAttr('outlink'); + } + return addon_url('cms/channel/index', $vars, static::$config['urlsuffix'], $domain); + } + + public function getOutlinkAttr($value, $data) + { + $indexUrl = $view_replace_str = config('view_replace_str.__PUBLIC__'); + $indexUrl = rtrim($indexUrl, '/'); + return str_replace('__INDEX__', $indexUrl, $value); + } + + protected static function init() + { + $config = static::$config = get_addon_config('cms'); + self::beforeInsert(function ($row) { + if ($row->getData('type') == 'link') { + $row->model_id = 0; + } + $diyname = $row['diyname'] ?? ''; + if ($diyname) { + $exists = Channel::getByDiyname($diyname); + if ($exists) { + throw new Exception("自定义URL名称已经存在"); + } + } + }); + self::beforeUpdate(function ($row) { + if (isset($row['parent_id']) && $row['parent_id']) { + $childrenIds = self::getChildrenIds($row['id'], true); + if (in_array($row['parent_id'], $childrenIds)) { + throw new Exception("上级栏目不能是其自身或子栏目"); + } + } + if (isset($row['diyname']) && $row['diyname']) { + $exists = Channel::where('diyname', $row['diyname'])->where('id', '<>', $row['id'])->find(); + if ($exists) { + throw new Exception("自定义URL名称已经存在"); + } + } + }); + self::beforeWrite(function ($row) { + //在更新之前对数组进行处理 + foreach ($row->getData() as $k => $value) { + if (is_array($value) && is_array(reset($value))) { + $value = json_encode(self::getArrayData($value), JSON_UNESCAPED_UNICODE); + } else { + $value = is_array($value) ? implode(',', $value) : $value; + } + $row->setAttr($k, $value); + } + }); + self::afterInsert(function ($row) { + //创建时自动添加权重值 + $pk = $row->getPk(); + $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]); + }); + self::afterDelete(function ($row) { + //删除时,删除子节点,同时将所有相关文档移入回收站 + $childIds = self::getChildrenIds($row['id']); + if ($childIds) { + Channel::destroy(function ($query) use ($childIds) { + $query->where('id', 'in', $childIds); + }); + } + $childIds[] = $row['id']; + db('cms_archives')->where('channel_id', 'in', $childIds)->update(['deletetime' => time()]); + }); + self::afterWrite(function ($row) use ($config) { + $changed = $row->getChangedData(); + //隐藏时判断是否有子节点,有则隐藏 + if (isset($changed['status']) && $changed['status'] == 'hidden') { + $childIds = self::getChildrenIds($row['id']); + db('cms_channel')->where('id', 'in', $childIds)->update(['status' => 'hidden']); + } + //隐藏栏目显示时判断是否有子节点 + if (isset($changed['isnav']) && !$changed['isnav']) { + $childIds = self::getChildrenIds($row['id']); + db('cms_channel')->where('id', 'in', $childIds)->update(['isnav' => 0]); + } + //推送到熊掌号+百度站长 + if (isset($changed['status']) && $changed['status'] == 'normal') { + if ($config['baidupush']) { + $urls = [$row->fullurl]; + \think\Hook::listen("baidupush", $urls); + } + } + //刷新栏目统计数据 + if (isset($changed['listtype']) || isset($changed['parent_id'])) { + $origined = $row->getOriginData(); + $refreshIds = [$origined['parent_id'] ?? 0, $row['parent_id'] ?? 0, $row['id'] ?? 0]; + self::refreshItems($refreshIds); + } + //同步配置到子栏目 + if (isset($row['syncconfig'])) { + $childIds = self::getChildrenIds($row['id']); + $data = [ + 'channeltpl' => $row['channeltpl'], + 'listtpl' => $row['listtpl'], + 'showtpl' => $row['showtpl'], + 'listtype' => $row['listtype'], + 'pagesize' => $row['pagesize'], + 'vip' => $row['vip'], + ]; + db('cms_channel')->where('id', 'in', $childIds)->update($data); + } + }); + } + + public static function getTypeList() + { + return ['channel' => __('Channel'), 'list' => __('List'), 'link' => __('Link')]; + } + + public function getFlagList() + { + $config = get_addon_config('cms'); + return $config['flagtype']; + } + + public static function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + public static function getListtypeList() + { + return ['0' => __('自已和所有子级'), '1' => __('自己和一级子级'), '2' => __('仅自己'), '3' => __('仅包含一级子级(不含自己)'), '4' => __('仅包含所有子级(不含自己)')]; + } + + public function getTypeTextAttr($value, $data) + { + $value = $value ? $value : $data['type']; + $list = $this->getTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getLinkdataAttr($value, $data) + { + $result = []; + if (isset($data['linktype']) && isset($data['linkid']) && $data['linktype'] && $data['linkid']) { + $model = Service::getModelByType($data['linktype'], $data['linkid']); + if ($model) { + $result = [ + 'type' => $data['linktype'], + 'source_id' => $data['linkid'], + 'title' => $model['title'] ?? ($model['name'] ?? '未知'), + 'url' => $model['url'] ?? '', + ]; + } + } + return $result; + } + + /** + * 获取栏目的所有子节点ID,无缓存 + * @param int $id 栏目ID + * @param bool $withself 是否包含自身 + * @return array + */ + public static function getChildrenIds($id, $withself = false) + { + static $tree; + if (!$tree) { + $tree = \fast\Tree::instance(); + $tree->init(collection(Channel::order('weigh desc,id desc')->field('id,parent_id,name,type,diyname,status')->select())->toArray(), 'parent_id'); + } + $childIds = $tree->getChildrenIds($id, $withself); + return $childIds; + } + + /** + * 获取栏目的所有父节点ID,无缓存 + * @param int $id 栏目ID + * @param bool $withself 是否包含自身 + * @return array + */ + public static function getParentsIds($id, $withself = false) + { + static $tree; + if (!$tree) { + $tree = \fast\Tree::instance(); + $tree->init(collection(Channel::order('weigh desc,id desc')->field('id,parent_id,name,type,diyname,status')->select())->toArray(), 'parent_id'); + } + $childIds = $tree->getParentsIds($id, $withself); + return $childIds; + } + + /** + * 刷新栏目统计数据 + * @param mixed $ids 栏目ID集合 + * @return bool + */ + public static function refreshItems($ids) + { + $ids = is_array($ids) ? $ids : explode(',', $ids); + $ids = array_filter(array_unique($ids)); + + try { + $channelList = self::where('id', 'in', $ids)->select(); + foreach ($channelList as $index => $channel) { + if ($channel['parent_id']) { + $ids = array_merge($ids, self::getParentsIds($channel['id'])); + } + } + $ids = array_filter(array_unique($ids)); + $channelList = self::where('id', 'in', $ids)->select(); + + foreach ($channelList as $index => $channel) { + $count = Archives::where(function ($query) use ($channel) { + //只统计当前栏目 + $query->where('channel_id', $channel['id']); + + //按栏目列表类型 + //$query->where(function ($query) use ($channel) { + // if ($channel['listtype'] <= 2) { + // $query->whereOr("channel_id", $channel['id']); + // } + // if ($channel['listtype'] == 1 || $channel['listtype'] == 3) { + // $query->whereOr('channel_id', 'in', function ($query) use ($channel) { + // $query->name("cms_channel")->where('parent_id', $channel['id'])->field("id"); + // }); + // } + // if ($channel['listtype'] == 0 || $channel['listtype'] == 4) { + // $childrenIds = self::getChildrenIds($channel['id'], false); + // if ($childrenIds) { + // $query->whereOr('channel_id', 'in', $childrenIds); + // } + // } + //}); + + //副栏目 + //$query->whereOr("(`channel_ids`!='' AND FIND_IN_SET('{$channel['id']}', `channel_ids`))"); + }) + ->where('status', 'normal') + ->whereNull('deletetime') + ->count(); + $channel->save(['items' => $count]); + } + + } catch (\Exception $e) { + \think\Log::record($e->getMessage()); + return false; + } + return true; + } + + public function model() + { + return $this->belongsTo('Modelx', 'model_id')->setEagerlyType(0); + } + + public function getSettingAttr($value, $data) + { + return is_array($value) ? $value : (array)json_decode($data['setting'], true); + } +} diff --git a/application/admin/model/cms/ChannelAdmin.php b/application/admin/model/cms/ChannelAdmin.php new file mode 100644 index 0000000..bd51632 --- /dev/null +++ b/application/admin/model/cms/ChannelAdmin.php @@ -0,0 +1,38 @@ +id; + $selected = ChannelAdmin::where('admin_id', $admin_id)->column('channel_id'); + return $selected; + } + + public function admin() + { + return $this->belongsTo('\app\admin\model\Admin', 'admin_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + public function channel() + { + return $this->belongsTo('\app\admin\model\cms\Channel', 'channel_id', 'id', [], 'LEFT')->setEagerlyType(0); + } +} diff --git a/application/admin/model/cms/Comment.php b/application/admin/model/cms/Comment.php new file mode 100644 index 0000000..fde3bdd --- /dev/null +++ b/application/admin/model/cms/Comment.php @@ -0,0 +1,159 @@ +getChangedData(); + if (isset($changedData['status']) && isset($config['score']['postcomment'])) { + if ($changedData['status'] == 'normal') { + User::score($config['score']['postcomment'], $row['user_id'], '发表评论'); + } else { + User::score(-$config['score']['postcomment'], $row['user_id'], '删除评论'); + } + } + self::refreshSourceComments($row['id']); + }); + self::afterDelete(function ($row) use ($config) { + $data = Comment::withTrashed()->where('id', $row['id'])->find(); + if ($data) { + if ($data['status'] == 'normal' && isset($config['score']['postcomment'])) { + User::score(-$config['score']['postcomment'], $row['user_id'], '删除评论'); + } + } + self::refreshSourceComments($row['id']); + }); + } + + /** + * 刷新评论 + * @param int $id 评论ID + * @return bool + */ + public static function refreshSourceComments($id) + { + $row = self::withTrashed()->where('id', $id)->find(); + if (!$row) { + return false; + } + $model = $row->source; + if ($model && $model instanceOf \think\Model) { + $comments = self::where('type', $row['type'])->where('aid', $row['aid'])->where('status', 'normal')->count(); + $model->save(['comments' => $comments]); + } + return true; + } + + public function getTypeList() + { + return ['archives' => __('Archives'), 'page' => __('Page'), 'special' => __('Special')]; + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getTypeTextAttr($value, $data) + { + $value = $value ? $value : $data['type']; + $list = $this->getTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + /** + * 根据类型和ID删除 + */ + public static function deleteByType($type, $aid, $force = false) + { + if (!$force) { + //删除评论 + $commentList = Comment::where(['type' => $type, 'aid' => $aid])->select(); + foreach ($commentList as $index => $item) { + $item->delete(); + } + } else { + //强制删除评论 + $commentList = Comment::withTrashed()->where(['type' => $type, 'aid' => $aid])->select(); + foreach ($commentList as $index => $item) { + $item->delete(true); + } + } + } + + public function user() + { + return $this->belongsTo('\app\common\model\User', 'user_id', '', [], 'LEFT')->setEagerlyType(0); + } + + /** + * 关联文档模型 + */ + public function archives() + { + return $this->belongsTo('Archives', 'aid', '', [], 'LEFT')->setEagerlyType(0); + } + + /** + * 关联单页模型 + */ + public function spage() + { + return $this->belongsTo("addons\cms\model\Page", 'aid', '', [], 'LEFT')->setEagerlyType(0); + } + + /** + * 关联专题模型 + */ + public function special() + { + return $this->belongsTo("addons\cms\model\Special", 'aid', '', [], 'LEFT')->setEagerlyType(0); + } + + /** + * 关联模型 + */ + public function source() + { + $type = $this->getData('type'); + $modelArr = ['page' => 'Page', 'archives' => 'Archives', 'special' => 'Special']; + $model = isset($modelArr[$type]) ? $modelArr[$type] : $modelArr['archives']; + return $this->belongsTo($model, "aid"); + } +} diff --git a/application/admin/model/cms/Diyform.php b/application/admin/model/cms/Diyform.php new file mode 100644 index 0000000..a17329e --- /dev/null +++ b/application/admin/model/cms/Diyform.php @@ -0,0 +1,192 @@ +find(); + if ($exist) { + throw new Exception("已经存在相同表名的模型"); + } + $info = null; + try { + $info = Db::name($row['table'])->getTableInfo(); + } catch (\Exception $e) { + } + if ($info) { + throw new Exception("数据表已经存在"); + } + }); + self::afterInsert(function ($row) { + $prefix = Config::get('database.prefix'); + $sql = "CREATE TABLE `{$prefix}{$row['table']}` ( + `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` int(10) DEFAULT NULL COMMENT '会员ID', + `title` varchar(255) DEFAULT '' COMMENT '标题', + `images` varchar(1500) DEFAULT '' COMMENT '图片', + `content` text DEFAULT NULL COMMENT '内容', + `createtime` bigint(16) DEFAULT NULL COMMENT '添加时间', + `updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间', + `memo` varchar(1500) DEFAULT '' COMMENT '备注', + `status` enum('normal','hidden','rejected') DEFAULT 'hidden' COMMENT '状态', + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `createtime` (`createtime`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{$row['name']}'"; + try { + Db::execute($sql); + } catch (\Exception $e) { + throw new Exception("发生错误:" . $e->getMessage()); + } + $time = time(); + $fieldsArr = [ + [ + "source" => "diyform", + "source_id" => $row['id'], + "name" => "title", + "type" => "string", + "title" => "标题", + "content" => "value1|title1\r\nvalue2|title2", + "length" => 255, + "iscontribute" => 1, + "isfilter" => 0, + "weigh" => 3, + "defaultvalue" => '', + "createtime" => $time, + "updatetime" => $time, + "status" => "normal" + ], + [ + "source" => "diyform", + "source_id" => $row['id'], + "name" => "images", + "type" => "images", + "title" => "图片", + "content" => "value1|title1\r\nvalue2|title2", + "length" => 1500, + "iscontribute" => 1, + "isfilter" => 0, + "weigh" => 2, + "defaultvalue" => '', + "createtime" => $time, + "updatetime" => $time, + "status" => "normal" + ], + [ + "source" => "diyform", + "source_id" => $row['id'], + "name" => "content", + "type" => "editor", + "title" => "内容", + "content" => "value1|title1\r\nvalue2|title2", + "length" => 0, + "iscontribute" => 1, + "isfilter" => 0, + "weigh" => 1, + "defaultvalue" => '', + "createtime" => $time, + "updatetime" => $time, + "status" => "normal" + ], + ]; + Db::name("cms_fields")->insertAll($fieldsArr); + }); + } + + public function getUrlAttr($value, $data) + { + return $this->buildUrl($value, $data); + } + + public function getFullurlAttr($value, $data) + { + return $this->buildUrl($value, $data, true); + } + + private function buildUrl($value, $data, $domain = false) + { + $diyname = isset($data['diyname']) && $data['diyname'] ? $data['diyname'] : $data['id']; + $time = $data['createtime'] ?? time(); + + $vars = [ + ':id' => $data['id'], + ':diyname' => $diyname, + ':year' => date("Y", $time), + ':month' => date("m", $time), + ':day' => date("d", $time) + ]; + return addon_url('cms/diyform/index', $vars, static::$config['urlsuffix'], $domain); + } + + public function getPosturlAttr($value, $data) + { + $diyname = isset($data['diyname']) && $data['diyname'] ? $data['diyname'] : $data['id']; + $time = $data['createtime'] ?? time(); + + $vars = [ + ':id' => $data['id'], + ':diyname' => $diyname, + ':year' => date("Y", $time), + ':month' => date("m", $time), + ':day' => date("d", $time) + ]; + return addon_url('cms/diyform/post', $vars, static::$config['urlsuffix']); + } + + public function getFieldsAttr($value, $data) + { + return is_array($value) ? $value : ($value ? explode(',', $value) : []); + } + + public function getSettingAttr($value, $data) + { + return is_array($value) ? $value : (array)json_decode($data['setting'], true); + } + + public function setSettingAttr($value) + { + return is_array($value) ? json_encode($value) : $value; + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } +} diff --git a/application/admin/model/cms/Fields.php b/application/admin/model/cms/Fields.php new file mode 100644 index 0000000..ac5e5f2 --- /dev/null +++ b/application/admin/model/cms/Fields.php @@ -0,0 +1,185 @@ + 'json', + ]; + protected static $listField = ['select', 'selects', 'checkbox', 'radio', 'array', 'selectpage', 'selectpages']; + + public function setError($error) + { + $this->error = $error; + } + + protected static function init() + { + $beforeUpdateCallback = function ($row) { + $changedData = $row->getChangedData(); + if (isset($changedData['name'])) { + if (!preg_match("/^([a-zA-Z0-9_]+)$/i", $row['name'])) { + throw new Exception("字段只支持字母数字下划线"); + } + if (is_numeric(substr($row['name'], 0, 1))) { + throw new Exception("字段不能以数字开始"); + } + + if ($row['source'] == 'model') { + $tableFields = \think\Db::name('cms_archives')->getTableFields(); + if (in_array(strtolower($row['name']), $tableFields)) { + throw new Exception("字段已经在主表存在了"); + } + if (in_array($row['name'], ['id', 'content'])) { + throw new Exception("字段已经存在"); + } + } elseif (in_array($row['source'], ['channel', 'page', 'special', 'block'])) { + //栏目、单页、专题、区块需过滤主表字段 + $tableFields = \think\Db::name('cms_' . $row['source'])->getTableFields(); + $customFieldList = Service::getCustomFields($row['source'], 0); + $tableFields = array_diff($tableFields, array_map(function ($field) { + return $field['name']; + }, collection($customFieldList)->toArray())); + if (in_array(strtolower($row['name']), $tableFields)) { + throw new Exception("字段已经在表中存在了"); + } + } elseif ($row['source'] == 'diyform') { + $tableFields = ['id', 'user_id', 'createtime', 'updatetime', 'memo', 'status']; + if (in_array(strtolower($row['name']), $tableFields)) { + throw new Exception("字段已经存在"); + } + } else { + $tableFields = ['id', 'user_id', 'type', 'createtime', 'updatetime']; + if (in_array(strtolower($row['name']), $tableFields)) { + throw new Exception("字段为保留字段,请使用其它字段"); + } + } + $vars = array_keys(get_class_vars('\think\Model')); + $vars = array_map('strtolower', $vars); + $vars = array_merge($vars, ['url', 'fullurl']); + if (in_array(strtolower($row['name']), $vars)) { + throw new Exception("字段为模型保留字段,请使用其它字段"); + } + } + }; + + $afterInsertCallback = function ($row) { + //为了避免引起更新的事件回调,这里采用直接执行SQL的写法 + Db::name('cms_fields')->update(['id' => $row['id'], 'weigh' => $row['id']]); + Fields::refreshTable($row, 'insert'); + }; + $afterUpdateCallback = function ($row) { + Fields::refreshTable($row, 'update'); + }; + + self::beforeInsert($beforeUpdateCallback); + self::beforeUpdate($beforeUpdateCallback); + + self::afterInsert($afterInsertCallback); + self::afterUpdate($afterUpdateCallback); + + self::afterDelete(function ($row) { + Fields::refreshTable($row, 'delete'); + }); + } + + public function getContentListAttr($value, $data) + { + return in_array($data['type'], self::$listField) ? Config::decode($data['content']) : $data['content']; + } + + public static function getContributeFields() + { + return ["channel_ids", "image", "images", "tags", "price", "outlink", "content", "keywords", "description"]; + } + + public static function getPublishFields() + { + return ["channel_ids", "user_id", "special_ids", "image", "images", "diyname", "tags", "price", "outlink", "content", "seotitle", "keywords", "description"]; + } + + public static function refreshTable($row, $action = 'insert') + { + $model = null; + if (in_array($row['source'], ['model', 'diyform'])) { + $model = $row['source'] == 'model' ? Modelx::get($row['source_id']) : Diyform::get($row['source_id']); + if (!$model) { + throw new Exception("未找到指定模型"); + } + $table = $model['table']; + } elseif (in_array($row['source'], ['channel', 'page', 'special', 'block'])) { + $table = "cms_" . $row['source']; + } else { + throw new Exception("未找到指定模型"); + } + + $alter = Alter::instance(); + if (isset($row['oldname']) && $row['oldname'] != $row['name']) { + $alter->setOldname($row['oldname']); + } + $alter + ->setTable($table) + ->setName($row['name']) + ->setLength($row['length']) + ->setContent($row['content']) + ->setDecimals($row['decimals']) + ->setDefaultvalue($row['defaultvalue']) + ->setComment($row['title']) + ->setType($row['type']); + if ($action == 'insert') { + $sql = $alter->getAddSql(); + } elseif ($action == 'update') { + $sql = $alter->getModifySql(); + } elseif ($action == 'delete') { + $sql = $alter->getDropSql(); + } else { + throw new Exception("操作类型错误"); + } + //变更模型数据 + if ($model) { + $fields = Fields::where('source', $row['source'])->where('source_id', $row['source_id'])->field('name')->column('name'); + $model->fields = implode(',', $fields); + $model->save(); + } + try { + db()->execute($sql); + } catch (PDOException $e) { + throw new Exception($e->getMessage()); + } + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + +} diff --git a/application/admin/model/cms/Leaguearchives.php b/application/admin/model/cms/Leaguearchives.php new file mode 100644 index 0000000..22eae8f --- /dev/null +++ b/application/admin/model/cms/Leaguearchives.php @@ -0,0 +1,40 @@ + __('Status Normal'), 'hidden' => __('Status Hidden'), 'rejected' => __('Status rejected'), 'pulloff' => __('Status pulloff')]; + // } +} diff --git a/application/admin/model/cms/Modelx.php b/application/admin/model/cms/Modelx.php new file mode 100644 index 0000000..b3ff977 --- /dev/null +++ b/application/admin/model/cms/Modelx.php @@ -0,0 +1,85 @@ + Fields::getContributeFields(), + 'publishfields' => Fields::getPublishFields(), + ]; + $row['setting'] = json_encode($setting); + if (!preg_match("/^([a-z0-9_]+)$/", $row['table'])) { + throw new Exception("表名只支持小写字母、数字、下划线"); + } + $exist = Modelx::where('table', $row['table'])->find(); + if ($exist) { + throw new Exception("已经存在相同表名的模型"); + } + $info = null; + try { + $info = Db::name($row['table'])->getTableInfo(); + } catch (\Exception $e) { + } + if ($info) { + throw new Exception("数据表已经存在"); + } + }); + self::afterInsert(function ($row) { + $prefix = Config::get('database.prefix'); + $sql = "CREATE TABLE `{$prefix}{$row['table']}` (`id` int(10) NOT NULL,`content` longtext NOT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{$row['name']}'"; + try { + Db::execute($sql); + } catch (\Exception $e) { + throw new Exception("发生错误:" . $e->getMessage()); + } + }); + //存在栏目无法删除 + self::beforeDelete(function ($row) { + $exist = Channel::where('model_id', $row['id'])->find(); + if ($exist) { + throw new Exception("模型下存在栏目,无法进行删除"); + } + }); + //删除模型后删除对应的表字段 + self::afterDelete(function ($row) { + Db::name("cms_fields")->where(['source' => 'model', 'source_id' => $row['id']])->delete(); + }); + } + + public function getFieldsAttr($value, $data) + { + return is_array($value) ? $value : ($value ? explode(',', $value) : []); + } + + public function getSettingAttr($value, $data) + { + return is_array($value) ? $value : (array)json_decode($data['setting'], true); + } + + public function setSettingAttr($value) + { + return is_array($value) ? json_encode($value) : $value; + } +} diff --git a/application/admin/model/cms/Order.php b/application/admin/model/cms/Order.php new file mode 100644 index 0000000..a56bc56 --- /dev/null +++ b/application/admin/model/cms/Order.php @@ -0,0 +1,57 @@ + __('Created'), 'paid' => __('Paid'), 'expired' => __('Expired')]; + } + + public function getPaytimeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['paytime']) ? $data['paytime'] : ''); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + protected function setPaytimeAttr($value) + { + return $value && !is_numeric($value) ? strtotime($value) : $value; + } + + public function user() + { + return $this->belongsTo("\app\common\model\User", 'user_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + public function archives() + { + return $this->belongsTo("Archives", 'archives_id', 'id', [], 'LEFT')->setEagerlyType(0); + } +} diff --git a/application/admin/model/cms/Page.php b/application/admin/model/cms/Page.php new file mode 100644 index 0000000..e190cd9 --- /dev/null +++ b/application/admin/model/cms/Page.php @@ -0,0 +1,120 @@ +getData() as $k => $value) { + if (is_array($value) && is_array(reset($value))) { + $value = json_encode(self::getArrayData($value), JSON_UNESCAPED_UNICODE); + } else { + $value = is_array($value) ? implode(',', $value) : $value; + } + $row->setAttr($k, $value); + } + }); + self::afterInsert(function ($row) { + $row->save(['weigh' => $row['id']]); + }); + self::afterDelete(function ($row) { + $data = Page::withTrashed()->find($row['id']); + //删除评论 + Comment::deleteByType('page', $row['id'], !$data); + }); + } + + public function getUrlAttr($value, $data) + { + return $this->buildUrl($value, $data); + } + + public function getFullurlAttr($value, $data) + { + return $this->buildUrl($value, $data, true); + } + + private function buildUrl($value, $data, $domain = false) + { + $diyname = isset($data['diyname']) && $data['diyname'] ? $data['diyname'] : $data['id']; + $time = $data['createtime'] ?? time(); + + $vars = [ + ':id' => $data['id'], + ':diyname' => $diyname, + ':year' => date("Y", $time), + ':month' => date("m", $time), + ':day' => date("d", $time) + ]; + return addon_url('cms/page/index', $vars, static::$config['urlsuffix'], $domain); + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getFlagList() + { + $config = get_addon_config('cms'); + return $config['flagtype']; + } + + public function getFlagTextAttr($value, $data) + { + $value = $value ? $value : $data['flag']; + $valueArr = explode(',', $value); + $list = $this->getFlagList(); + return implode(',', array_intersect_key($list, array_flip($valueArr))); + } + + protected function setKeywordsAttr($value) + { + return str_replace([" ", "\r\n", "\r", "\n"], "", strip_tags($value)); + } + + protected function setDescriptionAttr($value) + { + return str_replace([" ", "\r\n", "\r", "\n"], "", strip_tags($value)); + } +} diff --git a/application/admin/model/cms/SearchLog.php b/application/admin/model/cms/SearchLog.php new file mode 100644 index 0000000..40c7f0c --- /dev/null +++ b/application/admin/model/cms/SearchLog.php @@ -0,0 +1,40 @@ + __('Normal'), 'hidden' => __('Hidden')]; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : $data['status']; + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + +} diff --git a/application/admin/model/cms/Special.php b/application/admin/model/cms/Special.php new file mode 100644 index 0000000..3c51044 --- /dev/null +++ b/application/admin/model/cms/Special.php @@ -0,0 +1,198 @@ +getData() as $k => $value) { + if (is_array($value) && is_array(reset($value))) { + $value = json_encode(self::getArrayData($value), JSON_UNESCAPED_UNICODE); + } else { + $value = is_array($value) ? implode(',', $value) : $value; + } + $row->setAttr($k, $value); + } + + if (isset($row['tag_ids'])) { + $tagIds = array_filter(explode(',', $row['tag_ids'])); + if ($tagIds) { + foreach ($tagIds as $index => &$tagId) { + $tag = Tag::get($tagId); + if (!$tag && !is_numeric($tagId)) { + $data = [ + 'name' => $tagId, + ]; + $tag = Tag::create($data); + $tagId = $tag->id; + } + } + $row->tag_ids = implode(',', $tagIds); + } + } + }); + self::afterWrite(function ($row) use ($config) { + $changedData = $row->getChangedData(); + if (isset($changedData['status']) && $changedData['status'] == 'normal') { + //推送到熊掌号+百度站长 + if ($config['baidupush']) { + $urls = [$row->fullurl]; + \think\Hook::listen("baidupush", $urls); + } + } + + $oldArchivesIds = self::getArchivesIds($row['id']); + if (isset($row['archives_ids'])) { + $newArchivesIds = explode(",", $row['archives_ids']); + $remainIds = array_diff($newArchivesIds, $oldArchivesIds); + $removeIds = array_diff($oldArchivesIds, $newArchivesIds); + + $archivesList = \addons\cms\model\Archives::where('id', 'in', array_merge($remainIds, $removeIds))->select(); + foreach ($archivesList as $index => $item) { + $ids = explode(',', $item['special_ids']); + if (in_array($item['id'], $remainIds)) { + $ids[] = $row['id']; + } + if (in_array($item['id'], $removeIds)) { + $ids = array_diff($ids, [$row['id']]); + } + $item->save(['special_ids' => implode(',', array_unique(array_filter($ids)))]); + } + } + }); + + self::afterDelete(function ($row) { + $data = Special::withTrashed()->find($row['id']); + //删除评论 + Comment::deleteByType('special', $row['id'], !$data); + }); + } + + public function getUrlAttr($value, $data) + { + return $this->buildUrl($value, $data); + } + + public function getFullurlAttr($value, $data) + { + return $this->buildUrl($value, $data, true); + } + + private function buildUrl($value, $data, $domain = false) + { + $diyname = isset($data['diyname']) && $data['diyname'] ? $data['diyname'] : $data['id']; + $time = $data['createtime'] ?? time(); + + $vars = [ + ':id' => $data['id'], + ':diyname' => $diyname, + ':year' => date("Y", $time), + ':month' => date("m", $time), + ':day' => date("d", $time) + ]; + return addon_url('cms/special/index', $vars, static::$config['urlsuffix'], $domain); + } + + public function getFlagList() + { + $config = get_addon_config('cms'); + return $config['flagtype']; + } + + public function getStatusList() + { + return ['normal' => __('Normal'), 'hidden' => __('Hidden')]; + } + + + public function getFlagTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['flag']) ? $data['flag'] : ''); + $valueArr = explode(',', $value); + $list = $this->getFlagList(); + return implode(',', array_intersect_key($list, array_flip($valueArr))); + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + protected function setFlagAttr($value) + { + return is_array($value) ? implode(',', $value) : $value; + } + + protected function setKeywordsAttr($value) + { + return str_replace([" ", "\r\n", "\r", "\n"], "", strip_tags($value)); + } + + protected function setDescriptionAttr($value) + { + return str_replace([" ", "\r\n", "\r", "\n"], "", strip_tags($value)); + } + + /** + * 获取专题文档集合 + */ + public static function getArchivesIds($special_id) + { + $ids = Archives::whereRaw("FIND_IN_SET('{$special_id}', `special_ids`)")->column('id'); + return $ids; + } + + /** + * 获取专题的文档ID集合 + */ + public function getArchivesIdsAttr($value, $data) + { + if (isset($data['archives_ids'])) { + return $data['archives_ids']; + } + $ids = Special::getArchivesIds($data['id']); + return implode(',', $ids); + } + + +} diff --git a/application/admin/model/cms/SpiderLog.php b/application/admin/model/cms/SpiderLog.php new file mode 100644 index 0000000..d45a52c --- /dev/null +++ b/application/admin/model/cms/SpiderLog.php @@ -0,0 +1,124 @@ + __('Index'), 'archives' => __('Archives'), 'page' => __('Page'), 'special' => __('Special'), 'channel' => __('Channel'), 'diyform' => __('Diyform'), 'tag' => __('Tag'), 'user' => __('User')]; + } + + public function getTypeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['type']) ? $data['type'] : ''); + $list = $this->getTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getLastdataTextAttr($value, $data) + { + $lastdataArr = explode(',', $data['lastdata']); + foreach ($lastdataArr as $index => &$item) { + $item = date("Y-m-d H:i:s", $item); + } + return implode(',', $lastdataArr); + } + + + public function getFirsttimeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['firsttime']) ? $data['firsttime'] : ''); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + + public function getLasttimeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['lasttime']) ? $data['lasttime'] : ''); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + protected function setFirsttimeAttr($value) + { + return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); + } + + protected function setLasttimeAttr($value) + { + return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); + } + + /** + * 渲染来访记录 + * @param mixed $list + * @param string $type + */ + public static function render(&$list, $type) + { + if (!$list) { + return; + } + $ids = []; + foreach ($list as $index => $item) { + $ids[] = $item['id']; + } + $config = get_addon_config('cms'); + $spiderDict = $config['spiders'] ?? []; + $spiderFollowArr = array_filter(explode(',', $config['spiderfollow'] ?? '')); + if (!$spiderFollowArr) { + return; + } + $today = Date::unixtime(); + $spiderLogList = \addons\cms\model\SpiderLog::where('type', $type) + ->where('aid', 'in', $ids) + ->where('name', 'in', $spiderFollowArr) + ->field('aid,name,lasttime') + ->select(); + $logs = []; + $spiderLogList = $spiderLogList ? collection($spiderLogList)->toArray() : []; + foreach ($spiderLogList as $index => $item) { + $logs[$item['aid']] = $logs[$item['aid']] ?? []; + $logs[$item['aid']][$item['name']] = $item['lasttime']; + } + foreach ($list as $index => &$row) { + $spiders = []; + foreach ($spiderFollowArr as $key => $name) { + $status = !isset($logs[$row['id']][$name]) ? 'none' : ($logs[$row['id']][$name] > $today ? 'today' : 'pass'); + $date = !isset($logs[$row['id']][$name]) ? '' : $logs[$row['id']][$name]; + $spiders[$name] = ['name' => $name, 'title' => isset($spiderDict[$name]) ? $spiderDict[$name] . (stripos($spiderDict[$name], '搜索') === false ? '搜索' : '') : '未知', 'status' => $status, 'date' => $date ? date("Y-m-d", $date) : '']; + } + $row['spiders'] = array_values($spiders); + } + return; + } + +} diff --git a/application/admin/model/cms/Tag.php b/application/admin/model/cms/Tag.php new file mode 100644 index 0000000..5d49ec7 --- /dev/null +++ b/application/admin/model/cms/Tag.php @@ -0,0 +1,91 @@ +getChangedData(); + if (isset($changedData['name'])) { + $exists = \think\Db::name("cms_tag")->where('name', $changedData['name'])->where('id', '<>', $row['id'])->find(); + if ($exists) { + throw new Exception("标签已存在"); + } + } + }); + self::afterDelete(function ($row) { + //删除关联的TAG数据 + Taggable::where('tag_id', $row['id'])->delete(); + }); + self::afterUpdate(function ($row) { + $originData = $row->getOriginData(); + $changedData = $row->getChangedData(); + + if (isset($changedData['name'])) { + $value = $originData['name']; + $replacement = $changedData['name']; + $archivesList = \think\Db::name("cms_archives")->where('id', 'in', function ($query) use ($row) { + $query->name("cms_taggable")->where('tag_id', $row['id'])->field('archives_id'); + })->select(); + foreach ($archivesList as $index => $item) { + $tagsArr = explode(',', $item['tags']); + $tagsArr = array_map(function ($v) use ($value, $replacement) { + return $v == $value ? $replacement : $v; + }, $tagsArr); + \think\Db::name("cms_archives")->where('id', $item['id'])->update(['tags' => implode(',', $tagsArr)]); + } + } + }); + } + + public function getOriginData() + { + return $this->origin; + } + + public function getUrlAttr($value, $data) + { + return $this->buildUrl($value, $data); + } + + public function getFullurlAttr($value, $data) + { + return $this->buildUrl($value, $data, true); + } + + private function buildUrl($value, $data, $domain = false) + { + $diyname = isset($data['name']) && $data['name'] ? $data['name'] : $data['id']; + $time = $data['createtime'] ?? time(); + + $vars = [ + ':id' => $data['id'], + ':name' => $diyname, + ':diyname' => $diyname, + ':year' => date("Y", $time), + ':month' => date("m", $time), + ':day' => date("d", $time) + ]; + return addon_url('cms/tag/index', $vars, static::$config['urlsuffix'], $domain); + } +} diff --git a/application/admin/model/cms/Taggable.php b/application/admin/model/cms/Taggable.php new file mode 100644 index 0000000..aaf8920 --- /dev/null +++ b/application/admin/model/cms/Taggable.php @@ -0,0 +1,26 @@ +where('name', 'configgroup')->value('value'); + $configGroupTemp = json_decode($configGroupTemp, true); + foreach ($configGroupTemp as $k => $v) { + $configGroup[$k]['name'] = $k; + $configGroup[$k]['title'] = __($v); + $configGroup[$k]['list'] = []; + } + return $configGroup; + } + + /** + * 按分组读取配置列表 + * @param int $userSettings 是否是用户设置 + * @param int $userExclusive 是否是用户专有设置 + * @param int $uniappAvailable 是否是uniapp专有设置 + * @return array + */ + public static function getConfigListByGroup($userSettings, $userExclusive, $uniappAvailable = null) + { + $configList = self::getConfigGroup(); + + $where = []; + if ($userSettings !== null) { + $where['user_settings'] = $userSettings; + } + if ($userExclusive !== null) { + $where['user_exclusive'] = $userExclusive; + } + if ($uniappAvailable !== null) { + $where['uniapp_available'] = $uniappAvailable; + } + + $imConfig = collection(self::where($where)->order('weigh desc,id desc')->select())->toArray(); + foreach ($imConfig as $key => $value) { + if (!isset($configList[$value['group']]) || $value['name'] == 'configgroup') { + continue; + } + $value['title'] = __($value['title']); + if (in_array($value['type'], ['select', 'selects', 'checkbox'])) { + $value['value'] = explode(',', $value['value']); + } + $value['content'] = json_decode($value['content'], true); + $value['tip'] = htmlspecialchars($value['tip']); + $configList[$value['group']]['list'][] = $value; + } + + $index = 0; + foreach ($configList as $key => &$value) { + if (count($value['list'])) { + $value['active'] = !$index ? true : false; + $index++; + } else { + unset($configList[$key]); + } + } + return $configList; + } + + public static function getArrayData($data) + { + if (!isset($data['value'])) { + $result = []; + foreach ($data as $index => $datum) { + $result['field'][$index] = $datum['key']; + $result['value'][$index] = $datum['value']; + } + $data = $result; + } + $fieldarr = $valuearr = []; + $field = isset($data['field']) ? $data['field'] : (isset($data['key']) ? $data['key'] : []); + $value = isset($data['value']) ? $data['value'] : []; + foreach ($field as $m => $n) { + if ($n != '') { + $fieldarr[] = $field[$m]; + $valuearr[] = $value[$m]; + } + } + return $fieldarr ? array_combine($fieldarr, $valuearr) : []; + } +} \ No newline at end of file diff --git a/application/admin/model/fastim/CsrGroup.php b/application/admin/model/fastim/CsrGroup.php new file mode 100644 index 0000000..0385cb7 --- /dev/null +++ b/application/admin/model/fastim/CsrGroup.php @@ -0,0 +1,59 @@ +getPk(); + $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]); + }); + } + + + public function getStatusList() + { + return ['0' => __('Status 0'), '1' => __('Status 1')]; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getCsrNumberAttr($value, $data) + { + return \think\Db::name('fastim_user')->where('type', 'csr')->where('group_id', $data['id'])->count('id'); + } + +} diff --git a/application/admin/model/fastim/FastReply.php b/application/admin/model/fastim/FastReply.php new file mode 100644 index 0000000..ba10455 --- /dev/null +++ b/application/admin/model/fastim/FastReply.php @@ -0,0 +1,49 @@ + __('Status 0'), '1' => __('Status 1')]; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function user() + { + return $this->belongsTo('app\admin\model\fastim\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0); + } +} diff --git a/application/admin/model/fastim/Groupchat.php b/application/admin/model/fastim/Groupchat.php new file mode 100644 index 0000000..cf51d44 --- /dev/null +++ b/application/admin/model/fastim/Groupchat.php @@ -0,0 +1,86 @@ + __('Add_mode 0'), '1' => __('Add_mode 1')]; + } + + public function getInviteJoinGroupList() + { + return ['0' => __('Invite_join_group 0'), '1' => __('Invite_join_group 1')]; + } + + public function getSpeakList() + { + return ['0' => __('Speak 0'), '1' => __('Speak 1')]; + } + + public function getHistoryMessageList() + { + return ['0' => __('History_message 0'), '1' => __('History_message 1')]; + } + + + public function getAddModeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['add_mode']) ? $data['add_mode'] : ''); + $list = $this->getAddModeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getInviteJoinGroupTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['invite_join_group']) ? $data['invite_join_group'] : ''); + $list = $this->getInviteJoinGroupList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getSpeakTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['speak']) ? $data['speak'] : ''); + $list = $this->getSpeakList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getHistoryMessageTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['history_message']) ? $data['history_message'] : ''); + $list = $this->getHistoryMessageList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function fastimuser() + { + return $this->belongsTo('app\admin\model\fastim\User', 'leader', 'id', [], 'LEFT')->setEagerlyType(0); + } +} diff --git a/application/admin/model/fastim/Kbs.php b/application/admin/model/fastim/Kbs.php new file mode 100644 index 0000000..88b41be --- /dev/null +++ b/application/admin/model/fastim/Kbs.php @@ -0,0 +1,54 @@ +getPk(); + $row->getQuery()->where($pk, $row[$pk])->update(['weigh' => $row[$pk]]); + }); + } + + + public function getStatusList() + { + return ['0' => __('Status 0'), '1' => __('Status 1')]; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + +} diff --git a/application/admin/model/fastim/Records.php b/application/admin/model/fastim/Records.php new file mode 100644 index 0000000..49c28e2 --- /dev/null +++ b/application/admin/model/fastim/Records.php @@ -0,0 +1,107 @@ + __('Type default'), 'image' => __('Type image'), 'audio' => __('Type audio'), 'video' => __('Type video'), 'file' => __('Type file'), 'link' => __('Type link'), 'system' => __('Type system'), 'group_apply' => __('Type group_apply'), 'group_notice' => __('Type group_notice'), 'group_invitation' => __('Type group_invitation'), 'friend_apply' => __('Type friend_apply'), 'kbs_list' => __('Type kbs_list'), 'group_chat_notice' => __('Type group_chat_notice'), 'voice' => __('Type voice')]; + } + + public function getStatusList() + { + return ['0' => __('Status 0'), '1' => __('Status 1'), '2' => __('Status 2'), '3' => __('Status 3'), '4' => __('Status 4')]; + } + + public function getFormatMessageAttr($value, $data) + { + if (in_array($data['type'], CommonCode::$messageJsonTypes)) { + $message = json_decode($data['message'], true); + if ($data['type'] == 'group_invitation') { + $invitationUser = Common::getImUserInfo($message['invitation_user']); + $message['message'] = $invitationUser['nickname'] . '邀请接受人入群'; + } elseif ($data['type'] == 'group_notice') { + if ($message['action'] == 'quit') { + $userInfo = Common::getImUserInfo($message['quit_user']); + $message['message'] = $userInfo['nickname'] . '退出了群聊'; + } elseif ($message['action'] == 'dismiss') { + $message['message'] = $message['notice']; + } elseif ($message['action'] == 'refuse') { + $info = Common::getImGroupChat($data['group_id']); + $userInfo = Common::getImUserInfo($message['refuse_user']); + if ($data['sender_id'] == $info['leader']) { + $message['message'] = '管理员拒绝' . $userInfo['nickname'] . '加入群聊'; + } else { + $message['message'] = $userInfo['nickname'] . '拒绝加入群聊'; + } + } elseif ($message['action'] == 'propose') { + $message['message'] = '管理员将接收人请出群聊'; + } else { + $message['message'] = $message['notice'] ? $message['notice'] : $message['message'] ?? ''; + } + } + return $message; + } else { + return htmlspecialchars_decode($data['message']); + } + } + + + public function getTypeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['type']) ? $data['type'] : ''); + $list = $this->getTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function groupchat() + { + return $this->belongsTo('app\admin\model\fastim\Groupchat', 'group_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + public function sender() + { + return $this->belongsTo('app\admin\model\fastim\User', 'sender_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + public function recipient() + { + return $this->belongsTo('app\admin\model\fastim\User', 'recipient_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + +} diff --git a/application/admin/model/fastim/Report.php b/application/admin/model/fastim/Report.php new file mode 100644 index 0000000..2d8cda1 --- /dev/null +++ b/application/admin/model/fastim/Report.php @@ -0,0 +1,83 @@ + __('Type user'), 'group' => __('Type group'), 'feedback' => __('Type feedback')]; + } + + public function getStatusList() + { + return ['0' => __('Status 0'), '1' => __('Status 1')]; + } + + + public function getTypeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['type']) ? $data['type'] : ''); + $list = $this->getTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + /** + * 被举报人 + * @return [type] [description] + */ + public function user() + { + return $this->belongsTo('app\admin\model\fastim\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + /** + * 举报人 + * @return [type] [description] + */ + public function reportuser() + { + return $this->belongsTo('app\admin\model\fastim\User', 'report_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + /** + * 被举报群聊 + * @return [type] [description] + */ + public function chatgroup() + { + return $this->belongsTo('app\admin\model\fastim\Groupchat', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0); + } +} diff --git a/application/admin/model/fastim/Session.php b/application/admin/model/fastim/Session.php new file mode 100644 index 0000000..b3567c0 --- /dev/null +++ b/application/admin/model/fastim/Session.php @@ -0,0 +1,65 @@ + __('Type single'), 'group' => __('Type group'), 'service' => __('Type service')]; + } + + + public function getTypeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['type']) ? $data['type'] : ''); + $list = $this->getTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getChatInfoAttr($value, $data) + { + $info = false; + if ($data['type'] == 'group') { + $info = Common::getImGroupChat($data['chat_id']); + } elseif ($data['type'] == 'service') { + $info = \think\Db::name('fastim_service')->where('id', $data['chat_id'])->find(); + } + + return $info; + } + + public function user() + { + return $this->belongsTo('app\admin\model\fastim\User', 'user_one', 'id', [], 'LEFT')->setEagerlyType(0); + } + + public function usertwo() + { + return $this->belongsTo('app\admin\model\fastim\User', 'user_two', 'id', [], 'LEFT')->setEagerlyType(0); + } +} diff --git a/application/admin/model/fastim/User.php b/application/admin/model/fastim/User.php new file mode 100644 index 0000000..ea90178 --- /dev/null +++ b/application/admin/model/fastim/User.php @@ -0,0 +1,82 @@ + __('Type csr'), 'user' => __('Type user'), 'tourist' => __('Type tourist')]; + } + + public function getGenderList() + { + return ['secrecy' => __('Gender secrecy'), 'male' => __('Gender male'), 'female' => __('Gender female')]; + } + + public function getStatusList() + { + return ['0' => __('Status 0'), '1' => __('Status 1'), '2' => __('Status 2'), '3' => __('Status 3')]; + } + + + public function getTypeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['type']) ? $data['type'] : ''); + $list = $this->getTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getGenderTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['gender']) ? $data['gender'] : ''); + $list = $this->getGenderList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function fauser() + { + return $this->belongsTo('app\admin\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + public function admin() + { + return $this->belongsTo('app\admin\model\Admin', 'admin_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + public function csrgroup() + { + return $this->belongsTo('app\admin\model\fastim\CsrGroup', 'group_id', 'id', [], 'LEFT')->setEagerlyType(0); + } +} diff --git a/application/admin/model/shopro/Admin.php b/application/admin/model/shopro/Admin.php new file mode 100644 index 0000000..52d9fc7 --- /dev/null +++ b/application/admin/model/shopro/Admin.php @@ -0,0 +1,53 @@ +getRuleIds($admin->id); + $is_super = in_array('*', $RuleIds) ? 1 : 0; + if ($is_super) { + return true; + } + + if ($auth->check(implode(',', $rules), $admin->id)) { + return true; + } + + return false; + } + + + /** + * 是否是超级管理员 + * + * @param \think\Model $admin + * @return boolean + */ + public function isSuper(\think\Model $admin) + { + $auth = \app\admin\library\Auth::instance(); + $RuleIds = $auth->getRuleIds($admin->id); + + $is_super = in_array('*', $RuleIds) ? 1 : 0; + + return $is_super; + } +} diff --git a/application/admin/model/shopro/Cart.php b/application/admin/model/shopro/Cart.php new file mode 100644 index 0000000..1f52be9 --- /dev/null +++ b/application/admin/model/shopro/Cart.php @@ -0,0 +1,75 @@ +goods || !is_null($this->goods->deletetime) || !$this->sku_price) { + $status = 'deleted'; // 已删除 + } else if ($this->goods->status == 'down' || $this->sku_price->status == 'down') { + $status = 'down'; // 已下架 + } + + return $status; + } + + + public function getTagsAttr($value, $data) + { + $tags = [ + 'activity' => [], + ]; + + $activities = $this->activities; + foreach ($activities as $activity) { + $tags['activity'][] = $activity['type_text'] . $activity['status_text']; + } + + if ($this->sku_price && $this->sku_price->price < $data['snapshot_price']) { + // 当前规格价格,低于加入购物车时候的价格,则提示商品比加入时降价 + $tags['price'] = '距加入降 ¥ ' . bcsub($data['snapshot_price'], $this->sku_price->price, 2); + } + } + + + public function goods() + { + return $this->belongsTo(Goods::class, 'goods_id'); + } + + + public function skuPrice() + { + return $this->belongsTo(SkuPrice::class, 'goods_sku_price_id'); + } +} diff --git a/application/admin/model/shopro/Category.php b/application/admin/model/shopro/Category.php new file mode 100644 index 0000000..1b19565 --- /dev/null +++ b/application/admin/model/shopro/Category.php @@ -0,0 +1,35 @@ +style; + $string = 'children'; + if (strpos($style, 'second') === 0) { + $string .= '.children'; + } else if (strpos($style, 'third') === 0) { + $string .= '.children.children'; + } + + return $string; + } + + + public function children() + { + return $this->hasMany(self::class, 'parent_id', 'id')->normal()->order('weigh', 'desc')->order('id', 'asc'); + } +} diff --git a/application/admin/model/shopro/ClubThirdOauth.php b/application/admin/model/shopro/ClubThirdOauth.php new file mode 100644 index 0000000..16ffcdf --- /dev/null +++ b/application/admin/model/shopro/ClubThirdOauth.php @@ -0,0 +1,9 @@ +filterInstance(); + // var_dump($filters);exit; + $query = $instance->apply($query, $filters); + // var_dump($sort);exit; + if ($sort) { + $query = $instance->filterOrder($query,$sort_user_id); + // var_dump($query);exit; + } + + return $query; + } + + + /** + * 获取模型中文名 + * + * @return string|null + */ + // public function getModelName() + // { + // if (isset($this->modelName)) { + // $model_name = $this->modelName; + // } else { + // $tableComment = $this->tableComment(); + // $table_name = $this->getQuery()->getTable(); + // $model_name = $tableComment[$table_name] ?? null; + // } + + // return $model_name; + // } +} diff --git a/application/admin/model/shopro/Config.php b/application/admin/model/shopro/Config.php new file mode 100644 index 0000000..a3e9e8b --- /dev/null +++ b/application/admin/model/shopro/Config.php @@ -0,0 +1,192 @@ +find(); + if(!$row) return null; + if($row['type'] !== 'group') { + $config = $row->value; + }else { + $config = []; + $list = self::where('parent_code', $code)->select(); + foreach ($list as &$row) { + if ($row['type'] === 'group') { + $row->value = self::getConfigs($row->code, false); + } else { + cache("config:{$row->code}", $row->value); + } + $config[self::getShortCode($row)] = $row->value; + } + } + // 设置配置缓存 + cache("config:{$code}", $config); + } + + return $config; + } + + /** + * 获取单一配置项 + * + * @param string $code + * @param boolean $cache + * @return mixed + */ + public static function getConfigField($code, $cache = true) + { + // 从缓存中获取 + if ($cache) { + $config = cache("config:{$code}"); + if (empty($config)) { + $config = self::getConfigField($code, false); + } + } + + // 从数据库中查找 + if (!$cache) { + $config = self::where('code', $code)->value('value'); + // 设置配置缓存 + cache("config:{$code}", $config); + } + + return $config; + } + + private static function getShortCode($config) + { + if (!empty($config['parent_code'])) { + return str_replace("{$config['parent_code']}.", "", $config['code']); + } + return $config['code']; + } + + /** + * 更新配置 + * + * @param string $code + * @param array $configParams + * @return void + */ + public static function setConfigs(string $code, array $configParams) + { + operate_filter(); + foreach ($configParams as $configKey => $configValue) { + self::setConfigField($code . '.' . $configKey, $configValue); + } + + self::getConfigs(explode('.', $code)[0], false); + return self::getConfigs($code); + } + + /** + * 更新配置项 + */ + private static function setConfigField($code, $value) + { + $config = self::where('code', $code)->find(); + + if ($config) { + if ($config['type'] === 'group') { + foreach ($value as $k => $v) { + self::setConfigField($code . '.' . $k, $v); + } + } else { + $config->value = $value; + $config->save(); + } + } + } + + + /** + * 修改器 数据的保存格式 + * + * @param string|array $value + * @param array $data + * @return string + */ + public function setValueAttr($value, $data) + { + switch ($data['type']) { + case 'array': + $value = is_array($value) ? json_encode($value, JSON_UNESCAPED_UNICODE) : $value; + break; + } + + return $value; + } + + + /** + * 获取器,选项 + * + * @param string|array $value + * @param array $data + * @return array + */ + public function getStoreRangeAttr($value, $data) + { + return $this->attrFormatJson($value, $data, 'store_range'); + } + + + + /** + * 获取器,返回的格式 + * + * @param string|array $value + * @param array $data + * @return array + */ + public function getValueAttr($value, $data) + { + $value = $value ?: ($data['value'] ?? null); + + switch ($data['type']) { + case 'array': + $value = $this->attrFormatJson($value, $data, 'value', true); + break; + case 'boolean': + $value = intval($value) ? 1 : 0; + break; + case 'int': + $value = intval($value); + break; + case 'float': + $value = floatval($value); + break; + } + + return config_show($value, $data); + } +} diff --git a/application/admin/model/shopro/Coupon.php b/application/admin/model/shopro/Coupon.php new file mode 100644 index 0000000..5c85947 --- /dev/null +++ b/application/admin/model/shopro/Coupon.php @@ -0,0 +1,322 @@ + 'timestamp', + 'get_end_time' => 'timestamp', + // 'use_start_time' => 'timestamp', + // 'use_end_time' => 'timestamp', + ]; + + // 追加属性 + protected $append = [ + 'type_text', + 'status_text', + 'use_scope_text', + 'amount_text', + 'get_time_status', + 'get_time_text' + ]; + + + /** + * 默认类型列表 + * + * @return array + */ + public function typeList() + { + return [ + 'reduce' => '满减券', + 'discount' => '折扣券' + ]; + } + /** + * 可用范围列表 + * + * @return array + */ + public function useScopeList() + { + return [ + 'all_use' => '全场通用', + 'goods' => '指定商品可用', + 'disabled_goods' => '指定商品不可用', + 'category' => '指定分类可用', + ]; + } + /** + * 默认状态列表 + * + * @return array + */ + public function statusList() + { + return [ + 'normal' => '公开发放', + 'hidden' => '后台发放', + 'disabled' => '禁止使用', + ]; + } + + + /** + * 优惠券领取状态 + * + * @return void + */ + public function getStatusList() + { + return [ + 'can_get' => '立即领取', + 'cannot_get' => '已领取', + 'get_over' => '已领完', + + // 用户优惠券的状态 + 'used' => '已使用', + 'can_use' => '立即使用', + 'expired' => '已过期', + 'cannot_use' => '暂不可用' + ]; + } + + + public function scopeCanGet($query) + { + return $query->where('get_start_time', '<=', time()) + ->where('get_end_time', '>=', time()); + } + + + /** + * 查询指定商品满足的优惠券 + * + * @param [type] $query + * @param [type] $goods + * @return void + */ + public function scopeGoods($query, $goods) + { + $goods_id = $goods['id']; + $category_ids = $goods['category_ids']; + + // 查询符合商品的优惠券 + return $query->where(function ($query) use ($goods_id, $category_ids) { + $query->where('use_scope', 'all_use') + ->whereOr(function ($query) use ($goods_id) { + $query->where('use_scope', 'goods')->whereRaw("find_in_set($goods_id, items)"); + }) + ->whereOr(function ($query) use ($goods_id) { + $query->where('use_scope', 'disabled_goods')->whereRaw("not find_in_set($goods_id, items)"); + }) + ->whereOr(function ($query) use ($goods_id, $category_ids) { + $query->where('use_scope', 'category')->where(function ($query) use ($category_ids) { + // $category_ids = array_filter(explode(',', $category_ids)); + $category_ids = array_filter(array_map('intval', explode(',', $category_ids))); + + foreach ($category_ids as $key => $category_id) { + $query->whereOrRaw("find_in_set($category_id, items)"); + } + }); + }); + }); + } + + + + /** + * 开始使用时间获取器 + * + * @param string $value + * @param array $data + * @return int + */ + public function setUseStartTimeAttr($value, $data) + { + return $value ? strtotime($value) : (isset($data['use_start_time']) ? strtotime($data['use_start_time']) : 0); + } + + + /** + * 结束使用时间获取器 + * + * @param string $value + * @param array $data + * @return int + */ + public function setUseEndTimeAttr($value, $data) + { + return $value ? strtotime($value) : (isset($data['use_end_time']) ? strtotime($data['use_end_time']) : 0); + } + + + /** + * 可用范围获取器 + * + * @param string $value + * @param array $data + * @return string + */ + public function getUseScopeTextAttr($value, $data) + { + $value = $value ?: ($data['use_scope'] ?? null); + + $list = $this->useScopeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getAmountTextAttr($value, $data) + { + return '满' . $data['enough'] . '元,' . ($data['type'] == 'reduce' ? '减' . floatval($data['amount']) . '元' : '打' . floatval($data['amount']) . '折'); + } + + public function getItemsValueAttr($value, $data) + { + if (in_array($data['use_scope'], ['goods', 'disabled_goods'])) { + $items_value = Goods::whereIn('id', $data['items'])->select(); + $items_value = collection($items_value); + $items_value = $items_value->each(function ($goods) { + // 前端要显示活动标签 + $goods->promos = $goods->promos; + }); + } else { + $items_value = Category::whereIn('id', $data['items'])->select(); + } + + return $items_value ?? []; + } + + public function getGetStatusAttr($value, $data) + { + $limit_num = $data['limit_num'] ?? 0; + // 不限制领取次数,或者限制次数,领取数量还没达到最大值 + $get_status = (!$limit_num || ($limit_num && $limit_num > count($this->user_coupons))) ? 'can_get' : 'cannot_get'; + + if ($get_status == 'can_get' && $data['stock'] <= 0) { + $get_status = 'get_over'; // 已领完 + } + + $user_coupon_id = request()->param('user_coupon_id', 0); + if ($user_coupon_id) { + // 从我领取的优惠券进详情,覆盖 状态 + $user = auth_user(); + $userCoupon = UserCouponModel::where('user_id', ($user ? $user->id : 0))->find($user_coupon_id); + if ($userCoupon) { + $get_status = $userCoupon->status; + } + } + return $get_status; + } + + + public function getGetStatusTextAttr($value, $data) + { + $list = $this->getStatusList(); + return isset($list[$this->get_status]) ? $list[$this->get_status] : ''; + } + + + /** + * 后端发放状态 + * + * @return string + */ + public function getGetTimeStatusAttr($value, $data) { + if ($data['get_start_time'] > time()) { + $time_text = 'nostart'; // 未开始 + } else if ($data['get_start_time'] <= time() && $data['get_end_time'] >= time()) { + $time_text = 'ing'; + } else if ($data['get_end_time'] < time()) { + $time_text = 'ended'; + } + + return $time_text; + } + + + /** + * 后端发放状态 + * + * @return string + */ + public function getGetTimeTextAttr($value, $data) + { + if ($this->get_time_status == 'nostart') { + $time_text = '未开始'; // 未开始 + } else if ($this->get_time_status == 'ing') { + $time_text = '发放中'; + } else if ($this->get_time_status == 'ended') { + $time_text = '已结束'; + } + + return $time_text; + } + + + public function getGetNumAttr($value, $data) + { + return UserCouponModel::where('coupon_id', $data['id'])->count(); + } + + + public function getUseNumAttr($value, $data) + { + return UserCouponModel::where('coupon_id', $data['id'])->whereNotNull('use_time')->count(); + } + + + + public function getUseStartTimeAttr($value, $data) + { + $use_start_time = $value ? date('Y-m-d H:i:s', $value) : null; + $user_coupon_id = request()->param('user_coupon_id', 0); + if ($user_coupon_id && $data['use_time_type'] == 'days') { + // 从我领取的优惠券进详情,覆盖 状态 + $user = auth_user(); + $userCoupon = UserCouponModel::cache(60)->where('user_id', ($user ? $user->id : 0))->find($user_coupon_id); + if ($userCoupon) { + $use_start_time = date('Y-m-d H:i:s', $userCoupon->getData('createtime') + ($this->start_days * 86400)); + } + } + + return $use_start_time; + } + + public function getUseEndTimeAttr($value, $data) + { + $use_end_time = $value ? date('Y-m-d H:i:s', $value) : null; + $user_coupon_id = request()->param('user_coupon_id', 0); + if ($user_coupon_id && $data['use_time_type'] == 'days') { + // 从我领取的优惠券进详情,覆盖 状态 + $user = auth_user(); + $userCoupon = UserCouponModel::cache(60)->where('user_id', ($user ? $user->id : 0))->find($user_coupon_id); + if ($userCoupon) { + $use_end_time = date('Y-m-d H:i:s', $userCoupon->getData('createtime') + (($this->start_days + $this->days) * 86400)); + } + } + + return $use_end_time; + } + + + public function userCoupons() + { + $user = auth_user(); + return $this->hasMany(UserCouponModel::class, 'coupon_id')->where('user_id', ($user ? $user->id : 0)); + } +} diff --git a/application/admin/model/shopro/Feedback.php b/application/admin/model/shopro/Feedback.php new file mode 100644 index 0000000..0c2bf9c --- /dev/null +++ b/application/admin/model/shopro/Feedback.php @@ -0,0 +1,36 @@ + 'json' + ]; + + protected $append = [ + 'status_text' + ]; + + + /** + * 类型列表 + * + * @return array + */ + public function statusList() + { + return ['0' => '待处理', '1' => '已处理']; + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id')->field('id, nickname, avatar, mobile'); + } + +} diff --git a/application/admin/model/shopro/Pay.php b/application/admin/model/shopro/Pay.php new file mode 100644 index 0000000..b891825 --- /dev/null +++ b/application/admin/model/shopro/Pay.php @@ -0,0 +1,81 @@ + '未支付', + self::PAY_STATUS_PAID => '已支付', + self::PAY_STATUS_REFUND => '已退款' + ]; + } + + + public function payTypeList() + { + return [ + 'wechat' => '微信支付', + 'alipay' => '支付宝', + 'money' => '钱包支付', + 'score' => '积分支付', + 'offline' => '货到付款', + ]; + } + + + public function scopeTypeOrder($query) // scopeOrder 调用时候,和 order 排序方法冲突了 + { + return $query->where('order_type', 'order'); + } + + + public function scopeTypeTradeOrder($query) + { + return $query->where('order_type', 'trade_order'); + } + + + public function scopePaid($query) + { + return $query->where('status', self::PAY_STATUS_PAID); + } + + + public function scopeIsMoney($query) + { + return $query->whereIn('pay_type', ['wechat', 'alipay', 'money']); + } + + + /** + * 通用类型获取器 + * + * @param string $value + * @param array $data + * @return string + */ + public function getPayTypeTextAttr($value, $data) + { + $value = $value ?: ($data['pay_type'] ?? null); + + $list = $this->payTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } +} diff --git a/application/admin/model/shopro/PayConfig.php b/application/admin/model/shopro/PayConfig.php new file mode 100644 index 0000000..5419862 --- /dev/null +++ b/application/admin/model/shopro/PayConfig.php @@ -0,0 +1,46 @@ + 'json', + ]; + + // 追加属性 + protected $append = [ + 'type_text', + 'status_text' + ]; + + protected $hidden = [ + 'params' + ]; + + public function statusList() + { + return [ + 'normal' => '正常', + 'disabled' => '禁用', + ]; + } + + + public function typeList() + { + return [ + 'wechat' => '微信支付', + 'alipay' => '支付宝支付', + ]; + } +} diff --git a/application/admin/model/shopro/Refund.php b/application/admin/model/shopro/Refund.php new file mode 100644 index 0000000..29cb9a7 --- /dev/null +++ b/application/admin/model/shopro/Refund.php @@ -0,0 +1,38 @@ + '退款中', + self::STATUS_COMPLETED => '退款完成', + self::STATUS_FAIL => '退款失败' + ]; + } + + + public function refundTypeList() + { + return [ + 'back' => '原路退回', + 'money' => '退回到余额' + ]; + } + +} diff --git a/application/admin/model/shopro/SearchHistory.php b/application/admin/model/shopro/SearchHistory.php new file mode 100644 index 0000000..3f609d3 --- /dev/null +++ b/application/admin/model/shopro/SearchHistory.php @@ -0,0 +1,15 @@ + 'json' + ]; + + protected $append = [ + 'platform_text', + 'from_text' + ]; + + const FROM = ['forward' => '直接转发', 'poster' => '识别海报', 'link' => '分享链接']; + + const PLATFORM = ['H5' => 'H5网页', 'WechatOfficialAccount' => '微信公众号网页', 'WechatMiniProgram' => '微信小程序', 'App' => 'APP']; + + public function getPlatformTextAttr($value, $data) + { + $value = $value ?: ($data['platform'] ?? null); + + return (self::PLATFORM)[$value] ?? $value; + } + + public function getFromTextAttr($value, $data) + { + $value = $value ?: ($data['from'] ?? null); + + return (self::FROM)[$value] ?? $value; + } + + public static function log(Object $user, $params) + { + + // 错误的分享参数 + if (empty($params['spm'])) { + return false; + } + + $shareId = $params['shareId']; + // 分享用户为空 + if ($shareId <= 0) { + return false; + } + + // 不能分享给本人 + if ($shareId == $user->id) { + return false; + } + + // 新用户不能分享给老用户 按需打开 + // if($user->id < $shareId) { + // return false; + // } + + $shareUser = UserModel::where('id', $shareId)->find(); + // 分享人不存在 + if (!$shareUser) { + return false; + } + + // 5分钟内相同的分享信息不保存,防止冗余数据 + $lastShareLog = self::where([ + 'user_id' => $user->id + ])->where('createtime', '>', time() - 300)->order('id desc')->find(); + + if ($lastShareLog && $lastShareLog->spm === $params['spm']) { + return $lastShareLog; + } + + $memoText = '通过' . (self::FROM)[$params['from']] . '访问了'; + if ($params['page'] == '/pages/index/index') { + $memoText .= '首页'; + } + if ($params['page'] === '/pages/goods/index') { + $memoText .= '商品'; + $goodsId = $params['query']['id']; + } + if ($params['page'] === '/pages/goods/groupon') { + $memoText .= '拼团商品'; + $goodsId = $params['query']['id']; + } + if ($params['page'] === '/pages/goods/seckill') { + $memoText .= '秒杀商品'; + $goodsId = $params['query']['id']; + } + if ($params['page'] === '/pages/activity/groupon/detail') { + $memoText .= '拼团活动'; + } + + if (!empty($goodsId)) { + $goods = GoodsModel::find($goodsId); + if ($goods) { + $memoText .= "[{$goods->title}]"; + } + } + + $ext = [ + 'image' => $goods->image ?? "", + 'memo' => $memoText + ]; + + $shareInfo = self::create([ + 'user_id' => $user->id, + 'share_id' => $shareId, + 'spm' => $params['spm'], + 'page' => $params['page'], + 'query' => http_build_query($params['query']), + 'platform' => $params['platform'], + 'from' => $params['from'], + 'ext' => $ext + ]); + + $data = ['shareInfo' => $shareInfo]; + \think\Hook::listen('user_share_after', $data); + + return $shareInfo; + } + + + // -- commission code start -- + public function agent() + { + return $this->belongsTo(\app\admin\model\shopro\commission\Agent::class, 'share_id', 'user_id'); + } + // -- commission code end -- + + public function user() + { + return $this->belongsTo(UserModel::class, 'user_id', 'id'); + } +} diff --git a/application/admin/model/shopro/ThirdOauth.php b/application/admin/model/shopro/ThirdOauth.php new file mode 100644 index 0000000..64e89c2 --- /dev/null +++ b/application/admin/model/shopro/ThirdOauth.php @@ -0,0 +1,9 @@ + __('停用'), '1' => __('正常')]; + } + + + public function getStarttimeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['starttime']) ? $data['starttime'] : ''); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + + public function getEndtimeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['endtime']) ? $data['endtime'] : ''); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); + $list = $this->getStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + protected function setStarttimeAttr($value) + { + return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); + } + + protected function setEndtimeAttr($value) + { + return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); + } + + +} diff --git a/application/admin/model/shopro/Withdraw.php b/application/admin/model/shopro/Withdraw.php new file mode 100644 index 0000000..0b20700 --- /dev/null +++ b/application/admin/model/shopro/Withdraw.php @@ -0,0 +1,83 @@ + 'json' + ]; + // 追加属性 + protected $append = [ + 'status_text', + 'charge_rate_format', + 'withdraw_info_hidden', + 'withdraw_type_text' + ]; + + public function statusList() + { + return [ + -1 => '已拒绝', + 0 => '待审核', + 1 => '处理中', + 2 => '已处理' + ]; + } + + + public function withdrawTypeList() + { + return [ + 'wechat' => '微信零钱', + 'alipay' => '支付包账户', + 'back' => '银行卡', + ]; + } + + + /** + * 类型获取器 + */ + public function getWithdrawTypeTextAttr($value, $data) + { + $value = $value ?: ($data['withdraw_type'] ?? null); + + $list = $this->withdrawTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + + public function getChargeRateFormatAttr($value, $data) + { + $value = $value ?: ($data['charge_rate'] ?? null); + + return bcmul((string)$value, '100', 1) . '%'; + } + + public function getWithdrawInfoHiddenAttr($value, $data) + { + $withdraw_info = $value ?: ($this->withdraw_info ?? null); + + foreach ($withdraw_info as $key => &$info) { + if (in_array($key, ['微信用户', '真实姓名'])) { + $info = string_hide($info, 2); + } elseif (in_array($key, ['银行卡号', '支付宝账户', '微信ID'])) { + $info = account_hide($info); + } + } + + return $withdraw_info; + } + + + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id')->field('id, nickname, avatar, total_consume'); + } +} diff --git a/application/admin/model/shopro/WithdrawLog.php b/application/admin/model/shopro/WithdrawLog.php new file mode 100644 index 0000000..4440500 --- /dev/null +++ b/application/admin/model/shopro/WithdrawLog.php @@ -0,0 +1,13 @@ + 'json', + 'prehead_time' => 'timestamp', + 'start_time' => 'timestamp', + 'end_time' => 'timestamp', + ]; + + // 追加属性 + protected $append = [ + 'status', + 'status_text', + 'type_text', + // 'end_time_unix' // 不需要了 + ]; + + + public function classifies() + { + return [ + 'activity' => [ + 'groupon' => '拼团', + 'groupon_ladder' => '阶梯拼团', + // 'groupon_lucky' => '幸运拼团', + 'seckill' => '秒杀', + ], + 'promo' => [ + 'full_reduce' => '满减', + 'full_discount' => '满折', + 'full_gift' => '满赠', + 'free_shipping' => '满邮', + ], + 'app' => [ + 'signin' => '签到' + ] + ]; + } + + public function typeList() + { + return [ + 'groupon' => '拼团', + 'groupon_ladder' => '阶梯拼团', + // 'groupon_lucky' => '幸运拼团', + 'seckill' => '秒杀', + 'full_reduce' => '满减', + 'full_discount' => '满折', + 'full_gift' => '满赠', + 'free_shipping' => '满邮', + 'signin' => '签到', + ]; + } + + + /** + * 获取活动的互斥活动 + * + * @param string $current_activity_type + * @return array + */ + public function getMutexActivityTypes($current_activity_type) + { + $activityTypes = []; + switch ($current_activity_type) { + case 'groupon': + $activityTypes = ['groupon']; + break; + case 'groupon_ladder': + $activityTypes = ['groupon_ladder']; + break; + case 'groupon_lucky': + $activityTypes = ['groupon_lucky']; + break; + case 'seckill': + $activityTypes = ['seckill']; + break; + case 'full_reduce': + $activityTypes = ['full_reduce', 'full_discount']; + break; + case 'full_discount': + $activityTypes = ['full_reduce', 'full_discount']; + break; + case 'free_shipping': + $activityTypes = ['free_shipping']; + break; + case 'full_gift': + $activityTypes = ['full_gift']; + break; + case 'signin': + $activityTypes = ['signin']; + break; + } + + return $activityTypes; + } + + + /** + * 根据类型获取 classify + * + * @param string $type + * @return string + */ + public function getClassify($type) + { + $classifys = $this->classifies(); + $activitys = array_keys($classifys['activity']); + $promos = array_keys($classifys['promo']); + $apps = array_keys($classifys['app']); + + $classify = null; + if (in_array($type, $activitys)) { + $classify = 'activity'; + } else if (in_array($type, $promos)) { + $classify = 'promo'; + } else if (in_array($type, $apps)) { + $classify = 'app'; + } + + return $classify; + } + + + + /** + * status 组合 (在thinkphp5 where Closure 中,不能直接使用 scope,特殊场景下用来代替下面的 scopeNostart scopePrehead 等) + * + * @param [type] $query + * @param [type] $status + * @return void + */ + public function scopeStatusComb($query, $status) + { + return $query->where(function ($query) use ($status) { + foreach ($status as $st) { + $query->whereOr(function ($query) use ($st) { + switch($st) { + case 'nostart': + $query->where('start_time', '>', time()); + break; + case 'prehead': + $query->where('prehead_time', '<=', time())->where('start_time', '>', time()); + break; + case 'ing': + $query->where('start_time', '<=', time())->where('end_time', '>=', time()); + break; + case 'show': + $query->where('prehead_time', '<=', time())->where('end_time', '>=', time()); + break; + case 'ended': + $query->where('end_time', '<', time()); + break; + default: + error_stop('status 状态错误'); + } + }); + } + }); + } + + + /** + * 未开始的活动 + * + * @param think\query\Query $query + * @return void + */ + public function scopeNostart($query) + { + return $query->where('start_time', '>', time()); + } + + + /** + * 预售的活动 + * + * @param think\query\Query $query + * @return void + */ + public function scopePrehead($query) + { + return $query->where('prehead_time', '<=', time())->where('start_time', '>', time()); + } + + /** + * 进行中的活动 + * + * @param think\query\Query $query + * @return void + */ + public function scopeIng($query) + { + return $query->where('start_time', '<=', time())->where('end_time', '>=', time()); + } + + /** + * 已经开始预售,并且没有结束的活动 + * + * @param think\query\Query $query + * @return void + */ + public function scopeShow($query) + { + return $query->where('prehead_time', '<=', time())->where('end_time', '>=', time()); + } + + /** + * 已经结束的活动 + * + * @param think\query\Query $query + * @return void + */ + public function scopeEnded($query) + { + return $query->where('end_time', '<', time()); + } + + + + /** + * 修改器 classify + * + * @param string $value + * @param array $data + * @return integer|null + */ + public function setClassifyAttr($value, $data) + { + $classify = $value ?: ($data['classify'] ?? null); + if (!$classify) { + $type = $data['type'] ?? null; // 活动类型 + + $classify = $this->getClassify($type); + } + + return $classify; + } + + + /** + * 修改器 预热时间 + * + * @param string $value + * @param array $data + * @return integer|null + */ + public function setPreheadTimeAttr($value, $data) + { + // promo 类型 prehead_time 永远等于 start_time + $value = (isset($data['classify']) && $data['classify'] == 'promo') ? $data['start_time'] : ($value ?: $data['start_time']); + return $this->attrFormatUnix($value); + } + + /** + * 修改器 开始时间 + * + * @param string $value + * @return integer|null + */ + public function setStartTimeAttr($value) + { + return $this->attrFormatUnix($value); + } + + /** + * 修改器 结束时间 + * + * @param string $value + * @return integer|null + */ + public function setEndTimeAttr($value) + { + return $this->attrFormatUnix($value); + } + + + public function getStatusAttr($value, $data) + { + return $this->getStatusCode($data['prehead_time'], $data['start_time'], $data['end_time']); + } + + + public function getStatusTextAttr($value, $data) + { + return $this->getStatusText($this->status); + } + + + public function getGoodsListAttr($value, $data) + { + if ($data['goods_ids']) { + + $goods = Goods::field('id,title,price,sales,image,status')->whereIn('id', $data['goods_ids'])->select(); + $goods = collection($goods)->toArray(); // 全部转数组 + + $goodsIds = array_column($goods, 'id'); + $activitySkuPrices = ActivitySkuPriceModel::where('activity_id', $data['id'])->whereIn('goods_id', $goodsIds)->order('id', 'asc')->select(); + $activitySkuPrices = collection($activitySkuPrices)->toArray(); + + // 后台编辑活动时,防止不编辑规格无法提交问题 + foreach ($goods as &$gd) { + // 处理 $gd['activity_sku_prices'] + $gd['activity_sku_prices'] = []; + foreach ($activitySkuPrices as $skuPrice) { + if ($skuPrice['goods_id'] == $gd['id']) { + $gd['activity_sku_prices'][] = $skuPrice; + } + } + + // 处理活动规格,数据 + foreach ($gd['activity_sku_prices'] as $key => $skuPrice) { + $skuPrice = ActivityFacade::showSkuPrice($data['type'], $skuPrice); + $gd['activity_sku_prices'][$key] = $skuPrice; + } + } + } + + return $goods ?? []; + } + + + public function getRulesAttr($value, $data) + { + $rules = $data['rules'] ? json_decode($data['rules'], true) : []; + $type = $data['type']; + + // 获取各个活动规则相关的特殊数据 + $rules = ActivityFacade::rulesInfo($type, $rules); + + return $rules; + } + + + /** + * 通过时间判断活动状态 + * + * @param integer $prehead_time 预热时间 + * @param integer $start_time 开始时间 + * @param integer $end_time 结束时间 + * @return string + */ + public static function getStatusCode($prehead_time, $start_time, $end_time) + { + // 转为时间戳,(从 redis 中取出来的是 时间格式) + if (($prehead_time && $prehead_time > time()) || (!$prehead_time && $start_time > time())) { + $status = 'nostart'; // 未开始 + } else if ($prehead_time && $prehead_time < time() && $start_time > time()) { + $status = 'prehead'; // 预热 + } else if ($start_time < time() && $end_time > time()) { + $status = 'ing'; + } else if ($end_time < time()) { + $status = 'ended'; + } + + return $status ?? 'ended'; + } + + /** + * 判断活动状态中文 + * + * @param string $status 活动状态 + * @return string + */ + public static function getStatusText($status) + { + if ($status == 'nostart') { + $status_text = '未开始'; + } elseif ($status == 'prehead') { + $status_text = '预热中'; + } elseif ($status == 'ing') { + $status_text = '进行中'; + } elseif ($status == 'ended') { + $status_text = '已结束'; + } + + return $status_text ?? '已结束'; + } + + + public function getEndTimeUnixAttr($value, $data) + { + return isset($data['end_time']) ? $this->getData('end_time') : 0; + } + + + public function activitySkuPrices() + { + return $this->hasMany(SkuPrice::class, 'activity_id'); + } +} diff --git a/application/admin/model/shopro/activity/GiftLog.php b/application/admin/model/shopro/activity/GiftLog.php new file mode 100644 index 0000000..b0917a8 --- /dev/null +++ b/application/admin/model/shopro/activity/GiftLog.php @@ -0,0 +1,73 @@ + 'json', + 'errors' => 'json' + ]; + + // 追加属性 + protected $append = [ + 'type_text', + 'status_text' + ]; + + + /** + * 默认状态列表 + * + * @return array + */ + public function typeList() + { + return [ + 'coupon' => '优惠券', + 'score' => '积分', + 'money' => '余额', + 'goods' => '商品', + ]; + } + + + /** + * 默认状态列表 + * + * @return array + */ + public function statusList() + { + return [ + 'waiting' => '等待赠送', + 'finish' => '赠送完成', + 'fail' => '赠送失败', + ]; + } + + + public function scopeWaiting($query) + { + return $query->where('status', 'waiting'); + } + + public function scopeOpered($query) + { + return $query->whereIn('status', ['finish', 'fail']); + } + + public function scopeFinish($query) + { + return $query->where('status', 'finish'); + } + + public function scopeFail($query) + { + return $query->where('status', 'fail'); + } +} diff --git a/application/admin/model/shopro/activity/Groupon.php b/application/admin/model/shopro/activity/Groupon.php new file mode 100644 index 0000000..0268d84 --- /dev/null +++ b/application/admin/model/shopro/activity/Groupon.php @@ -0,0 +1,98 @@ + 'timestamp', + 'expire_time' => 'timestamp' + ]; + + // 追加属性 + protected $append = [ + 'status_text', + ]; + + + public function statusList() + { + return [ + 'invalid' => '拼团失败', + 'ing' => '进行中', + 'finish' => '已成团', + 'finish_fictitious' => '虚拟成团', + ]; + } + + /** + * 查询正在进行中的团 + */ + public function scopeIng($query) + { + return $query->where('status', 'ing'); + } + + + public function getStatusTextAttr($value, $data) + { + $value = $value ?: ($data['status'] ?? ''); + + $list = $this->statusList(); + $value = ($value == 'finish_fictitious' && strpos(request()->url(), 'addons/shopro') !== false) ? 'finish' : $value; + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getExpireTimeUnixAttr($value, $data) + { + return isset($data['expire_time']) ? $this->getData('expire_time') : 0; + } + + + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } + + + public function goods() + { + return $this->belongsTo(Goods::class, 'goods_id', 'id'); + } + + public function activity() + { + return $this->belongsTo(Activity::class, 'activity_id', 'id'); + } + + public function grouponLogs() + { + return $this->hasMany(GrouponLog::class, 'groupon_id', 'id'); + } + + + public function leader() + { + return $this->hasOne(GrouponLog::class, 'groupon_id', 'id')->where('is_leader', 1); + } + + + /** + * 前端拼团详情用 + * + * @return void + */ + public function my() + { + $user = auth_user(); + return $this->hasOne(GrouponLog::class, 'groupon_id', 'id')->where('user_id', ($user ? $user->id : 0))->where('is_fictitious', 0); + } +} diff --git a/application/admin/model/shopro/activity/GrouponLog.php b/application/admin/model/shopro/activity/GrouponLog.php new file mode 100644 index 0000000..5ba994d --- /dev/null +++ b/application/admin/model/shopro/activity/GrouponLog.php @@ -0,0 +1,43 @@ +belongsTo(Order::class, 'order_id'); + } + + + public function groupon() + { + return $this->belongsTo(Groupon::class, 'groupon_id'); + } + + public function goods() + { + return $this->belongsTo(Goods::class, 'goods_id'); + } + + + public function orderItem() + { + return $this->hasOne(OrderItem::class, 'order_id', 'order_id'); + } +} diff --git a/application/admin/model/shopro/activity/Order.php b/application/admin/model/shopro/activity/Order.php new file mode 100644 index 0000000..db44dfe --- /dev/null +++ b/application/admin/model/shopro/activity/Order.php @@ -0,0 +1,93 @@ + 'json', + ]; + + // 追加属性 + protected $append = [ + 'status_text', + 'activity_type_text', + 'discount_text' + ]; + + const STATUS_UNPAID = 'unpaid'; + const STATUS_PAID = 'paid'; + + public function statusList() + { + return [ + 'unpaid' => '未支付', + 'paid' => '已支付', + ]; + } + + // 未支付 + public function scopeUnpaid($query) + { + return $query->where('status', self::STATUS_UNPAID); + } + + // 已支付 + public function scopePaid($query) + { + return $query->whereIn('status', [self::STATUS_PAID]); + } + + + + public function getActivityTypeTextAttr($value, $data) + { + $value = $value ?: ($data['activity_type'] ?? null); + $ext = $this->ext; + + $list = (new Activity)->typeList(); + $text = isset($list[$value]) ? $list[$value] : ''; + + if (in_array($value, ['groupon', 'groupon_ladder'])) { + if (in_array($data['status'], [self::STATUS_PAID]) && (!isset($ext['groupon_id']) || !$ext['groupon_id'])) { + // 已支付,并且没有团 id,就是单独购买 + $text .= '-单独购买'; + } + } + + return $text; + } + + + public function getDiscountTextAttr($value, $data) + { + $ext = $this->ext; + $discount_text = ''; + if ($ext && isset($ext['rules']) && $ext['rules']) { + $tags = ActivityFacade::formatRuleTags([ + 'type' => $ext['rules']['rule_type'], + 'discounts' => [$ext['rules']['discount_rule']] + ], $data['activity_type']); + + $discount_text = $tags[0] ?? ''; + } + + return $discount_text ?: $this->activity_type_text; + } + + + + public function getGoodsIdsAttr($value, $data) + { + return $this->attrFormatComma($value, $data, 'goods_ids', true); + } + + + +} diff --git a/application/admin/model/shopro/activity/Signin.php b/application/admin/model/shopro/activity/Signin.php new file mode 100644 index 0000000..d93a8da --- /dev/null +++ b/application/admin/model/shopro/activity/Signin.php @@ -0,0 +1,21 @@ + 'json' + ]; + + // 追加属性 + protected $append = [ + + ]; + +} diff --git a/application/admin/model/shopro/activity/SkuPrice.php b/application/admin/model/shopro/activity/SkuPrice.php new file mode 100644 index 0000000..f2ad7c3 --- /dev/null +++ b/application/admin/model/shopro/activity/SkuPrice.php @@ -0,0 +1,128 @@ + 'json', + ]; + + // 追加属性 + protected $append = [ + 'status_text', + ]; + + + /** + * 普通拼团,团长价 + * + * @param string $value + * @param array $data + * @return string + */ + public function getLeaderPriceAttr($value, $data) + { + $ext = $data['ext']; + $is_leader_discount = $ext['is_leader_discount'] ?? 0; + $leader_price = $ext['leader_price'] ?? 0; + + $leader_price = $is_leader_discount ? $leader_price : $data['price']; + return $leader_price; + } + + + + private function currentLadder($data, $level, $is_leader = false) + { + $ext = $data['ext']; + $is_leader_discount = $ext['is_leader_discount'] ?? 0; + $ladders = $ext['ladders'] ?? []; + $ladders = array_column($ladders, null, 'ladder_level'); + $currentLadder = $ladders[$level] ?? []; // 当前阶梯的 价格数据 + + $key = ($is_leader && $is_leader_discount) ? 'leader_ladder_price' : 'ladder_price'; + return $currentLadder[$key] ?? 0; + } + + /** + * 阶梯拼团,第一阶梯价 + * + * @param string $value + * @param array $data + * @return string + */ + public function getLadderOneAttr($value, $data) + { + return $this->currentLadder($data, 'ladder_one'); + } + + + /** + * 阶梯拼团,第二阶梯价 + * + * @param string $value + * @param array $data + * @return string + */ + public function getLadderTwoAttr($value, $data) + { + return $this->currentLadder($data, 'ladder_two'); + } + + + /** + * 阶梯拼团,第二阶梯价 + * + * @param string $value + * @param array $data + * @return string + */ + public function getLadderThreeAttr($value, $data) + { + return $this->currentLadder($data, 'ladder_three'); + } + + + /** + * 阶梯拼团,第一阶梯团长价 + * + * @param string $value + * @param array $data + * @return string + */ + public function getLadderOneLeaderAttr($value, $data) + { + return $this->currentLadder($data, 'ladder_one', true); + } + + + /** + * 阶梯拼团,第二阶团长梯价 + * + * @param string $value + * @param array $data + * @return string + */ + public function getLadderTwoLeaderAttr($value, $data) + { + return $this->currentLadder($data, 'ladder_two', true); + } + + + /** + * 阶梯拼团,第二阶团长梯价 + * + * @param string $value + * @param array $data + * @return string + */ + public function getLadderThreeLeaderAttr($value, $data) + { + return $this->currentLadder($data, 'ladder_three', true); + } +} diff --git a/application/admin/model/shopro/app/ScoreSkuPrice.php b/application/admin/model/shopro/app/ScoreSkuPrice.php new file mode 100644 index 0000000..487a29b --- /dev/null +++ b/application/admin/model/shopro/app/ScoreSkuPrice.php @@ -0,0 +1,63 @@ +sku_price; + return $skuPrice ? $skuPrice->goods_sku_ids : ''; + } + + + public function getGoodsSkuTextAttr($value, $data) + { + $skuPrice = $this->sku_price; + return $skuPrice ? $skuPrice->goods_sku_text : ''; + } + + public function getImageAttr($value, $data) + { + $skuPrice = $this->sku_price; + return $skuPrice ? $skuPrice->image : ''; + } + + + /** + * 积分加现金 + * + * @param string $value + * @param array $data + * @return string + */ + public function getScorePriceAttr($value, $data) + { + $score = $data['score'] ?? 0; + $price = $data['price'] ?? 0; + + return $score . '积分' . ($price ? '+¥' . $price : ''); + } + + + public function skuPrice() { + return $this->belongsTo(SkuPrice::class, 'goods_sku_price_id'); + } +} diff --git a/application/admin/model/shopro/app/mplive/Goods.php b/application/admin/model/shopro/app/mplive/Goods.php new file mode 100644 index 0000000..e91c505 --- /dev/null +++ b/application/admin/model/shopro/app/mplive/Goods.php @@ -0,0 +1,53 @@ + '我的小程序', 1 => '其他小程序']; + } + + public function auditStatusList() + { + return [0 => '未审核', 1 => '审核中', 2 => '审核通过', 3 => '审核失败']; + } + + public function priceTypeList() + { + return [1 => '一口价', 2 => '价格区间', 3 => '折扣价']; + } + + public function getPriceTypeTextAttr($value, $data) + { + $value = $value ?: ($data['price_type'] ?? null); + + $list = $this->priceTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getAuditStatusTextAttr($value, $data) + { + $value = $value ?: ($data['audit_status'] ?? null); + + $list = $this->auditStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } +} diff --git a/application/admin/model/shopro/app/mplive/Room.php b/application/admin/model/shopro/app/mplive/Room.php new file mode 100644 index 0000000..34f2a0d --- /dev/null +++ b/application/admin/model/shopro/app/mplive/Room.php @@ -0,0 +1,99 @@ + '系统错误', + 1 => '未创建直播间', + 1003 => '商品 id 不存在', + 47001 => '入参格式不符合规范', + 200002 => '入参错误', + 300001 => '禁止创建/更新商品 或 禁止编辑/更新房间', + 300002 => '名称长度不符合规则', + 300003 => '价格输入不合规(如:现价比原价大、传入价格非数字等)', + 300004 => '商品名称存在违规违法内容', + 300005 => '商品图片存在违规违法内容', + 300006 => ' 图片上传失败(如:mediaID过期)', + 300007 => '线上小程序版本不存在该链接', + 300008 => '添加商品失败', + 300009 => '商品审核撤回失败', + 300010 => '商品审核状态不对(如:商品审核中)', + 300011 => '操作非法(API不允许操作非 API 创建的商品)', + 300012 => '没有提审额度(每天500次提审额度)', + 300013 => '提审失败', + 300014 => '审核中,无法删除(非零代表失败)', + 300017 => '商品未提审', + 300018 => '商品图片尺寸过大', + 300021 => '商品添加成功,审核失败', + 300022 => '此房间号不存在', + 300023 => '房间状态 拦截(当前房间状态不允许此操作)', + 300024 => '商品不存在', + 300025 => '商品审核未通过', + 300026 => '房间商品数量已经满额', + 300027 => '导入商品失败', + 300028 => '房间名称违规', + 300029 => '主播昵称违规', + 300030 => '主播微信号不合法', + 300031 => '直播间封面图不合规', + 300032 => '直播间分享图违规', + 300033 => '添加商品超过直播间上限', + 300034 => '主播微信昵称长度不符合要求', + 300035 => '主播微信号不存在', + 300036 => '主播微信号未实名认证', + 300037 => '购物直播频道封面图不合规', + 300038 => '未在小程序管理后台配置客服', + 300039 => '主播副号微信号不合法', + 300040 => '名称含有非限定字符(含有特殊字符)', + 300041 => '创建者微信号不合法', + 300042 => '推流中禁止编辑房间', + 300043 => '每天只允许一场直播开启关注', + 300044 => '商品没有讲解视频', + 300045 => '讲解视频未生成', + 300046 => '讲解视频生成失败', + 300047 => '已有商品正在推送,请稍后再试', + 300048 => '拉取商品列表失败', + 300049 => '商品推送过程中不允许上下架', + 300050 => '排序商品列表为空', + 300051 => '解析 JSON 出错', + 300052 => '已下架的商品无法推送', + 300053 => '直播间未添加此商品', + 500001 => '副号不合规', + 500002 => '副号未实名', + 500003 => '已经设置过副号了,不能重复设置', + 500004 => '不能设置重复的副号', + 500005 => '副号不能和主号重复', + 600001 => '用户已被添加为小助手', + 600002 => '找不到用户', + 9410000 => '直播间列表为空', + 9410001 => '获取房间失败', + 9410002 => '获取商品失败', + 9410003 => '获取回放失败', + ]; + + /** + * 类型列表 + * + * @return array + */ + public function typeList() + { + return [0 => '手机直播', 1 => '推流']; + } + + public function statusList() + { + return [101 => '直播中', 102 => '未开始', 103 => '已结束', 104 => '禁播', 105 => '暂停', 106 => '异常', 107 => '已过期']; + } +} diff --git a/application/admin/model/shopro/chat/CommonWord.php b/application/admin/model/shopro/chat/CommonWord.php new file mode 100644 index 0000000..d52bfb6 --- /dev/null +++ b/application/admin/model/shopro/chat/CommonWord.php @@ -0,0 +1,23 @@ +where('room_id', $room_id); + } +} diff --git a/application/admin/model/shopro/chat/CustomerService.php b/application/admin/model/shopro/chat/CustomerService.php new file mode 100644 index 0000000..9fc8645 --- /dev/null +++ b/application/admin/model/shopro/chat/CustomerService.php @@ -0,0 +1,57 @@ + 'timestamp', + ]; + + + public function statusList() + { + return ['offline' => '离线', 'online' => '在线', 'busy' => '忙碌']; + } + + + public function getAuthModelAttr($value, $data) + { + return $this->customer_service_user['auth_model'] ?? null; + } + + public function getAuthTextAttr($value, $data) + { + return $this->customer_service_user['auth_text'] ?? null; + } + + + public function customerService() + { + return $this->morphMany(Record::class, ['sender_identify', 'sender_id'], 'customer_service'); + } + + public function customerServiceUser() + { + return $this->HasOne(CustomerServiceUser::class, 'customer_service_id'); + } + + +} diff --git a/application/admin/model/shopro/chat/CustomerServiceUser.php b/application/admin/model/shopro/chat/CustomerServiceUser.php new file mode 100644 index 0000000..96be03e --- /dev/null +++ b/application/admin/model/shopro/chat/CustomerServiceUser.php @@ -0,0 +1,62 @@ + ['name' => '管理员', 'value' => 'admin'], + 'user' => ['name' => '用户', 'value' => 'user'], + ]; + + public function scopeAuthAdmin($query, $admin_id) + { + return $query->where('auth', 'admin')->where('auth_id', $admin_id); + } + + + public function scopeAuthUser($query, $user_id) + { + return $query->where('auth', 'user')->where('auth_id', $user_id); + } + + + public function getAuthModelAttr($value, $data) + { + return $this->{$data['auth']}; + } + + public function getAuthTextAttr($value, $data) + { + return self::$authType[$data['auth']]['name'] ?? ''; + } + + + public function admin() + { + return $this->belongsTo(Admin::class, 'auth_id'); + } + + public function customerService() + { + return $this->belongsTo(CustomerService::class, 'customer_service_id'); + } + + public function user() + { + return $this->belongsTo(ShopUser::class, 'auth_id'); + } +} diff --git a/application/admin/model/shopro/chat/Question.php b/application/admin/model/shopro/chat/Question.php new file mode 100644 index 0000000..1271dd9 --- /dev/null +++ b/application/admin/model/shopro/chat/Question.php @@ -0,0 +1,23 @@ +where('room_id', $room_id); + } +} diff --git a/application/admin/model/shopro/chat/Record.php b/application/admin/model/shopro/chat/Record.php new file mode 100644 index 0000000..27a1dfb --- /dev/null +++ b/application/admin/model/shopro/chat/Record.php @@ -0,0 +1,94 @@ +where('sender_identify', 'customer'); + } + + + public function scopeCustomerService($query) + { + return $query->where('sender_identify', 'customer_service'); + } + + public function scopeNoRead($query) + { + return $query->whereNull('read_time'); + } + + + public function setMessageAttr($value, $data) + { + switch ($data['message_type']) { + case 'order': + case 'goods': + $value = is_array($value) ? json_encode($value) : $value; + break; + default : + $value = $value; + } + + return $value; + } + + + /** + * 处理消息 + * + * @param string $value + * @param array $data + * @return string + */ + public function getMessageAttr($value, $data) + { + switch($data['message_type']) { + case 'order': + case 'goods': + $message = json_decode($value, true); + + break; + default : + $message = $value; + break; + } + + + // if ($data['message_type'] == 'image') { + // $message = Online::cdnurl($value); + // } else if (in_array($data['message_type'], ['order', 'goods'])) { + // $messageArr = json_decode($value, true); + // if (isset($messageArr['image']) && $messageArr['image']) { + // $messageArr['image'] = Online::cdnurl($messageArr['image']); + // } + + // $message = json_encode($messageArr); + // } else if ($data['message_type'] == 'text') { + // // 全文匹配图片拼接 cdnurl + // $url = Online::cdnurl('/uploads'); + // $message = str_replace("belongsTo(User::class, 'chat_user_id'); + } +} diff --git a/application/admin/model/shopro/chat/User.php b/application/admin/model/shopro/chat/User.php new file mode 100644 index 0000000..30c348e --- /dev/null +++ b/application/admin/model/shopro/chat/User.php @@ -0,0 +1,33 @@ + 'timestamp', + ]; + + public function customer() + { + return $this->morphMany(Record::class, ['sender_identify', 'sender_id'], 'customer'); + } + + public function user() + { + return $this->belongsTo(ShopUser::class, 'auth_id'); + } + + + public function customerService() + { + return $this->belongsTo(CustomerService::class, 'customer_service_id'); + } +} diff --git a/application/admin/model/shopro/chat/traits/ChatCommon.php b/application/admin/model/shopro/chat/traits/ChatCommon.php new file mode 100644 index 0000000..b52585f --- /dev/null +++ b/application/admin/model/shopro/chat/traits/ChatCommon.php @@ -0,0 +1,31 @@ + '总后台', 'value' => 'admin'], + // ['name' => '官网', 'value' => 'official'], + // ['name' => '商城', 'value' => 'shop'] + ]; + } + + + public function getRoomNameAttr($value, $data) + { + $value = $value ?: ($data['room_id'] ?? null); + + $list = array_column(self::defaultRooms(), null, 'value'); + return isset($list[$value]) ? $list[$value]['name'] : $value; + } + +} diff --git a/application/admin/model/shopro/commission/Agent.php b/application/admin/model/shopro/commission/Agent.php new file mode 100644 index 0000000..84e2802 --- /dev/null +++ b/application/admin/model/shopro/commission/Agent.php @@ -0,0 +1,83 @@ + 'timestamp', + 'apply_info' => 'json', + 'child_agent_level_1' => 'json', + 'child_agent_level_all' => 'json', + ]; + protected $append = [ + 'status_text', + 'pending_reward' + ]; + + // 分销商状态 AGENT_STATUS + const AGENT_STATUS_NORMAL = 'normal'; // 正常 + const AGENT_STATUS_PENDING = 'pending'; // 审核中 不分佣、不打款、没有团队信息 + const AGENT_STATUS_FREEZE = 'freeze'; // 冻结 正常记录分佣、不打款,记录业绩和团队信息 冻结解除后立即打款 + const AGENT_STATUS_FORBIDDEN = 'forbidden'; // 禁用 不分佣、不记录业绩和团队信息 + const AGENT_STATUS_NEEDINFO = 'needinfo'; // 需要完善表单资料 临时状态 + const AGENT_STATUS_REJECT = 'reject'; // 审核驳回, 重新修改 临时状态 + const AGENT_STATUS_NULL = NULL; // 未满足成为分销商条件 + + + // 分销商升级锁 UPGRADE_LOCK + const UPGRADE_LOCK_OPEN = 1; // 禁止分销商升级 + const UPGRADE_LOCK_CLOSE = 0; // 允许分销商升级 + + public function statusList() + { + return [ + 'normal' => '正常', + 'pending' => '审核中', + 'freeze' => '冻结', + 'forbidden' => '禁用', + 'reject' => '拒绝' + ]; + } + + /** + * 可用分销商 + */ + public function scopeAvaliable($query) + { + return $query->where('status', 'in', [self::AGENT_STATUS_NORMAL, self::AGENT_STATUS_FREEZE]); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id')->field('id, nickname, avatar, mobile, total_consume, parent_user_id'); + } + + public function levelInfo() + { + return $this->belongsTo(Level::class, 'level', 'level')->field(['level', 'name', 'image', 'commission_rules']); + } + + public function getPendingRewardAttr($value, $data) + { + $amount = Reward::pending()->where('agent_id', $data['user_id'])->sum('commission'); + return number_format($amount, 2, '.', ''); + } + + public function levelStatusInfo() + { + return $this->belongsTo(Level::class, 'level_status', 'level'); + } + + public function upgradeLevel() + { + return $this->belongsTo(Level::class, 'level_status', 'level'); + } +} diff --git a/application/admin/model/shopro/commission/CommissionGoods.php b/application/admin/model/shopro/commission/CommissionGoods.php new file mode 100644 index 0000000..823d038 --- /dev/null +++ b/application/admin/model/shopro/commission/CommissionGoods.php @@ -0,0 +1,47 @@ + 'json' + ]; + protected $append = [ + 'status_text' + ]; + + public function statusList() + { + return [ + 0 => '不参与', + 1 => '参与中' + ]; + } + + public function getCommissionConfigAttr($value, $data) + { + return json_decode($value, true); + } + + public function goods() + { + return $this->belongsTo(GoodsModel::class, 'goods_id', 'id'); + } +} diff --git a/application/admin/model/shopro/commission/Level.php b/application/admin/model/shopro/commission/Level.php new file mode 100644 index 0000000..1067b1c --- /dev/null +++ b/application/admin/model/shopro/commission/Level.php @@ -0,0 +1,20 @@ + 'json', + 'upgrade_rules' => 'json' + ]; + +} diff --git a/application/admin/model/shopro/commission/Log.php b/application/admin/model/shopro/commission/Log.php new file mode 100644 index 0000000..9c3003f --- /dev/null +++ b/application/admin/model/shopro/commission/Log.php @@ -0,0 +1,232 @@ + $agentId, + 'event' => $event, + 'remark' => $remark, + 'oper_type' => $oper['type'], + 'oper_id' => $oper['id'], + 'createtime' => time() + ]; + return self::create($log); + } + return NULL; + } + + public static function setAgentEvent($ext) + { + switch ($ext['type']) { + case 'status': // 变更状态 + switch ($ext['value']) { + case Agent::AGENT_STATUS_PENDING: + $remark = "您的资料已提交,等待管理员审核"; + break; + case Agent::AGENT_STATUS_FORBIDDEN: + $remark = "您的账户已被禁用"; + break; + case Agent::AGENT_STATUS_NORMAL: + $remark = "恭喜您成为分销商"; + break; + case Agent::AGENT_STATUS_FREEZE: + $remark = "您的账户已被冻结"; + break; + case Agent::AGENT_STATUS_REJECT: + $remark = "您的申请已被拒绝,请重新申请"; + break; + } + break; + case 'level': // 变更等级 + $remark = "您的等级已变更为[{$ext['level']['name']}]"; + break; + case 'apply_info': + $remark = '您的分销商资料信息已更新'; + break; + } + return $remark ?? ""; + } + + public static function setShareEvent($ext) + { + $remark = "您已成为用户[{$ext['user']['nickname']}]的推荐人"; + return $remark; + } + + public static function setBindEvent($ext) + { + $remark = ""; + if ($ext['user']) { + $remark = "用户[{$ext['user']['nickname']}]已绑定为您的推荐人"; + } + return $remark; + } + + public static function setOrderEvent($ext) + { + switch ($ext['type']) { + case 'paid': + $goodsName = $ext['item']['goods_title']; + if (mb_strlen($goodsName) > 9) { + $goodsName = mb_substr($goodsName, 0, 5) . '...' . mb_substr($goodsName, -3); + } + if ($ext['order']['self_buy'] == 1) { + $remark = "您购买了{$goodsName},为您新增业绩{$ext['order']['amount']}元, +1分销订单"; + } else { + $remark = "用户{$ext['buyer']['nickname']}购买了{$goodsName},为您新增业绩{$ext['order']['amount']}元, +1分销订单"; + } + break; + case 'refund': + $remark = "用户{$ext['buyer']['nickname']}已退款,扣除业绩{$ext['order']['amount']}元, -1分销订单"; + break; + case 'admin': + $remark = "扣除业绩{$ext['order']['amount']}元, -1分销订单"; + break; + } + return $remark; + } + + public static function setRewardEvent($ext) + { + $actionStr = ''; + $remark = ''; + switch ($ext['type']) { + case 'paid': + $actionStr = '支付成功'; + break; + case 'confirm': + $actionStr = '已确认收货'; + break; + case 'finish': + $actionStr = '已完成订单'; + break; + } + if ($actionStr !== '') { + $remark = "用户{$actionStr}, "; + } + switch ($ext['reward']['status']) { + case Reward::COMMISSION_REWARD_STATUS_PENDING: + $rewardStatus = '待入账'; + break; + case Reward::COMMISSION_REWARD_STATUS_ACCOUNTED: + $rewardStatus = '已入账'; + break; + case Reward::COMMISSION_REWARD_STATUS_BACK: + $rewardStatus = '已扣除'; + break; + case Reward::COMMISSION_REWARD_STATUS_CANCEL: + $rewardStatus = '已取消'; + break; + } + $remark .= "您有{$ext['reward']['commission']}元佣金{$rewardStatus}"; + + return $remark; + } + + + public function eventList() + { + return [ + 'agent' => '分销商', + 'order' => '订单', + 'reward' => '佣金', + 'share' => '推荐', + 'bind' => '绑定', + ]; + } + + + public function operTypeList() + { + return [ + 'user' => '用户', + 'admin' => '管理员', + 'system' => '系统', + ]; + } + + + /** + * 事件类型 + * + * @param string $value + * @param array $data + * @return string + */ + public function getEventTextAttr($value, $data) + { + $value = $value ?: ($data['event'] ?? null); + + $list = $this->eventList(); + return isset($list[$value]) ? $list[$value] : '-'; + } + + + /** + * 操作人类型 + * + * @param string $value + * @param array $data + * @return string + */ + public function getOperTypeTextAttr($value, $data) + { + $value = $value ?: ($data['oper_type'] ?? null); + + $list = $this->operTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function agent() + { + return $this->belongsTo(UserModel::class, 'agent_id', 'id')->field('id, username, nickname, avatar, gender'); + } +} diff --git a/application/admin/model/shopro/commission/Order.php b/application/admin/model/shopro/commission/Order.php new file mode 100644 index 0000000..b0d5e67 --- /dev/null +++ b/application/admin/model/shopro/commission/Order.php @@ -0,0 +1,116 @@ + 'json', + 'commission_time' => 'timestamp' + ]; + + protected $append = [ + 'reward_event_text', + 'reward_type_text', + 'commission_order_status_text', + 'commission_reward_status_text' + ]; + + public function getRewardEventTextAttr($value, $data) + { + $value = $value ?: ($data['reward_event'] ?? ''); + $eventMap = [ + 'paid' => '支付后结算', + 'confirm' => '收货后结算', + 'finish' => '订单完成结算', + 'admin' => '手动结算' + ]; + return isset($eventMap[$value]) ? $eventMap[$value] : '-'; + } + + public function getRewardTypeTextAttr($value, $data) + { + $value = $value ?: ($data['reward_type'] ?? ''); + $eventMap = [ + 'goods_price' => '商品价', + 'pay_price' => '实际支付价' + ]; + return isset($eventMap[$value]) ? $eventMap[$value] : '-'; + } + + public function getCommissionOrderStatusTextAttr($value, $data) + { + $value = $value ?: ($data['commission_order_status'] ?? ''); + $eventMap = [ + -2 => '已扣除', + -1 => '已取消', + 0 => '不计入', + 1 => '已计入' + ]; + return isset($eventMap[$value]) ? $eventMap[$value] : '-'; + } + + public function getCommissionRewardStatusTextAttr($value, $data) + { + $value = $value ?: ($data['commission_reward_status'] ?? ''); + $eventMap = [ + -2 => '已退回', + -1 => '已取消', + 0 => '未结算', + 1 => '已结算' + ]; + return isset($eventMap[$value]) ? $eventMap[$value] : '-'; + } + + public function scopeBack($query) + { + return $query->where('commission_order_status', self::COMMISSION_ORDER_STATUS_BACK); + } + + public function scopeYes($query) + { + return $query->where('commission_order_status', self::COMMISSION_ORDER_STATUS_YES); + } + + public function scopeCancel($query) + { + return $query->where('commission_order_status', self::COMMISSION_ORDER_STATUS_CANCEL); + } + + public function buyer() + { + return $this->belongsTo(UserModel::class, 'buyer_id', 'id')->field('id, nickname, avatar, mobile'); + } + + public function agent() + { + return $this->belongsTo(UserModel::class, 'agent_id', 'id')->field('id, nickname, avatar, mobile'); + } + + public function order() + { + return $this->belongsTo(OrderModel::class, 'order_id', 'id'); + } + + public function orderItem() + { + return $this->belongsTo(OrderItemModel::class, 'order_item_id', 'id'); + } + + public function rewards() + { + return $this->hasMany(Reward::class, 'commission_order_id', 'id'); + } +} diff --git a/application/admin/model/shopro/commission/Reward.php b/application/admin/model/shopro/commission/Reward.php new file mode 100644 index 0000000..1a259c4 --- /dev/null +++ b/application/admin/model/shopro/commission/Reward.php @@ -0,0 +1,112 @@ + 'json', + 'commission_time' => 'timestamp' + ]; + protected $append = [ + 'status_text', + 'type_text' + ]; + + public function statusList() + { + return [ + -2 => '已退回', + -1 => '已取消', + 0 => '未结算', + 1 => '已结算' + ]; + } + + public function typeList() + { + return [ + 'commission' => '佣金钱包', + 'money' => '余额钱包', + 'score' => '积分钱包', + 'bank' => '企业付款到银行卡', + 'change' => '企业付款到零钱' + ]; + } + + /** + * 待入账 + */ + public function scopePending($query) + { + return $query->where('status', self::COMMISSION_REWARD_STATUS_PENDING); + } + /** + * 已退回 + */ + public function scopeBack($query) + { + return $query->where('status', self::COMMISSION_REWARD_STATUS_BACK); + } + + /** + * 已入账 + */ + public function scopeAccounted($query) + { + return $query->where('status', self::COMMISSION_REWARD_STATUS_ACCOUNTED); + } + + /** + * 已取消 + */ + public function scopeCancel($query) + { + return $query->where('status', self::COMMISSION_REWARD_STATUS_CANCEL); + } + + /** + * 待入账和已入账 + * + * @return void + */ + public function scopeIncome($query) + { + return $query->where('status', 'in', [self::COMMISSION_REWARD_STATUS_ACCOUNTED, self::COMMISSION_REWARD_STATUS_PENDING]); + } + + public function buyer() + { + return $this->belongsTo(UserModel::class, 'buyer_id', 'id')->field('id, nickname, avatar, mobile'); + } + + public function agent() + { + return $this->belongsTo(UserModel::class, 'agent_id', 'id')->field('id, nickname, avatar, mobile'); + } + + public function order() + { + return $this->belongsTo(OrderModel::class, 'order_id', 'id'); + } + + public function orderItem() + { + return $this->belongsTo(OrderItemModel::class, 'order_item_id', 'id'); + } +} diff --git a/application/admin/model/shopro/data/Area.php b/application/admin/model/shopro/data/Area.php new file mode 100644 index 0000000..fd779a8 --- /dev/null +++ b/application/admin/model/shopro/data/Area.php @@ -0,0 +1,19 @@ +hasMany(self::class, 'pid', 'id'); + } +} diff --git a/application/admin/model/shopro/data/Express.php b/application/admin/model/shopro/data/Express.php new file mode 100644 index 0000000..21b2f8e --- /dev/null +++ b/application/admin/model/shopro/data/Express.php @@ -0,0 +1,15 @@ + __('Male'), '0' => __('Female')]; + return isset($list[$value]) ? $list[$value] : ''; + } + +} diff --git a/application/admin/model/shopro/data/Faq.php b/application/admin/model/shopro/data/Faq.php new file mode 100644 index 0000000..d221f50 --- /dev/null +++ b/application/admin/model/shopro/data/Faq.php @@ -0,0 +1,20 @@ +hasMany(self::class, 'group', 'group')->order('id asc'); + } +} diff --git a/application/admin/model/shopro/data/Richtext.php b/application/admin/model/shopro/data/Richtext.php new file mode 100644 index 0000000..cdeef37 --- /dev/null +++ b/application/admin/model/shopro/data/Richtext.php @@ -0,0 +1,18 @@ + '店铺模板', + 'diypage' => '自定义页面' + ]; + } + + public function scopeTemplate($query) + { + return $query->where('type', 'template'); + } + + + public function scopeTypeDiypage($query) + { + return $query->where('type', 'diypage'); + } + + public function scopeDesigner($query) + { + return $query->where('type', 'designer'); + } + + public function page() + { + return $this->hasMany(Page::class, 'decorate_id', 'id'); + } + + public function diypage() + { + return $this->hasOne(Page::class, 'decorate_id', 'id')->where('type', 'diypage'); + } + + public function getPlatformAttr($value, $data) + { + if($value) { + return explode(',', $value); + }else { + return []; + } + } + + public function setPlatformAttr($value, $data) + { + if($value) { + return implode(',', $value); + }else { + return ""; + } + } +} diff --git a/application/admin/model/shopro/decorate/Page.php b/application/admin/model/shopro/decorate/Page.php new file mode 100644 index 0000000..3b0f508 --- /dev/null +++ b/application/admin/model/shopro/decorate/Page.php @@ -0,0 +1,37 @@ +domain() . "/storage/", $value); + // $value = str_replace("\"\/storage\/", "\"" . request()->domain() . "/storage/", $value); + + return json_decode($value, true); + } + + public static function buildData($template) + { + foreach ($template['home']['data'] as $data) { + switch ($data['type']) { + case 'goodsCard': + break; + } + } + // exit(); + } +} diff --git a/application/admin/model/shopro/dispatch/Dispatch.php b/application/admin/model/shopro/dispatch/Dispatch.php new file mode 100644 index 0000000..9dbc438 --- /dev/null +++ b/application/admin/model/shopro/dispatch/Dispatch.php @@ -0,0 +1,31 @@ +where('status', 'normal'); + } + + public function express() + { + return $this->hasMany(DispatchExpress::class, 'dispatch_id')->order('weigh', 'desc')->order('id', 'asc'); + } + + + public function autosend() + { + return $this->hasOne(DispatchAutosend::class, 'dispatch_id'); + } +} diff --git a/application/admin/model/shopro/dispatch/DispatchAutosend.php b/application/admin/model/shopro/dispatch/DispatchAutosend.php new file mode 100644 index 0000000..a5d0404 --- /dev/null +++ b/application/admin/model/shopro/dispatch/DispatchAutosend.php @@ -0,0 +1,35 @@ +field('name')->select(); + $districtText = collection($districtText)->column('name'); + return implode(',', $districtText); + } + +} diff --git a/application/admin/model/shopro/goods/Comment.php b/application/admin/model/shopro/goods/Comment.php new file mode 100644 index 0000000..06528c7 --- /dev/null +++ b/application/admin/model/shopro/goods/Comment.php @@ -0,0 +1,94 @@ + 'json', + 'reply_time' => 'timestamp', + ]; + + protected $append = [ + 'status_text' + ]; + + protected $hidden = [ + 'user_type' + ]; + + public static $typeAll = [ + 'all' => ['code' => 'all', 'name' => '全部'], + 'images' => ['code' => 'images', 'name' => '有图'], + 'good' => ['code' => 'good', 'name' => '好评'], + 'moderate' => ['code' => 'moderate', 'name' => '中评'], + 'bad' => ['code' => 'bad', 'name' => '差评'], + ]; + + public function scopeImages($query) + { + return $query->whereNotNull('images')->where('images', '<>', '')->where('images', '<>', '[]'); + } + + public function scopeGood($query) + { + return $query->where('level', 'in', [5, 4]); + } + + public function scopeModerate($query) + { + return $query->where('level', 'in', [3, 2]); + } + + public function scopeBad($query) + { + return $query->where('level', 1); + } + + public function scopeNoReply($query) + { + return $query->whereNull('reply_time'); + } + + + public function getUserNicknameAttr($value, $data) + { + $value = $value ?: ($data['user_nickname'] ?? ''); + + return $value ? string_hide($value, 2) : $value; + } + + + + public function admin() + { + return $this->belongsTo(\app\admin\model\Admin::class, 'admin_id', 'id')->field('id,username,nickname,avatar'); + } + + public function goods() + { + return $this->belongsTo(\app\admin\model\shopro\goods\Goods::class, 'goods_id', 'id'); + } + + public function order() + { + return $this->belongsTo(\app\admin\model\shopro\order\Order::class, 'order_id', 'id'); + } + + + public function orderItem() + { + return $this->belongsTo(\app\admin\model\shopro\order\OrderItem::class, 'order_item_id', 'id'); + } +} diff --git a/application/admin/model/shopro/goods/Goods.php b/application/admin/model/shopro/goods/Goods.php new file mode 100644 index 0000000..7fa0111 --- /dev/null +++ b/application/admin/model/shopro/goods/Goods.php @@ -0,0 +1,535 @@ + 'json', + 'image_wh' => 'json', + 'params' => 'json', + ]; + + // 追加属性 + protected $append = [ + 'status_text', + 'type_text', + 'dispatch_type_text' + ]; + + + protected $hidden = [ + 'content', + 'max_sku_price', + 'score_sku_prices', + 'activity_sku_prices', + 'total_sales' // 商品列表,销量排序会用到 + ]; + + /** + * type 中文 + */ + public function typeList() + { + return [ + 'normal' => '实体商品', + 'virtual' => '虚拟商品', + 'card' => '电子卡密', + ]; + } + + /** + * status 中文 + */ + public function statusList() + { + return [ + 'up' => '上架中', + 'down' => '已下架', + 'hidden' => '已隐藏', + ]; + } + + /** + * status 中文 + */ + public function dispatchTypeList() + { + return [ + 'express' => '快递物流', + 'autosend' => '自动发货', + 'custom' => '商家发货' + ]; + } + + + /** + * 修改器 service_ids + * + * @param array|string $value + * @param array $data + * @return string + */ + public function setServiceIdsAttr($value, $data) + { + $service_ids = is_array($value) ? join(',', $value) : $value; + return $service_ids; + } + + + public function setIsOfflineAttr($value, $data) + { + // 除了实体商品,其他都是 0 + return $data['type'] == 'normal' ? $value : 0; + } + + + public function scopeShow($query) + { + return $query->whereIn('status', ['up', 'hidden']); + } + + + public function getDispatchTypeTextAttr($value, $data) + { + $value = $value ?: ($data['dispatch_type'] ?? null); + + $list = $this->dispatchTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getPriceAttr($value, $data) + { + // 前端传入的 session_id + $activity_id = session('goods-activity_id:' . $data['id']); + if ($activity_id && $this->activity) { + // 活动商品的价格 + $skuPrices = $data['new_sku_prices'] ?? []; + $prices = $skuPrices instanceof \think\Collection ? $skuPrices->column('min_price') : array_column($skuPrices, 'min_price'); + $maxPrices = $skuPrices instanceof \think\Collection ? $skuPrices->column('max_price') : array_column($skuPrices, 'max_price'); + $min_price = $prices ? min($prices) : $data['price']; + $max_price = $maxPrices ? max($maxPrices) : $data['price']; + $priceArr[] = $min_price; + if ($min_price < $max_price) { + $priceArr[] = $max_price; + } + } else if (isset($data['show_score_shop'])) { + // 积分商品价格 + $skuPrices = $data['new_sku_prices'] ?? []; + $skuPrices = $skuPrices instanceof \think\Collection ? $skuPrices->column(null, 'score') : array_column($skuPrices, null, 'score'); + ksort($skuPrices); + $skuPrice = current($skuPrices); // 需要积分最少的规格 + if ($skuPrice) { + // 不自动拼接积分 + // $price = $skuPrice['score'] . '积分'; + // if ($skuPrice['price'] > 0) { + // $price .= '+¥' . $skuPrice['price']; + // } + // $priceArr[] = $price; + $priceArr[] = $skuPrice['price']; + } else { + // 防止没有规格 + $priceArr[] = $data['price']; + } + } else { + // 普通商品的价格区间 + $price = $value ? $value : ($data['price'] ?? 0); + $priceArr = [$price]; + if ($price && isset($data['is_sku']) && $data['is_sku']) { + $max_price = $this->max_sku_price->price; + if ($price < $max_price) { + $priceArr[] = $max_price; + } + } + } + + return $priceArr; + } + + + + /** + * 这个目前只有拼团单独购买要使用 + * + * @return void + */ + public function getOriginalGoodsPriceAttr($value, $data) + { + $activity_id = session('goods-activity_id:' . $data['id']); + $priceArr = []; + if ($activity_id && $this->activity) { + // 活动商品的价格 + $skuPrices = $data['new_sku_prices'] ?? []; + $prices = $skuPrices instanceof \think\Collection ? $skuPrices->column('old_price') : array_column($skuPrices, 'old_price'); + $min_price = $prices ? min($prices) : $data['price']; + $max_price = $prices ? max($prices) : $data['price']; + $priceArr[] = $min_price; + if ($min_price < $max_price) { + $priceArr[] = $max_price; + } + } + + return $priceArr; + } + + + /** + * 前端积分商城列表,获取默认所需积分(价格不自动拼接积分了,所以这里单独设置一个属性) + */ + public function getScoreAttr($value, $data) + { + // 积分商品价格 + $skuPrices = $data['new_sku_prices'] ?? []; + $skuPrices = $skuPrices instanceof \think\Collection ? $skuPrices->column(null, 'score') : array_column($skuPrices, null, 'score'); + ksort($skuPrices); + $skuPrice = current($skuPrices); // 需要积分最少的规格 + if ($skuPrice) { + $scoreAmount = $skuPrice['score']; + } else { + // 防止没有规格 + $scoreAmount = 0; + } + + return $scoreAmount; + } + + + public function getSalesAttr($value, $data) + { + // 前端传入的 session_id + $activity_id = session('goods-activity_id:' . $data['id']); + $sales = $data['sales'] ?? 0; + $sales += ($data['show_sales'] ?? 0); + if ($activity_id && $this->activity) { + if ($this->activity['rules'] && isset($this->activity['rules']['sales_show_type']) && $this->activity['rules']['sales_show_type'] == 'real') { + // 活动设置显示真实销量 + $skuPrices = $data['new_sku_prices'] ?? []; + $sales = array_sum($skuPrices instanceof \think\Collection ? $skuPrices->column('sales') : array_column($skuPrices, 'sales')); + } + } else if (isset($data['show_score_shop'])) { + // 积分商城显示真实销量 + $skuPrices = $data['new_sku_prices'] ?? []; + $sales = array_sum($skuPrices instanceof \think\Collection ? $skuPrices->column('sales') : array_column($skuPrices, 'sales')); + } + + return $sales; + } + + + + /** + * 真实销量 + */ + public function getRealSalesAttr($value, $data) + { + $sales = $data['sales'] ?? 0; + return $sales; + } + + + /** + * 相关商品(包含活动)购买用户,只查三个 + * + * @param [type] $value + * @param [type] $data + * @return void + */ + public function getBuyersAttr($value, $data) + { + // 查询活动正在购买的人Goods + $activity_id = session('goods-activity_id:' . $data['id']); + $orderItems = OrderItem::with(['user' => function ($query) { + return $query->field('id,nickname,avatar'); + }])->whereExists(function ($query) { + $order_name = (new Order())->getQuery()->getTable(); + $order_item_name = (new OrderItem())->getQuery()->getTable(); + + $query->table($order_name)->where($order_item_name . '.order_id=' . $order_name . '.id')->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED, Order::STATUS_PENDING]); + }); + + if ($activity_id) { + $orderItems = $orderItems->where('activity_id', $activity_id); + } + + $orderItems = $orderItems->fieldRaw('max(id),user_id')->where('goods_id', $data['id'])->group('user_id')->limit(3)->select(); + + $user = []; + foreach ($orderItems as $item) { + if ($item['user']) { + $user[] = $item['user']; + } + } + + return $user; + } + + + public function getIsScoreShopAttr($value, $data) + { + $scoreGoodsIds = ScoreSkuPrice::group('goods_id')->cache(20)->column('goods_id'); + + return in_array($data['id'], $scoreGoodsIds) ? 1 : 0; + } + + + /** + * 获取当前商品所属分类的所有上级 + * + * @param string $value + * @param array $data + * @return array + */ + public function getCategoryIdsArrAttr($value, $data) + { + $categoryIds = $data['category_ids'] ? explode(',', $data['category_ids']) : []; + + $categoryIdsArr = []; + $category = new Category(); + + foreach ($categoryIds as $key => $category_id) { + $currentCategoryIds = (new Tree($category))->getParentFields($category_id); + if ($currentCategoryIds) { + $categoryIdsArr[] = $currentCategoryIds; + } + } + + return $categoryIdsArr; + } + + + /** + * 获取服务ids数组 + * + * @param [type] $value + * @param [type] $data + * @return void + */ + public function getServiceIdsAttr($value, $data) + { + $serviceIds = $this->attrFormatComma($value, $data, 'service_ids', true); + + return $serviceIds ? array_values(array_filter(array_map("intval", $serviceIds))) : $serviceIds; + } + + + + /** + * 获取所有服务列表 + * + * @param string $value + * @param array $data + * @return array + */ + public function getServiceAttr($value, $data) + { + $serviceIds = $this->attrFormatComma($value, $data, 'service_ids'); + + $serviceData = []; + if ($serviceIds) { + $serviceData = Service::whereIn('id', $serviceIds)->select(); + } + return $serviceData; + } + + + /** + * 获取规格列表 + * + * @param string $value + * @param array $data + * @return array + */ + public function getSkusAttr($value, $data) + { + $sku = Sku::with('children')->where('goods_id', $data['id'])->where('parent_id', 0)->select(); + return $sku; + } + + /** + * 获取规格项列表 + * + * @param string $value + * @param array $data + * @return array + */ + public function getSkuPricesAttr($value, $data) + { + $skuPrices = collection(SkuPrice::where('goods_id', $data['id'])->select()); + return $skuPrices; + } + + + + /** + * 获取器获取所有活动 + * + * @param string $value + * @param array $data + * @return array + */ + public function getActivitiesAttr($value, $data) + { + $activities = ActivityFacade::getGoodsActivitys($data['id']); + + return $activities; + } + + + /** + * 获取器获取指定活动 + * + * @param string $value + * @param array $data + * @return array + */ + public function getActivityAttr($value, $data) + { + $activity_id = session('goods-activity_id:' . $data['id']); + $activities = ActivityFacade::getGoodsActivityByActivity($data['id'], $activity_id); + + return $activities; + } + + + public function getPromosAttr($value, $data) + { + $promos = ActivityFacade::getGoodsPromos($data['id']); + + foreach ($promos as $key => $promo) { + $rules = $promo['rules']; + $rules['simple'] = true; + $tags = ActivityFacade::formatRuleTags($rules, $promo['type']); + + $promo['tag'] = $tags[0] ?? ''; + $promo['tags'] = $tags; + + $texts = ActivityFacade::formatRuleTexts($rules, $promo['type']); + $promo['texts'] = $texts; + + $promos[$key] = $promo; + } + + return $promos ?? []; + } + + + /** + * 积分商城价格,积分商城的属性 + * + * @param string $value + * @param array $data + * @return string + */ + public function getScorePriceAttr($value, $data) + { + $scoreSkuPrices = collection($this->score_sku_prices)->column(null, 'score'); + ksort($scoreSkuPrices); + $scoreSkuPrice = current($scoreSkuPrices); // 需要积分最少的规格 + // print_r($scoreSkuPrices);exit; + if ($scoreSkuPrice) { + $price['score'] = $scoreSkuPrice['score'] ?? 0; + $price['price'] = $scoreSkuPrice['price'] ?? 0; + return $price; + } else { + return null; + } + + // return $score . '积分' . ($price > 0 ? '+¥' . $price : ''); + } + + + /** + * 积分商城销量 + * + * @param string $value + * @param array $data + * @return string + */ + public function getScoreSalesAttr($value, $data) + { + $scoreSkuPrices = $this->score_sku_prices; + + return array_sum(collection($scoreSkuPrices)->column('sales')); + } + + + /** + * 积分商城库存 + * + * @param string $value + * @param array $data + * @return string + */ + public function getScoreStockAttr($value, $data) + { + $scoreSkuPrices = $this->score_sku_prices; + + return array_sum(collection($scoreSkuPrices)->column('stock')); + } + + + public function maxSkuPrice() + { + return $this->hasOne(SkuPrice::class, 'goods_id')->order('price', 'desc'); + } + + public function favorite() + { + $user = auth_user(); + $user_id = empty($user) ? 0 : $user->id; + return $this->hasOne(GoodsLog::class, 'goods_id', 'id')->where('user_id', $user_id)->favorite(); + } + + public function activitySkuPrices() + { + return $this->hasMany(ActivitySkuPriceModel::class, 'goods_id'); + } + + public function scoreSkuPrices() + { + return $this->hasMany(ScoreSkuPrice::class, 'goods_id')->up(); + } + + + /** + * 包含下架的积分规格 + */ + public function allScoreSkuPrices() + { + return $this->hasMany(ScoreSkuPrice::class, 'goods_id'); + } + + public function delScoreSkuPrices() + { + return $this->hasMany(ScoreSkuPrice::class, 'goods_id')->removeOption('soft_delete')->whereNotNull('deletetime'); // 只查被删除的记录,这里使用 onlyTrashed 报错 + } + + // -- commission code start -- + public function commissionGoods() + { + return $this->hasOne(\app\admin\model\shopro\commission\CommissionGoods::class, 'goods_id'); + } + // -- commission code end -- +} diff --git a/application/admin/model/shopro/goods/Service.php b/application/admin/model/shopro/goods/Service.php new file mode 100644 index 0000000..53cf014 --- /dev/null +++ b/application/admin/model/shopro/goods/Service.php @@ -0,0 +1,13 @@ +hasMany(self::class, 'parent_id'); + } +} diff --git a/application/admin/model/shopro/goods/SkuPrice.php b/application/admin/model/shopro/goods/SkuPrice.php new file mode 100644 index 0000000..08c4ea2 --- /dev/null +++ b/application/admin/model/shopro/goods/SkuPrice.php @@ -0,0 +1,38 @@ +attrFormatComma($value, $data, 'goods_sku_text', true); + return $arr ? array_values(array_filter($arr)) : $arr; + } + + + + public function activitySkuPrice() + { + return $this->hasOne(ActivitySkuPriceModel::class, 'goods_sku_price_id', 'id'); + } + + + public function scoreSkuPrice() + { + return $this->hasOne(ScoreSkuPrice::class, 'goods_sku_price_id', 'id'); + } +} diff --git a/application/admin/model/shopro/goods/StockLog.php b/application/admin/model/shopro/goods/StockLog.php new file mode 100644 index 0000000..ad38695 --- /dev/null +++ b/application/admin/model/shopro/goods/StockLog.php @@ -0,0 +1,33 @@ +belongsTo(Goods::class, 'goods_id', 'id')->field('id,title,image,is_sku'); + } + + + public function skuPrice() + { + return $this->belongsTo(SkuPrice::class, 'goods_sku_price_id', 'id'); + } + + + public function oper() + { + return $this->belongsTo(Admin::class, 'admin_id', 'id'); + } + +} diff --git a/application/admin/model/shopro/goods/StockWarning.php b/application/admin/model/shopro/goods/StockWarning.php new file mode 100644 index 0000000..f79ea0c --- /dev/null +++ b/application/admin/model/shopro/goods/StockWarning.php @@ -0,0 +1,34 @@ +belongsTo(Goods::class, 'goods_id', 'id'); + } + + + public function skuPrice() + { + return $this->belongsTo(SkuPrice::class, 'goods_sku_price_id', 'id'); + } + +} diff --git a/application/admin/model/shopro/notification/Config.php b/application/admin/model/shopro/notification/Config.php new file mode 100644 index 0000000..5e7ac9d --- /dev/null +++ b/application/admin/model/shopro/notification/Config.php @@ -0,0 +1,20 @@ + 'json', + ]; + + + protected $append = [ + 'status_text', + ]; +} diff --git a/application/admin/model/shopro/notification/Notification.php b/application/admin/model/shopro/notification/Notification.php new file mode 100644 index 0000000..75149d1 --- /dev/null +++ b/application/admin/model/shopro/notification/Notification.php @@ -0,0 +1,66 @@ + 'timestamp', + 'data' => 'array', + ]; + + public static $notificationType = [ + 'system' => '系统消息', + 'shop' => '商城消息', + // 'site' => '网站消息' + ]; + + + public function scopeNotificationType($query, $type) + { + if ($type) { + $query = $query->where('notification_type', $type); + } + + return $query; + } + + /** + * 将数据转换成可以显示成键值对的格式 + * + * @param string $value + * @param array $data + * @return array + */ + public function getDataAttr($value, $data) + { + $data = json_decode($data['data'], true); + if (isset($data['message_type']) && $data['message_type'] == 'notification') { + $messageData = $data['data']; + $class = new $data['class_name'](); + $fields = $class->returnField['fields'] ?? []; + $fields = array_column($fields, null, 'field'); + + $newData = []; + foreach ($messageData as $k => $d) { + $da = $fields[$k] ?? []; + if ($da) { + $da['value'] = $d; + $newData[] = $da; + } + } + + $data['data'] = $newData; + } + + return $data; + } +} diff --git a/application/admin/model/shopro/order/Action.php b/application/admin/model/shopro/order/Action.php new file mode 100644 index 0000000..314877f --- /dev/null +++ b/application/admin/model/shopro/order/Action.php @@ -0,0 +1,40 @@ +order_id = $order->id; + $self->order_item_id = is_null($item) ? 0 : $item->id; + $self->oper_type = $type; + $self->oper_id = $oper_id; + $self->order_status = $order->status; + $self->dispatch_status = is_null($item) ? 0 : $item->dispatch_status; + $self->comment_status = is_null($item) ? 0 : $item->comment_status; + $self->aftersale_status = is_null($item) ? 0 : $item->aftersale_status; + $self->refund_status = is_null($item) ? 0 : $item->refund_status; + $self->remark = $remark; + $self->save(); + + return $self; + } +} diff --git a/application/admin/model/shopro/order/Address.php b/application/admin/model/shopro/order/Address.php new file mode 100644 index 0000000..b23f4b7 --- /dev/null +++ b/application/admin/model/shopro/order/Address.php @@ -0,0 +1,19 @@ + '仅退款', + 'return' => '退货退款', + 'other' => '其他' + ]; + } + + + public function dispatchStatusList() + { + return [ + self::DISPATCH_STATUS_REFUSE => '已拒收', + self::DISPATCH_STATUS_NOSEND => '未发货', + self::DISPATCH_STATUS_SENDED => '已发货', + self::DISPATCH_STATUS_GETED => '已收货' + ]; + } + + + public function aftersaleStatusList() + { + return [ + self::AFTERSALE_STATUS_CANCEL => '已取消', + self::AFTERSALE_STATUS_REFUSE => '拒绝', + self::AFTERSALE_STATUS_NOOPER => '未处理', + self::AFTERSALE_STATUS_ING => '申请售后', + self::AFTERSALE_STATUS_COMPLETED => '售后完成' + ]; + } + + public function aftersaleStatusDescList() + { + return [ + self::AFTERSALE_STATUS_CANCEL => '买家取消了售后申请', + self::AFTERSALE_STATUS_REFUSE => '卖家拒绝了售后申请', + self::AFTERSALE_STATUS_NOOPER => '买家申请了售后,请及时处理', + self::AFTERSALE_STATUS_ING => '售后正在处理中', + self::AFTERSALE_STATUS_COMPLETED => '售后已完成' + ]; + } + + + + public function refundStatusList() + { + return [ + self::REFUND_STATUS_NOREFUND => '未退款', + self::REFUND_STATUS_AGREE => '同意退款', + ]; + } + + // 已取消 + public function scopeCancel($query) + { + return $query->where('aftersale_status', self::AFTERSALE_STATUS_CANCEL); + } + + // 已拒绝 + public function scopeRefuse($query) + { + return $query->where('aftersale_status', self::AFTERSALE_STATUS_REFUSE); + } + + public function scopeNoOper($query) + { + return $query->where('aftersale_status', self::AFTERSALE_STATUS_NOOPER); + } + + // 处理中 + public function scopeIng($query) + { + return $query->where('aftersale_status', self::AFTERSALE_STATUS_ING); + } + + + // 处理完成 + public function scopeCompleted($query) + { + return $query->where('aftersale_status', self::AFTERSALE_STATUS_COMPLETED); + } + + // 需要处理的,包含未处理,和处理中的,个人中心显示售后数量 + public function scopeNeedOper($query) + { + return $query->whereIn('aftersale_status', [self::AFTERSALE_STATUS_NOOPER, self::AFTERSALE_STATUS_ING]); + } + + /** + * 后台售后列表,主表是 order 表时不用用 scope 了 + * + * @param [type] $scope + * @return void + */ + public static function getScopeWhere($scope) + { + $where = []; + switch ($scope) { + case 'cancel': + $where['aftersale_status'] = self::AFTERSALE_STATUS_CANCEL; + break; + case 'refuse': + $where['aftersale_status'] = self::AFTERSALE_STATUS_REFUSE; + break; + case 'nooper': + $where['aftersale_status'] = self::AFTERSALE_STATUS_NOOPER; + break; + case 'ing': + $where['aftersale_status'] = self::AFTERSALE_STATUS_ING; + break; + case 'completed': + $where['aftersale_status'] = self::AFTERSALE_STATUS_COMPLETED; + break; + } + + return $where; + } + + // 可以取消 + public function scopeCanCancel($query) + { + // 未处理,处理中,可以取消 + return $query->where('aftersale_status', 'in', [ + self::AFTERSALE_STATUS_NOOPER, + self::AFTERSALE_STATUS_ING + ]); + } + + + // 可以操作 + public function scopeCanOper($query) + { + // 未处理,处理中,可以 操作退款,拒绝,完成 + return $query->where('aftersale_status', 'in', [ + self::AFTERSALE_STATUS_NOOPER, + self::AFTERSALE_STATUS_ING + ]); + } + + // 可以删除 + public function scopeCanDelete($query) + { + // 取消,拒绝,完成可以删除 + return $query->where('aftersale_status', 'in', [ + self::AFTERSALE_STATUS_CANCEL, + self::AFTERSALE_STATUS_REFUSE, + self::AFTERSALE_STATUS_COMPLETED + ]); + } + + + public function getDispatchStatusTextAttr($value, $data) + { + $value = $value ?: ($data['dispatch_status'] ?? null); + + $list = $this->dispatchStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getBtnsAttr($value, $data) + { + $btns = []; + switch ($data['aftersale_status']) { + case self::AFTERSALE_STATUS_NOOPER: + case self::AFTERSALE_STATUS_ING: + $btns[] = 'cancel'; + break; + case self::AFTERSALE_STATUS_CANCEL: + case self::AFTERSALE_STATUS_REFUSE: + case self::AFTERSALE_STATUS_COMPLETED: + $btns[] = 'delete'; + break; + } + + return $btns; + } + + + public function getAftersaleStatusTextAttr($value, $data) + { + $value = $value ?: ($data['aftersale_status'] ?? null); + + $list = $this->aftersaleStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getAftersaleStatusDescAttr($value, $data) + { + $value = $value ?: ($data['aftersale_status'] ?? null); + + $list = $this->aftersaleStatusDescList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + + + public function getRefundStatusTextAttr($value, $data) + { + $value = $value ?: ($data['refund_status'] ?? null); + + $list = $this->refundStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + /** + * 获取建议退款金额,不考虑剩余可退款金额是否够退 + * + * @param string $value + * @param array $data + * @return string + */ + public function getSuggestRefundFeeAttr($value, $data) + { + $current_goods_amount = bcmul($data['goods_price'], (string)$data['goods_num'], 2); + $total_amount = bcadd($current_goods_amount, $data['dispatch_fee'], 2); + $suggest_refund_fee = bcsub($total_amount, $data['discount_fee'], 2); // (商品金额 + 运费金额) - 总优惠(活动,优惠券,包邮优惠) + + return $suggest_refund_fee; + } + + public function logs() + { + return $this->hasMany(AftersaleLog::class, 'order_aftersale_id', 'id')->order('id', 'desc'); + } + + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } + + + public function order() + { + return $this->belongsTo(Order::class, 'order_id'); + } +} diff --git a/application/admin/model/shopro/order/AftersaleLog.php b/application/admin/model/shopro/order/AftersaleLog.php new file mode 100644 index 0000000..cf0690a --- /dev/null +++ b/application/admin/model/shopro/order/AftersaleLog.php @@ -0,0 +1,78 @@ + 'json', + ]; + + protected $append = [ + 'log_type_text' + ]; + + public function logTypeList() + { + return [ + 'apply_aftersale' => '售后服务单申请成功,等待售后处理', + 'cancel' => '用户取消申请售后', + 'delete' => '用户删除售后单', + 'completed' => '售后订单已完成', + 'refuse' => '卖家拒绝售后', + 'refund' => '卖家同意退款', + 'add_log' => '卖家留言' + ]; + } + + + + + public static function add($order = null, $aftersale = null, $oper = null, $type = 'user', $data = []) + { + $oper_id = $oper ? $oper['id'] : 0; + $images = $data['images'] ?? []; + + $self = new self(); + $self->order_id = $order ? $order->id : ($aftersale ? $aftersale->id : 0); + $self->order_aftersale_id = $aftersale ? $aftersale->id : 0; + $self->oper_type = $type; + $self->oper_id = $oper_id; + $self->dispatch_status = $aftersale ? $aftersale->dispatch_status : 0; + $self->aftersale_status = $aftersale ? $aftersale->aftersale_status : 0; + $self->refund_status = $aftersale ? $aftersale->refund_status : 0; + $self->log_type = $data['log_type']; + $self->content = $data['content'] ?? ''; + $self->images = $images; + $self->save(); + + // 售后单变动行为 + $data = ['aftersale' => $aftersale, 'order' => $order, 'aftersaleLog' => $self]; + \think\Hook::listen('order_aftersale_change', $data); + + return $self; + } + + + /** + * log 类型获取器 + * + * @param string $value + * @param array $data + * @return string + */ + public function getLogTypeTextAttr($value, $data) + { + $value = $value ?: ($data['log_type'] ?? null); + + $list = $this->logTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + +} diff --git a/application/admin/model/shopro/order/Express.php b/application/admin/model/shopro/order/Express.php new file mode 100644 index 0000000..fb93a1f --- /dev/null +++ b/application/admin/model/shopro/order/Express.php @@ -0,0 +1,49 @@ + 'json' + ]; + + // 追加属性 + protected $append = [ + 'status_text' + ]; + + + public function statusList() + { + return [ + 'noinfo' => '暂无信息', + 'collect' => '已揽件', + 'transport' => '运输中', + 'delivery' => '派送中', + 'signfor' => '已签收', + 'refuse' => '用户拒收', + 'difficulty' => '问题件', + 'invalid' => '无效件', + 'timeout' => '超时单', + 'fail' => '签收失败', + 'back' => '退回', + ]; + } + + + public function items() + { + return $this->hasMany(OrderItem::class, 'order_express_id', 'id'); + } + + + public function logs() + { + return $this->hasMany(ExpressLog::class, 'order_express_id', 'id')->order('id', 'desc'); + } +} diff --git a/application/admin/model/shopro/order/ExpressLog.php b/application/admin/model/shopro/order/ExpressLog.php new file mode 100644 index 0000000..6c652e9 --- /dev/null +++ b/application/admin/model/shopro/order/ExpressLog.php @@ -0,0 +1,37 @@ + '', + 'collect' => '已揽件', + 'transport' => '运输中', + 'delivery' => '派送中', + 'signfor' => '已签收', + 'refuse' => '用户拒收', + 'difficulty' => '问题件', + 'invalid' => '无效件', + 'timeout' => '超时单', + 'fail' => '签收失败', + 'back' => '退回', + ]; + } +} diff --git a/application/admin/model/shopro/order/Invoice.php b/application/admin/model/shopro/order/Invoice.php new file mode 100644 index 0000000..e630388 --- /dev/null +++ b/application/admin/model/shopro/order/Invoice.php @@ -0,0 +1,184 @@ + 'array', + 'finish_time' => 'timestamp' + ]; + + protected $hidden = [ + 'order_items' + ]; + + // 追加属性 + protected $append = [ + 'type_text', + 'status_text' + ]; + + + public function typeList() + { + return (new UserInvoiceModel)->typeList(); + } + + /** + * 状态 + * + * @return array + */ + public function statusList() + { + return [ + 'cancel' => '已取消', + 'unpaid' => '未支付', + 'waiting' => '等待开票', + 'finish' => '已开具', + ]; + } + + + public function getOrderStatusAttr($value, $data) + { + $order = $this->order; + $order_status = 'normal'; + + if (!$order) { + $order_status = 'order_deleted'; + } else if (in_array($order->status, [Order::STATUS_PAID, Order::STATUS_COMPLETED])) { + $items = $this->order_items; + $refund_num = 0; + $aftersale_num = 0; + foreach ($items as $item) { + if (in_array($item->refund_status, [ + OrderItem::REFUND_STATUS_AGREE, + OrderItem::REFUND_STATUS_COMPLETED + ])) { + $refund_num += 1; + } + + if (in_array($item->aftersale_status, [ + OrderItem::AFTERSALE_STATUS_REFUSE, + OrderItem::AFTERSALE_STATUS_ING, + OrderItem::AFTERSALE_STATUS_COMPLETED + ])) { + $aftersale_num += 1; + } + } + + if ($refund_num) { + $order_status = 'refund'; + if ($refund_num == count($items)) { + $order_status = 'refund_all'; + } + } else if ($aftersale_num) { + $order_status = 'aftersale'; + } + } else if ($order->isOffline($order)) { + $order_status = 'offline_unpaid'; + } + + return $order_status; + } + + + public function getOrderStatusTextAttr($value, $data) + { + $order_status = $this->order_status; + + switch($order_status) { + case 'order_deleted': + $order_status_text = '该订单已被删除'; + break; + case 'refund_all': + $order_status_text = '该订单已全部退款'; + break; + case 'refund': + $order_status_text = '该订单已部分退款'; + break; + case 'aftersale': + $order_status_text = '该订单已申请售后'; + break; + case 'offline_unpaid': + $order_status_text = '该订单货到付款-未付款'; + break; + default : + $order_status_text = ''; + } + + return $order_status_text; + } + + + + public function getOrderFeeAttr($value, $data) + { + $order = $this->order; + + if ($order && $order->pay_fee != $order->original_pay_fee) { + return [ + 'pay_fee' => $order->pay_fee, + 'original_pay_fee' => $order->original_pay_fee, + ]; + } + return null; + } + + + public function scopeCancel($query) + { + return $query->where('status', 'cancel'); + } + + + public function scopeWaiting($query) + { + return $query->where('status', 'waiting'); + } + + + public function scopeFinish($query) + { + return $query->where('status', 'finish'); + } + + + /** + * 不展示 未支付的 + */ + public function scopeShow($query) + { + return $query->whereIn('status', ['cancel', 'waiting', 'finish']); + } + + + + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } + + + + public function order() + { + return $this->belongsTo(Order::class, 'order_id', 'id'); + } + + + public function orderItems() + { + return $this->hasMany(OrderItem::class, 'order_id', 'order_id'); + } + + +} diff --git a/application/admin/model/shopro/order/Order.php b/application/admin/model/shopro/order/Order.php new file mode 100644 index 0000000..715e66b --- /dev/null +++ b/application/admin/model/shopro/order/Order.php @@ -0,0 +1,361 @@ + 'json', + 'paid_time' => 'timestamp', + ]; + + // 追加属性 + protected $append = [ + 'type_text', + 'status_code', + 'status_text', + 'status_desc', + 'apply_refund_status_text', + 'btns', + 'platform_text', + 'activity_type_text', + 'promo_types_text', + 'wechat_extra_data' + ]; + + + // 订单状态 + const STATUS_CLOSED = 'closed'; + const STATUS_CANCEL = 'cancel'; + const STATUS_UNPAID = 'unpaid'; + const STATUS_PAID = 'paid'; + const STATUS_COMPLETED = 'completed'; + const STATUS_PENDING = 'pending'; // 待定 后付款 + + + const APPLY_REFUND_STATUS_NOAPPLY = 0; + const APPLY_REFUND_STATUS_APPLY = 1; + const APPLY_REFUND_STATUS_FINISH = 2; + const APPLY_REFUND_STATUS_REFUSE = -1; + + + public function statusList() + { + return [ + 'closed' => '交易关闭', + 'cancel' => '已取消', + 'unpaid' => '未支付', + 'pending' => '待定', // 货到付款未付款状态 + 'paid' => '已支付', + 'completed' => '已完成' + ]; + } + + + public function applyRefundStatusList() + { + return [ + self::APPLY_REFUND_STATUS_NOAPPLY => '未申请', + self::APPLY_REFUND_STATUS_APPLY => '申请退款', + self::APPLY_REFUND_STATUS_FINISH => '退款完成', + self::APPLY_REFUND_STATUS_REFUSE => '拒绝申请' + ]; + } + + + /** + * 订单列表状态搜索 + */ + public function searchStatusList() + { + return [ + 'unpaid' => '待付款', + 'paid' => '已支付', // 包括刚支付的,发货中,和已退款的,以及已完成的所有付过款的订单,不包含货到付款还未真实付款的订单 + 'nosend' => '待发货', + 'noget' => '待收货', + 'refuse' => '已拒收', + 'nocomment' => '待评价', + 'completed' => '已完成', + 'aftersale' => '售后', + 'applyRefundIng' => '申请退款', + 'refund' => '已退款', + 'cancel' => '已取消', + 'closed' => '交易关闭', // 包含货到付款,拒收的商品 + ]; + } + + + public function typeList() + { + return [ + 'goods' => '商城订单', + 'score' => '积分订单' + ]; + } + + + public function platformList() + { + return [ + 'H5' => 'H5', + 'WechatOfficialAccount' => '微信公众号', + 'WechatMiniProgram' => '微信小程序', + 'App' => 'App', + ]; + } + + + public function getExtAttr($value, $data) + { + $ext = (isset($data['ext']) && $data['ext']) ? json_decode($data['ext'], true) : []; + + // 每类活动优惠金额聚合(可能订单购买多个商品,然后同时参与了两种满减,金额累加) + $ext['promo_discounts'] = [ + 'full_reduce' => 0, + 'full_discount' => 0, + 'free_shipping' => 0, + 'full_gift' => 0 + ]; + if ($ext && isset($ext['promo_infos']) && $ext['promo_infos']) { + foreach ($ext['promo_infos'] as $key => $info) { + if ($info['activity_type'] == 'full_gift') { + $ext['promo_discounts']['full_gift'] = 1; + continue; + } + $ext['promo_discounts'][$info['activity_type']] = bcadd((string)$ext['promo_discounts'][$info['activity_type']], (string)$info['promo_discount_money'], 2); + } + } + + // 格式化 + $ext['promo_discounts'] = array_map(function ($value) { + return number_format(floatval($value), 2, '.', ''); + }, $ext['promo_discounts']); + + // 处理时间 + if (isset($ext['closed_time']) && $ext['closed_time']) { + $ext['closed_date'] = date('Y-m-d H:i:s', $ext['closed_time']); + } + if (isset($ext['cancel_time']) && $ext['cancel_time']) { + $ext['cancel_date'] = date('Y-m-d H:i:s', $ext['cancel_time']); + } + if (isset($ext['pending_time']) && $ext['pending_time']) { + $ext['pending_date'] = date('Y-m-d H:i:s', $ext['pending_time']); + } + if (isset($ext['apply_refund_time']) && $ext['apply_refund_time']) { + $ext['apply_refund_date'] = date('Y-m-d H:i:s', $ext['apply_refund_time']); + } + if (isset($ext['send_time']) && $ext['send_time']) { + $ext['send_date'] = date('Y-m-d H:i:s', $ext['send_time']); + } + if (isset($ext['confirm_time']) && $ext['confirm_time']) { + $ext['confirm_date'] = date('Y-m-d H:i:s', $ext['confirm_time']); + } + if (isset($ext['comment_time']) && $ext['comment_time']) { + $ext['comment_date'] = date('Y-m-d H:i:s', $ext['comment_time']); + } + if (isset($ext['completed_time']) && $ext['completed_time']) { + $ext['completed_date'] = date('Y-m-d H:i:s', $ext['completed_time']); + } + if (isset($ext['refund_time']) && $ext['refund_time']) { + $ext['refund_date'] = date('Y-m-d H:i:s', $ext['refund_time']); + } + return $ext; + } + + + public function getPlatformTextAttr($value, $data) + { + $value = $value ?: ($data['platform'] ?? null); + + $list = $this->platformList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + + /** + * 微信小程序发货信息管理所需参数 + * + * @param string|null $value + * @param array $data + * @return array + */ + public function getWechatExtraDataAttr($value, $data) + { + $extraData = []; + + if (strpos(request()->url(), 'addons/shopro') !== false && $data['platform'] == 'WechatMiniProgram') { + // 前端接口,并且是 微信小程序订单,才返这个参数 + $pays = $this->pays; + foreach ($pays as $pay) { + if ($pay->status != PayModel::PAY_STATUS_UNPAID && $pay->pay_type == 'wechat') { + $extraData['merchant_trade_no'] = $pay->pay_sn; + $extraData['transaction_id'] = $pay->transaction_id; + } + } + } + + return $extraData; + } + + + + /** + * 申请退款状态 + * + * @param string $value + * @param array $data + * @return string + */ + public function getApplyRefundStatusTextAttr($value, $data) + { + $value = $value ?: ($data['apply_refund_status'] ?? null); + + $list = $this->applyRefundStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getActivityTypeTextAttr($value, $data) + { + $value = $value ?: ($data['activity_type'] ?? null); + $ext = $this->ext; + + $list = (new Activity)->typeList(); + $text = isset($list[$value]) ? $list[$value] : ''; + + if (in_array($value, ['groupon', 'groupon_ladder'])) { + // 订单已支付的,或者线下支付(货到付款)的 + if ((in_array($data['status'], [self::STATUS_PAID, self::STATUS_COMPLETED]) || $this->isOffline($data)) + && (!isset($ext['groupon_id']) || !$ext['groupon_id'])) + { + // 已支付,并且没有团 id,就是单独购买 + $text .= '-单独购买'; + } + } + + return $text; + } + + + public function getPromoTypesTextAttr($value, $data) + { + $value = $value ?: ($data['promo_types'] ?? null); + + $promoTypes = array_filter(explode(',', $value)); + $texts = []; + $list = (new Activity)->typeList(); + foreach ($promoTypes as $type) { + $text = isset($list[$type]) ? $list[$type] : ''; + if ($text) { + $texts[] = $text; + } + } + return $texts; + } + + + /** + * 已支付订单,支付类型 + * + * @param string $value + * @param array $data + * @return void + */ + public function getPayTypesAttr($value, $data) + { + $status = $data['status'] ?? ''; + $payTypes = []; + // 订单已支付的,或者线下支付(货到付款)的 + if (in_array($status, [self::STATUS_PAID, self::STATUS_COMPLETED]) || $this->isOffline($data)) { + $payTypes = PayModel::typeOrder()->where('order_id', $data['id'])->where('status', '<>', PayModel::PAY_STATUS_UNPAID)->group('pay_type')->column('pay_type'); + } + + return $payTypes; + } + /** + * 已支付订单,支付类型文字 + * + * @param string $value + * @param array $data + * @return void + */ + public function getPayTypesTextAttr($value, $data) + { + $payTypes = $this->pay_types; + $list = (new PayModel)->payTypeList(); + + $texts = []; + foreach ($payTypes as $pay_type) { + $text = isset($list[$pay_type]) ? $list[$pay_type] : ''; + + if ($text) { + $texts[] = $text; + } + } + + return $texts; + } + + + public function getExpressAttr($value, $data) + { + return Express::with(['items', 'logs'])->where('order_id', $data['id'])->select(); + } + + + + public function items() + { + return $this->hasMany(OrderItem::class, 'order_id', 'id'); + } + + + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } + + public function aftersales() + { + return $this->hasMany(Aftersale::class, 'order_id')->order('id', 'desc'); + } + + + public function address() + { + return $this->hasOne(Address::class, 'order_id', 'id'); + } + + public function invoice() + { + return $this->hasOne(Invoice::class, 'order_id', 'id'); + } + + + public function pays() + { + return $this->hasMany(PayModel::class, 'order_id', 'id')->typeOrder()->order('id', 'desc'); + } + + + public function activityOrders() + { + return $this->hasMany(ActivityOrder::class, 'order_id'); + } +} diff --git a/application/admin/model/shopro/order/OrderItem.php b/application/admin/model/shopro/order/OrderItem.php new file mode 100644 index 0000000..8958ed8 --- /dev/null +++ b/application/admin/model/shopro/order/OrderItem.php @@ -0,0 +1,227 @@ + 'json' + ]; + + // 追加属性 + protected $append = [ + 'dispatch_status_text', + 'dispatch_type_text', + 'aftersale_status_text', + 'refund_status_text', + 'comment_status_text', + 'status_code', + 'status_text', + 'status_desc', + 'btns', + 'activity_type_text', + 'promo_types_text' + ]; + + // 发货状态 + const DISPATCH_STATUS_REFUSE = -1; // 已拒收 + const DISPATCH_STATUS_NOSEND = 0; // 未发货 + const DISPATCH_STATUS_SENDED = 1; // 已发货 + const DISPATCH_STATUS_GETED = 2; // 已收货 + + + // 售后状态 + const AFTERSALE_STATUS_REFUSE = -1; // 拒绝 + const AFTERSALE_STATUS_NOAFTER = 0; // 未申请 + const AFTERSALE_STATUS_ING = 1; // 申请售后 + const AFTERSALE_STATUS_COMPLETED = 2; // 售后完成 + + + // 退款状态 + const REFUND_STATUS_NOREFUND = 0; // 退款状态 未申请 + const REFUND_STATUS_AGREE = 1; // 已同意 + const REFUND_STATUS_COMPLETED = 2; // 退款完成 + + // 评价状态 + const COMMENT_STATUS_NO = 0; // 待评价 + const COMMENT_STATUS_OK = 1; // 已评价 + + + public function dispatchStatusList() + { + return [ + self::DISPATCH_STATUS_REFUSE => '已拒收', + self::DISPATCH_STATUS_NOSEND => '待发货', + self::DISPATCH_STATUS_SENDED => '待收货', + self::DISPATCH_STATUS_GETED => '已收货' + ]; + } + + public function dispatchTypeList() + { + return [ + 'express' => '快递物流', + 'autosend' => '自动发货', + 'custom' => '手动发货' + ]; + } + + public function aftersaleStatusList() + { + return [ + self::AFTERSALE_STATUS_REFUSE => '售后驳回', + self::AFTERSALE_STATUS_NOAFTER => '未申请', + self::AFTERSALE_STATUS_ING => '申请售后', + self::AFTERSALE_STATUS_COMPLETED => '已完成' + ]; + } + + public function refundStatusList() + { + return [ + self::REFUND_STATUS_NOREFUND => '未退款', + self::REFUND_STATUS_AGREE => '退款完成', + self::REFUND_STATUS_COMPLETED => '退款完成', + ]; + } + + public function commentStatusList() + { + return [ + self::COMMENT_STATUS_NO => '待评价', + self::COMMENT_STATUS_OK => '已评价', + ]; + } + + public function getActivityTypeTextAttr($value, $data) + { + $value = $value ?: ($data['activity_type'] ?? null); + + $list = (new Activity)->typeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getDispatchTypeTextAttr($value, $data) + { + $value = $value ?: ($data['dispatch_type'] ?? null); + + $list = $this->dispatchTypeList(); + if (strpos(request()->url(), 'addons/shopro') !== false) { + $list['custom'] = '商家发货'; + } + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function getPromoTypesTextAttr($value, $data) + { + $value = $value ?: ($data['promo_types'] ?? null); + + $promoTypes = array_filter(explode(',', $value)); + $texts = []; + $list = (new Activity)->typeList(); + foreach ($promoTypes as $type) { + $text = isset($list[$type]) ? $list[$type] : ''; + if ($text) { + $texts[] = $text; + } + } + return $texts; + } + + public function getDispatchStatusTextAttr($value, $data) + { + $value = $value ?: ($data['dispatch_status'] ?? null); + + $list = $this->dispatchStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getAftersaleStatusTextAttr($value, $data) + { + $value = $value ?: ($data['aftersale_status'] ?? null); + + $list = $this->aftersaleStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getRefundStatusTextAttr($value, $data) + { + $value = $value ?: ($data['refund_status'] ?? null); + + $list = $this->refundStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getCommentStatusTextAttr($value, $data) + { + $value = $value ?: ($data['comment_status'] ?? null); + + $list = $this->commentStatusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + /** + * 获取建议退款金额,不考虑剩余可退款金额是否够退 + * + * @param string $value + * @param array $data + * @return string + */ + public function getSuggestRefundFeeAttr($value, $data) + { + $current_goods_amount = bcmul($data['goods_price'], (string)$data['goods_num'], 2); + $total_amount = bcadd($current_goods_amount, $data['dispatch_fee'], 2); + $suggest_refund_fee = bcsub($total_amount, $data['discount_fee'], 2); // (商品金额 + 运费金额) - 总优惠(活动,优惠券,包邮优惠) + + return $suggest_refund_fee; + } + + + /** + * 可以确认收货 + * + * @param \think\db\Query $query + * @return void + */ + public function scopeCanConfirm($query) + { + return $query->where('dispatch_status', OrderItem::DISPATCH_STATUS_SENDED) // 已发货 + ->whereNotIn('refund_status', [OrderItem::REFUND_STATUS_AGREE, OrderItem::REFUND_STATUS_COMPLETED]); // 没有退款完成 + } + + + /** + * 可以评价 + * + * @param \think\db\Query $query + * @return void + */ + public function scopeCanComment($query) + { + return $query->where('dispatch_status', OrderItem::DISPATCH_STATUS_GETED) // 已收货 + ->where('comment_status', OrderItem::COMMENT_STATUS_NO) // 未评价 + ->whereNotIn('refund_status', [OrderItem::REFUND_STATUS_AGREE, OrderItem::REFUND_STATUS_COMPLETED]); // 没有退款完成 + } + + public function express() + { + return $this->belongsTo(Express::class, 'order_express_id'); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } +} diff --git a/application/admin/model/shopro/order/traits/OrderItemStatus.php b/application/admin/model/shopro/order/traits/OrderItemStatus.php new file mode 100644 index 0000000..6cea976 --- /dev/null +++ b/application/admin/model/shopro/order/traits/OrderItemStatus.php @@ -0,0 +1,268 @@ +ext; + $aftersale_id = (isset($ext['aftersale_id']) && !empty($ext['aftersale_id'])) ? $ext['aftersale_id'] : 0; + + $status_code = $this->status_code; + + $item_code = 'null'; // 有售后的时候,第二状态 + if (strpos($status_code, '|') !== false) { + $codes = explode('|', $status_code); + $status_code = $codes[0] ?? 'null'; + $item_code = $codes[1] ?? 'null'; + } + + switch ($status_code) { + case 'null': + case 'cancel': + case 'closed': + case 'unpaid': + // 未支付的返回空 + break; + case 'refuse': // 拒收 + $status_text = '已拒收'; + $status_desc = '用户拒绝收货'; + break; + case 'nosend': + $status_text = '待发货'; + $status_desc = '等待卖家发货'; + $backendBtns[] = 'send'; + $backendBtns[] = 'refund'; // 退款 + // $btns[] = $aftersale_id ? 're_aftersale' : 'aftersale'; // 售后,售后取消会有 aftersale_id , 待发货商品,不用申请售后,直接用订单退款 + break; + case 'noget': + $status_text = '待收货'; + $status_desc = '等待买家收货'; + // $btns[] = 'get'; // 确认收货,总订单上才有 + $btns[] = $aftersale_id ? 're_aftersale' : 'aftersale'; // 售后,售后取消会有 aftersale_id + $backendBtns[] = 'send_cancel'; // 取消发货 + $backendBtns[] = 'refund'; + break; + case 'nocomment': + $status_text = '待评价'; + $status_desc = '等待买家评价'; + $btns[] = 'comment'; // 评价,总订单上才有(前端通过这个判断待评价商品) + $btns[] = $aftersale_id ? 're_aftersale' : 'aftersale'; // 售后,售后取消会有 aftersale_id + $backendBtns[] = 'refund'; // 退款 + break; + case 'commented': + $status_text = '已评价'; + $status_desc = '订单已评价'; + $btns[] = 'buy_again'; + $backendBtns[] = 'refund'; // 退款 + $backendBtns[] = 'comment_view'; // 查看评价 + break; + case 'refund_completed': + $status_text = '退款完成'; + $status_desc = '订单退款完成'; + break; + case 'refund_agree': // 不需要申请退款(状态不会出现) + $status_text = '退款完成'; + $status_desc = '卖家已同意退款'; + break; + case 'aftersale_ing': + $status_text = '售后中'; + $status_desc = '售后处理中'; + + if ($item_code == 'noget') { + if (in_array($data['dispatch_type'], ['express'])) { // 除了 自提扫码核销外,都可确认收货 + $backendBtns[] = 'send_cancel'; // 取消发货 + } + } else if ($item_code == 'nocomment') { + // 售后中也可以评价订单 + $btns[] = 'comment'; // 评价,总订单上才有(前端通过这个判断待评价商品) + } + break; + case 'aftersale_refuse': + case 'aftersale_completed': + switch ($status_code) { + case 'aftersale_refuse': + $status_text = '售后拒绝'; + $status_desc = '售后申请拒绝'; + if ($item_code != 'commented') { + $btns[] = 're_aftersale'; // 售后 + } + break; + case 'aftersale_completed': + $status_text = '售后完成'; + $status_desc = '售后完成'; + break; + } + + // 售后拒绝,或者完成的时候,还可以继续操作订单 + switch ($item_code) { + case 'nosend': + if (in_array($data['dispatch_type'], ['express'])) { // 除了 自提扫码核销外,都可确认收货 + $backendBtns[] = 'send'; // 发货 + } + break; + case 'noget': + if (in_array($data['dispatch_type'], ['express'])) { // 除了 自提扫码核销外,都可确认收货 + // $btns[] = 'get'; // 确认收货,总订单上才有 + $backendBtns[] = 'send_cancel'; // 取消发货 + } + break; + case 'nocomment': + $btns[] = 'comment'; // 评价,总订单上才有(前端通过这个判断待评价商品) + break; + case 'commented': + $btns[] = 'buy_again'; + $backendBtns[] = 'comment_view'; // 查看评价 + break; + } + + break; + } + + // 如果有售后id 就显示售后详情按钮,退款中可能是售后退的款 + if ($aftersale_id) { + $btns[] = 'aftersale_info'; + $backendBtns[] = 'aftersale_info'; + } + + $return = null; + switch($type) { + case 'status_text': + $return = $status_text; + break; + case 'btns': + $return = $btns; + break; + case 'status_desc': + $return = $status_desc; + break; + case 'backend_btns': + $return = $backendBtns; + break; + } + + return $return; + } + + + + public function getStatusTextAttr($value, $data) + { + return $this->getStatus($data, 'status_text'); + } + + public function getStatusDescAttr($value, $data) + { + return $this->getStatus($data, 'status_desc'); + } + + public function getBtnsAttr($value, $data) + { + $btn_name = strpos(request()->url(), 'addons/shopro') !== false ? 'btns' : 'backend_btns'; + + return $this->getStatus($data, $btn_name); + } + + + // 获取订单 item status_code 状态,不进行订单是否支付判断,在这里查询数据库特别慢, + // 需要处理情况,订单列表:要正确显示item 状态,直接获取 item 的状态 + public function getStatusCodeAttr($value, $data) + { + // $status_code = 'null'; + + // $order = Order::withTrashed()->where('id', $data['order_id'])->find(); + // if (!$order) { + // return $status_code; + // } + + // // 判断是否支付 + // if (!in_array($order->status, [Order::STATUS_PAYED, Order::STATUS_FINISH])) { + // return $order->status_code; + // } + + // 获取 item status_code + return $this->getBaseStatusCode($data); + } + + + /** + * $data 当前 item 数据 + * $from 当前 model 调用,还是 order 调用 + */ + public function getBaseStatusCode($data, $from = 'item') + { + $status_code = 'null'; + + if ($data['refund_status'] == OrderItem::REFUND_STATUS_AGREE) { + // 退款已同意 + return 'refund_agree'; + } + if ($data['refund_status'] == OrderItem::REFUND_STATUS_COMPLETED) { + // 退款完成 + return 'refund_completed'; + } + + if ($data['aftersale_status']) { + // 只申请了售后,没有退款 + // status_code + $status_code = $this->getNormalStatusCode($data); + + // item 要原始状态,总订单还要原来的未退款状态 + if ($from == 'item') { + switch ($data['aftersale_status']) { + case OrderItem::AFTERSALE_STATUS_REFUSE: + $status_code = 'aftersale_refuse' . '|' . $status_code; + break; + case OrderItem::AFTERSALE_STATUS_ING: + $status_code = 'aftersale_ing' . '|' . $status_code; + break; + case OrderItem::AFTERSALE_STATUS_COMPLETED: + $status_code = 'aftersale_completed' . '|' . $status_code; + break; + } + } + } else { + // status_code + $status_code = $this->getNormalStatusCode($data); + } + + return $status_code; + } + + + + public function getNormalStatusCode($data) + { + // 获取未申请售后和退款时候的 status_code + $status_code = 'null'; + + switch ($data['dispatch_status']) { + case OrderItem::DISPATCH_STATUS_REFUSE: + $status_code = 'refuse'; + break; + case OrderItem::DISPATCH_STATUS_NOSEND: + $status_code = 'nosend'; + break; + case OrderItem::DISPATCH_STATUS_SENDED: + $status_code = 'noget'; + break; + case OrderItem::DISPATCH_STATUS_GETED: + if ($data['comment_status'] == OrderItem::COMMENT_STATUS_NO) { + $status_code = 'nocomment'; + } else { + $status_code = 'commented'; + } + break; + } + + return $status_code; // status_code + } +} diff --git a/application/admin/model/shopro/order/traits/OrderScope.php b/application/admin/model/shopro/order/traits/OrderScope.php new file mode 100644 index 0000000..3de1668 --- /dev/null +++ b/application/admin/model/shopro/order/traits/OrderScope.php @@ -0,0 +1,211 @@ +where('status', Order::STATUS_CLOSED); + } + + // 已取消 + public function scopeCancel($query) + { + return $query->where('status', Order::STATUS_CANCEL); + } + + // 未支付 + public function scopeUnpaid($query) + { + return $query->where('status', Order::STATUS_UNPAID); + } + + + // 可以取消,未支付&货到付款未发货的订单 + public function scopeCanCancel($query) + { + return $query->where(function ($query) { + $query->where('status', Order::STATUS_UNPAID)->whereOr(function ($query) { + $self_name = (new Order())->getQuery()->getTable(); + $item_name = (new OrderItem())->getQuery()->getTable(); + + $query->where('pay_mode', 'offline')->where('status', Order::STATUS_PENDING)->whereExists(function ($query) use ($self_name, $item_name) { + // 货到付款订单,未发货未退款,可以申请售后 + $query->table($item_name)->where('order_id=' . $self_name . '.id') + ->where('dispatch_status', OrderItem::DISPATCH_STATUS_NOSEND) // 未发货 + ->where('refund_status', OrderItem::REFUND_STATUS_NOREFUND); // 没有退款完成; + }); + }); + }); + } + + // 已支付 + public function scopePaid($query) + { + return $query->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED]); + } + + + // 线下支付(货到付款) pending 时 + public function scopeOffline($query) + { + return $query->where('pay_mode', 'offline')->where('status', Order::STATUS_PENDING); + } + + + /** + * 是否线下支付(货到付款),暂未付款 + * + * @param object|array $order + * @return boolean + */ + public function isOffline($order) + { + return ($order['status'] == Order::STATUS_PENDING && $order['pay_mode'] == 'offline') ? true : false; + } + + + // 已支付的,或者是线下付款的未支付订单,后续可以,发货,收货,评价 + public function scopePretendPaid($query) + { + return $query->where(function ($query) { + $query->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED])->whereOr(function($query) { + $query->where('pay_mode', 'offline')->where('status', Order::STATUS_PENDING); + }); + }); + } + + // 已完成 + public function scopeCompleted($query) + { + return $query->where('status', Order::STATUS_COMPLETED); + } + + // 未申请全额退款,或者已拒绝 + public function scopeNoApplyRefund($query) + { + return $query->whereIn('apply_refund_status', [ + Order::APPLY_REFUND_STATUS_NOAPPLY, + Order::APPLY_REFUND_STATUS_REFUSE + ]); + } + + // 申请全额退款中 + public function scopeApplyRefundIng($query) + { + return $query->where('apply_refund_status', Order::APPLY_REFUND_STATUS_APPLY); + } + + + // 未发货 + public function scopeNosend($query) + { + $self_name = (new Order())->getQuery()->getTable(); + $item_name = (new OrderItem())->getQuery()->getTable(); + + $is_express = (request()->action() == 'exportdelivery') ? 1 : 0; // 是否是 express,将只查快递物流的代发货 + + return $query->noApplyRefund()->whereExists(function ($query) use ($self_name, $item_name, $is_express) { + $query->table($item_name)->where('order_id=' . $self_name . '.id') + ->where('dispatch_status', OrderItem::DISPATCH_STATUS_NOSEND) // 未发货 + ->where('refund_status', OrderItem::REFUND_STATUS_NOREFUND); // 没有退款完成 + if ($is_express) { // 只查快递物流的代发货 + $query->where('dispatch_type', 'express'); + } + })->whereNotExists(function ($query) use ($self_name, $item_name, $is_express) { + // 不是 正在售后的商品 + $query->table($item_name)->where('order_id=' . $self_name . '.id') + ->where('aftersale_status', OrderItem::AFTERSALE_STATUS_ING) // 但是售后中 + ->where('dispatch_status', OrderItem::DISPATCH_STATUS_NOSEND) // 未发货 + ->where('refund_status', OrderItem::REFUND_STATUS_NOREFUND); // 没有退款完成; + + if ($is_express) { // 只查快递物流的代发货 + $query->where('dispatch_type', 'express'); + } + }); + } + + // 待收货 + public function scopeNoget($query) + { + return $query->whereExists(function ($query) { + $self_name = (new Order())->getQuery()->getTable(); + $item_name = (new OrderItem())->getQuery()->getTable(); + + $query->table($item_name)->where('order_id=' . $self_name . '.id') + ->where('dispatch_status', OrderItem::DISPATCH_STATUS_SENDED) // 已发货 + ->where('refund_status', OrderItem::REFUND_STATUS_NOREFUND); // 没有退款完成 + }); + } + + + // 已拒收 + public function scopeRefuse($query) + { + return $query->whereExists(function ($query) { + $self_name = (new Order())->getQuery()->getTable(); + $item_name = (new OrderItem())->getQuery()->getTable(); + + $query->table($item_name)->where('order_id=' . $self_name . '.id') + ->where('dispatch_status', OrderItem::DISPATCH_STATUS_REFUSE) // 已拒收 + ->where('refund_status', OrderItem::REFUND_STATUS_NOREFUND); // 没有退款完成 + }); + } + + + // 待评价 + public function scopeNocomment($query) + { + return $query->whereExists(function ($query) { + $self_name = (new Order())->getQuery()->getTable(); + $item_name = (new OrderItem())->getQuery()->getTable(); + + $query->table($item_name)->where('order_id=' . $self_name . '.id') + ->where('dispatch_status', OrderItem::DISPATCH_STATUS_GETED) // 已收货 + ->where('refund_status', OrderItem::REFUND_STATUS_NOREFUND) // 没有退款完成 + ->where('comment_status', OrderItem::COMMENT_STATUS_NO); // 未评价 + }); + } + + // 售后 (后台要用,虽然有专门的售后单列表) + public function scopeAftersale($query) + { + return $query->whereExists(function ($query) { + $self_name = (new Order())->getQuery()->getTable(); + $item_name = (new OrderItem())->getQuery()->getTable(); + $query->table($item_name)->where('order_id=' . $self_name . '.id') + ->where('aftersale_status', '<>', OrderItem::AFTERSALE_STATUS_NOAFTER); + }); + } + + // 退款 + public function scopeRefund($query) + { + return $query->whereExists(function ($query) { + $self_name = (new Order())->getQuery()->getTable(); + $item_name = (new OrderItem())->getQuery()->getTable(); + $query->table($item_name)->where('order_id=' . $self_name . '.id') + ->where('refund_status', '<>', OrderItem::REFUND_STATUS_NOREFUND); + }); + } + + + public function scopeCanAftersale($query) + { + return $query->where('status', 'in', [Order::STATUS_PAID, Order::STATUS_COMPLETED]); + } + + public function scopeCanDelete($query) + { + return $query->where('status', 'in', [ + Order::STATUS_CANCEL, + Order::STATUS_CLOSED, + Order::STATUS_COMPLETED + ]); + } +} diff --git a/application/admin/model/shopro/order/traits/OrderStatus.php b/application/admin/model/shopro/order/traits/OrderStatus.php new file mode 100644 index 0000000..3984286 --- /dev/null +++ b/application/admin/model/shopro/order/traits/OrderStatus.php @@ -0,0 +1,411 @@ +ext; + + switch ($this->status_code) { + case 'cancel': + $status_text = '已取消'; + $status_desc = '买家已取消'; + $btns[] = 'delete'; // 删除订单 + break; + case 'closed': + $status_text = '交易关闭'; + if (isset($ext['closed_type']) && $ext['closed_type'] == 'refuse') { + $status_desc = '买家拒绝收货'; + } else { + $status_desc = '买家未在规定时间内付款'; + } + $btns[] = 'delete'; // 删除订单 + break; + case 'unpaid': + $status_text = '待付款'; + $status_desc = '等待买家付款'; + $btns[] = 'cancel'; // 取消订单 + $btns[] = 'pay'; // 支付 + $backendBtns[] = 'change_fee'; // 订单改价 + break; + // 已支付的 + case 'apply_refund': + $status_text = '申请退款中'; + $status_desc = '等待卖家处理退款'; + $backendBtns[] = 'apply_refund_oper'; // 处理申请退款按钮 + $backendBtns[] = 'refund'; // 只能退款,或者在列表上拒绝申请退款 + break; + case 'commented': + $status_text = '已评价'; + $status_desc = '订单已评价'; + + $dispatchType = $this->getItemDispatchTypes(); + if (in_array('express', $dispatchType)) { + $btns[] = 'express'; // 查看物流 + } + $backendBtns[] = 'refund'; + break; + case 'nocomment': + $status_text = '待评价'; + $status_desc = '等待买家评价'; + + $dispatchType = $this->getItemDispatchTypes(); + if (in_array('express', $dispatchType)) { + $btns[] = 'express'; // 查看物流 + } + $btns[] = 'comment'; + $backendBtns[] = 'refund'; + break; + case 'noget': + $status_text = '待收货'; + $status_desc = '等待买家收货'; + + $dispatchType = $this->getItemDispatchTypes(); + if (in_array('express', $dispatchType)) { + $btns[] = 'express'; // 查看物流 + } + + if ($this->isOffline($data)) { + $status_desc = '卖家已发货,等待包裹运达'; + + // 用户可以拒收,后台确认收货 + $btns[] = 'refuse'; // 用户拒收 + $backendBtns[] = 'confirm'; + }else { + // 计算自动确认收货时间 + $send_time = $ext['send_time'] ?? 0; + $auto_confirm = Config::getConfigField('shop.order.auto_confirm'); + $auto_confirm_unix = $auto_confirm * 86400; + if ($send_time && $auto_confirm_unix) { + $auto_confirm_time = $send_time + $auto_confirm_unix; + + $status_desc .= ',还剩' . diff_in_time($auto_confirm_time, null, true, true) . '自动确认'; + } + + $btns[] = 'confirm'; // 确认收货 + $backendBtns[] = 'refund'; + } + + break; + case 'nosend': + $status_text = '待发货'; + $status_desc = '等待卖家发货'; + $statusCodes = $this->getItemStatusCode(); + if (in_array('noget', $statusCodes)) { // 只要存在待收货的item + $btns[] = 'confirm'; // 确认收货 (部分发货时也可以收货) + } + + if ($this->isOffline($data)) { + // 发货之前用户可以取消 + $btns[] = 'cancel'; // 用户取消订单 + } else { + $backendBtns[] = 'refund'; + } + $backendBtns[] = 'send'; + if (!isset($ext['need_address']) || $ext['need_address']) { // 自动发货这些不需要收货地址的,没有 edit_consignee + $backendBtns[] = 'edit_consignee'; //修改收货地址 + } + + break; + case 'refund_completed': + $status_text = '退款完成'; + $status_desc = '订单退款完成'; + break; + case 'refund_agree': + $status_text = '退款完成'; + $status_desc = '订单退款完成'; + break; + case 'groupon_ing': + $status_text = '等待成团'; + $status_desc = '等待拼团成功'; + if ($this->isOffline($data)) { + // 货到付款未付款,不能退款,等待拼团时还未发货,用户可取消订单 + $btns[] = 'cancel'; // 用户取消订单 + } else { + $backendBtns[] = 'refund'; // 全部退款 直接不申请退款 + } + break; + case 'groupon_invalid': + $status_text = '拼团失败'; + $status_desc = '拼团失败'; + break; + // 已支付的结束 + case 'completed': + $status_text = '交易完成'; + $status_desc = '交易已完成'; + $btns[] = 'delete'; // 删除订单 + break; + } + + // 有活动 + if (in_array($data['activity_type'], ['groupon', 'groupon_ladder', 'groupon_lucky'])) { + // 是拼团订单 + if (isset($ext['groupon_id']) && $ext['groupon_id']) { + $btns[] = 'groupon'; // 拼团详情 + } + } + + if (in_array($this->status_code, ['nosend', 'groupon_ing']) && !$this->isOffline($data)) { // 线下付款订单,不可申请全额退款 + if (in_array($data['apply_refund_status'], [Order::APPLY_REFUND_STATUS_NOAPPLY, Order::APPLY_REFUND_STATUS_REFUSE])) { + // 获取所有的 item 状态 + $statusCodes = $this->getItemStatusCode(); + if (count($statusCodes) == 1 && current($statusCodes) == 'nosend') { + // items 只有 未发货,就显示 申请退款按钮 + if ($data['apply_refund_status'] == Order::APPLY_REFUND_STATUS_REFUSE) { + $btns[] = 're_apply_refund'; // 重新申请退款 + } else { + $btns[] = 'apply_refund'; // 申请退款 + } + } + } + } + + if ($data['invoice_status'] == 1) { + $btns[] = 'invoice'; // 查看发票 + } + + $return = null; + switch ($type) { + case 'status_text': + $return = $status_text; + break; + case 'btns': + $return = $btns; + break; + case 'status_desc': + $return = $status_desc; + break; + case 'backend_btns': + if (in_array('refund', $backendBtns)) { + // 判断是否有退款,如果存在退款就移除 refund 按钮 + $refundCount = OrderItem::where('order_id', $data['id'])->where('refund_status', '<>', OrderItem::REFUND_STATUS_NOREFUND)->count(); + if ($refundCount && ($key = array_search('refund', $backendBtns))) { + unset($backendBtns[$key]); + } + + $backendBtns = array_values($backendBtns); + } + + $return = $backendBtns; + break; + } + + return $return; + } + + + /** + * 获取支付成功之后的子状态 + */ + public function getPayedStatusCode($data) + { + $status_code = ''; + + // 获取所有的 item 状态 + $statusCodes = $this->getItemStatusCode(); + + if (in_array('nosend', $statusCodes)) { + // 存在待发货就是待发货 + $status_code = 'nosend'; + } else if (in_array('noget', $statusCodes)) { + // 存在待收货,就是待收货 + $status_code = 'noget'; + } else if (in_array('nocomment', $statusCodes)) { + // 存在待评价,就是待评价 + $status_code = 'nocomment'; + } else if (in_array('commented', $statusCodes)) { + // 存在已评价,就是已评价 + $status_code = 'commented'; + } else if (in_array('refund_completed', $statusCodes)) { + // 所有商品退款完成,或者退款中(不可能存在待发货或者收货的商品,上面已判断过) + $status_code = 'refund_completed'; + } else if (in_array('refund_agree', $statusCodes)) { + // 所有商品都同意退款了 (不可能存在待发货或者收货的商品,上面已判断过) + $status_code = 'refund_agree'; + } // 售后都不在总状态显示 + + if ($data['apply_refund_status'] == Order::APPLY_REFUND_STATUS_APPLY && !in_array($status_code, ['refund_completed', 'refund_agree'])) { + return $status_code = 'apply_refund'; // 申请退款中,并且还没退款 + } + + $ext = $this->ext; + // 是拼团订单 + if ( + in_array($data['activity_type'], ['groupon', 'groupon_ladder', 'groupon_lucky']) && + isset($ext['groupon_id']) && $ext['groupon_id'] + ) { + $groupon = Groupon::where('id', $ext['groupon_id'])->find(); + if ($groupon) { + if ($groupon['status'] == 'ing') { + // 尚未成团 + $status_code = $statusCodes[0] ?? ''; // 拼团订单只能有一个商品 + $status_code = in_array($status_code, ['refund_agree', 'refund_completed']) ? $status_code : 'groupon_ing'; // 如果订单已退款,则是退款状态,不显示拼团中 + } else if ($groupon['status'] == 'invalid') { + $status_code = 'groupon_invalid'; + } + } + } + + return $status_code; + } + + + /** + * 获取订单items状态 + * + * @param string $type + * @return array + */ + private function getItemStatusCode($type = 'order') + { + // 循环判断 item 状态 + $itemStatusCode = []; + foreach ($this->items as $key => $item) { + // 获取 item status + $itemStatusCode[] = (new OrderItem)->getBaseStatusCode($item, $type); + } + + // 取出不重复不为空的 status_code + $statusCodes = array_values(array_unique(array_filter($itemStatusCode))); + + return $statusCodes; + } + + + private function getItemDispatchTypes() + { + $dispatchType = []; + foreach ($this->items as $key => $item) { + // 获取 item status + $dispatchType[] = $item['dispatch_type']; + } + $dispatchType = array_unique(array_filter($dispatchType)); // 过滤重复,过滤空值 + return $dispatchType; + } + + + public function getStatusTextAttr($value, $data) + { + return $this->getStatus($data, 'status_text'); + } + + public function getStatusDescAttr($value, $data) + { + return $this->getStatus($data, 'status_desc'); + } + + + public function getBtnsAttr($value, $data) + { + $btn_name = strpos(request()->url(), 'addons/shopro') !== false ? 'btns' : 'backend_btns'; + + return $this->getStatus($data, $btn_name); + } + + + // 获取订单状态 + public function getStatusCodeAttr($value, $data) + { + $status_code = ''; + + switch ($data['status']) { + case Order::STATUS_CLOSED: + $status_code = 'closed'; // 订单交易关闭 + break; + case Order::STATUS_CANCEL: + $status_code = 'cancel'; // 订单已取消 + break; + case Order::STATUS_UNPAID: + $status_code = 'unpaid'; // 订单等待支付 + break; + case Order::STATUS_PENDING: + $status_code = $this->getPayedStatusCode($data); // 订单线下付款 + break; + case Order::STATUS_PAID: + // 根据 item 获取支付后的状态信息 + $status_code = $this->getPayedStatusCode($data); + break; + case Order::STATUS_COMPLETED: + $status_code = 'completed'; + break; + } + + return $status_code; + } + + + /** + * 处理未支付 item status_code + * 查询列表 item status_code 不关联订单表,使用这个方法进行处理 + */ + public function setOrderItemStatusByOrder($order) + { + $order = $order instanceof \think\Model ? $order->toArray() : $order; + + foreach ($order['items'] as $key => &$item) { + if ((!in_array($order['status'], [Order::STATUS_PAID, Order::STATUS_COMPLETED]) && !$this->isOffline($order)) // 没有支付,并且也不是货到付款 + || $order['apply_refund_status'] == Order::APPLY_REFUND_STATUS_APPLY) { + // 未支付,status_code = 订单的 status_code + $item['status_code'] = $order['status_code']; + $item['status_text'] = ''; + $item['status_desc'] = ''; + $item['btns'] = []; + } else { + if (strpos(request()->url(), 'addons/shopro') !== false) { + // 前端 + if (strpos($item['status_code'], 'nosend') !== false) { + if (!$this->isOffline($order) && !array_intersect(['re_apply_refund', 'apply_refund'], $order['btns'])) { + // 不能申请全额退款了(有部分发货,或者退款)的待发货的 item 要显示申请售后的按钮 + $aftersale_id = (isset($item['ext']['aftersale_id']) && !empty($item['ext']['aftersale_id'])) ? $item['ext']['aftersale_id'] : 0; + + if (strpos($item['status_code'], 'aftersale_ing') === false && strpos($item['status_code'], 'aftersale_completed') === false) { + // 不是售后中,也不是售后完成 + if (strpos($item['status_code'], 'aftersale_refuse') !== false && $aftersale_id) { + // 如果申请过退款,被拒绝了,则为重新申请售后 + $item['btns'][] = 're_aftersale'; + } else { + // 取消售后是 re_aftersale ,未申请过是 aftersale + $item['btns'][] = $aftersale_id ? 're_aftersale' : 'aftersale'; // 售后,售后取消会有 aftersale_id + } + } + } + } else if (strpos($item['status_code'], 'noget') !== false) { + // 如果是货到付款的 待收货 + if ($this->isOffline($order)) { + foreach($item['btns'] as $btnk => $btn) { + if (in_array($btn, ['re_aftersale', 'aftersale'])) { + unset($item['btns'][$btnk]); + } + } + } + } + } else { + // 后端,如果是货到付款, 并且还没收货,不显示退款按钮 + if ($this->isOffline($order) && (strpos($item['status_code'], 'nosend') !== false || strpos($item['status_code'], 'noget') !== false)) { + $refund_key = array_search('refund', $item['btns']); + if ($refund_key !== false) { + unset($item['btns'][$refund_key]); + } + } + } + + + } + } + + return $order; + } +} diff --git a/application/admin/model/shopro/trade/Order.php b/application/admin/model/shopro/trade/Order.php new file mode 100644 index 0000000..b2059b0 --- /dev/null +++ b/application/admin/model/shopro/trade/Order.php @@ -0,0 +1,179 @@ + 'json', + 'paid_time' => 'timestamp', + ]; + + // 追加属性 + protected $append = [ + 'type_text', + 'status_text', + 'platform_text', + ]; + + + // 订单状态 + const STATUS_CLOSED = 'closed'; + const STATUS_CANCEL = 'cancel'; + const STATUS_UNPAID = 'unpaid'; + const STATUS_PAID = 'paid'; + const STATUS_COMPLETED = 'completed'; + + public function statusList() + { + return [ + 'closed' => '交易关闭', + 'cancel' => '已取消', + 'unpaid' => '未支付', + 'paid' => '已支付', + 'completed' => '已完成' + ]; + } + + + /** + * 订单列表状态搜索 + */ + public function searchStatusList() + { + return [ + 'unpaid' => '待付款', + 'paid' => '已支付', // 包括刚支付的,以及已完成的所有付过款的订单 + 'completed' => '已完成', + 'cancel' => '已取消', + 'closed' => '交易关闭', + ]; + } + + + public function typeList() + { + return [ + 'recharge' => '充值订单', + ]; + } + + + public function platformList() + { + return [ + 'H5' => 'H5', + 'WechatOfficialAccount' => '微信公众号', + 'WechatMiniProgram' => '微信小程序', + 'App' => 'App', + ]; + } + + + public function getPlatformTextAttr($value, $data) + { + $value = $value ?: ($data['platform'] ?? null); + + $list = $this->platformList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + /** + * 已支付订单,支付类型 + * + * @param string $value + * @param array $data + * @return void + */ + public function getPayTypeAttr($value, $data) + { + $status = $data['status'] ?? ''; + $payTypes = []; + if (in_array($status, [self::STATUS_PAID, self::STATUS_COMPLETED])) { + $payTypes = PayModel::typeTradeOrder()->where('order_id', $data['id'])->where('status', '<>', PayModel::PAY_STATUS_UNPAID)->group('pay_type')->column('pay_type'); + } + + return $payTypes[0] ?? ''; + } + + + /** + * 已支付订单,支付类型文字 + * + * @param string $value + * @param array $data + * @return void + */ + public function getPayTypeTextAttr($value, $data) + { + $pay_types = $this->pay_type; + $list = (new PayModel)->payTypeList(); + + return isset($list[$pay_types]) ? $list[$pay_types] : ''; + } + + + + // 已关闭 + public function scopeClosed($query) + { + return $query->where('status', Order::STATUS_CLOSED); + } + + // 已取消 + public function scopeCancel($query) + { + return $query->where('status', Order::STATUS_CANCEL); + } + + // 未支付 + public function scopeUnpaid($query) + { + return $query->where('status', Order::STATUS_UNPAID); + } + + // 已支付 + public function scopePaid($query) + { + return $query->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED]); + } + + // 已完成 + public function scopeCompleted($query) + { + return $query->where('status', Order::STATUS_COMPLETED); + } + + + public function scopeRecharge($query) + { + return $query->where('type', 'recharge'); + } + + public function scopeVip($query) + { + return $query->where('type', 'vip'); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } + + public function pays() + { + return $this->hasMany(PayModel::class, 'order_id', 'id')->typeTradeOrder(); + } +} diff --git a/application/admin/model/shopro/traits/ModelAttr.php b/application/admin/model/shopro/traits/ModelAttr.php new file mode 100644 index 0000000..8be8f40 --- /dev/null +++ b/application/admin/model/shopro/traits/ModelAttr.php @@ -0,0 +1,222 @@ + '正常', + 'hidden' => '隐藏', + 'enable' => '启用中', + 'disabled' => '已禁用', + 'up' => '上架', + 'down' => '下架', + ]; + } + + + /** + * (查询范围)正常 + */ + public function scopeNormal($query) + { + return $query->where('status', 'normal'); + } + + /** + * (查询范围)启用 + */ + public function scopeEnable($query) + { + return $query->where('status', 'enable'); + } + + /** + * (查询范围)禁用 + */ + public function scopeDisabled($query) + { + return $query->where('status', 'disabled'); + } + + + /** + * (查询范围)隐藏 hidden 和框架底层 hidden 冲突了 + */ + public function scopeStatusHidden($query) + { + return $query->where('status', 'hidden'); + } + + /** + * (查询范围)上架 + */ + public function scopeUp($query) + { + return $query->where('status', 'up'); + } + + /** + * (查询范围)下架 + */ + public function scopeDown($query) + { + return $query->where('status', 'down'); + } + + + // /** + // * 创建时间格式化 + // * + // * @param string $value + // * @param array $data + // * @return string + // */ + // public function getCreatetimeAttr($value, $data) + // { + // return $this->attrTimeFormat($value, $data, 'createtime'); + // } + + + // /** + // * 更新时间格式化 + // * + // * @param string $value + // * @param array $data + // * @return string + // */ + // public function getUpdatetimeAttr($value, $data) + // { + // return $this->attrTimeFormat($value, $data, 'updatetime'); + // } + + + /** + * 更新时间格式化 + * + * @param string $value + * @param array $data + * @return string + */ + public function getDeletetimeAttr($value, $data) + { + return $this->attrTimeFormat($value, $data, 'deletetime'); + } + + + + /** + * 通用类型获取器 + * + * @param string $value + * @param array $data + * @return string + */ + public function getTypeTextAttr($value, $data) + { + $value = $value ?: ($data['type'] ?? null); + + $list = $this->typeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + /** + * 通用状态获取器 + * + * @param string $value + * @param array $data + * @return string + */ + public function getStatusTextAttr($value, $data) + { + $value = $value ?: ($data['status'] ?? ''); + + $list = $this->statusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + + /** + * 时间格式化 + * + * @param mix $value + * @param array $data + * @param string $field + * @return string + */ + protected function attrTimeFormat($value, $data, $field) + { + $value = $value ?: ($data[$field] ?? null); + + return $value ? (is_string($value) ? $value : date('Y-m-d H:i:s', $value)) : null; + } + + + /** + * 获取器格式化 json + * + * @param mix $value + * @param array $data + * @param string $field + * @param bool $return_array + * @return array|null + */ + protected function attrFormatJson($value, $data, $field, $return_array = false) + { + $value = $value ?: ($data[$field] ?? null); + + $value = $value ? json_decode($value, true) : ($return_array ? [] : $value); + return $value === false ? $data[$field] : $value; + } + + + + /** + * 获取器格式化 , 隔开的数据 + * + * @param mix $value + * @param array $data + * @param string $field + * @param bool $return_array + * @return array|null + */ + protected function attrFormatComma($value, $data, $field, $return_array = false) + { + $value = $value ?: ($data[$field] ?? null); + + $value = $value ? explode(',', $value) : ($return_array ? [] : $value); + return $value === false ? $data[$field] : $value; + } + + + /** + * 将时间格式化为时间戳 + * + * @param [type] $time + * @return void + */ + protected function attrFormatUnix($time) + { + return $time ? (!is_numeric($time) ? strtotime($time) : $time) : null; + } +} diff --git a/application/admin/model/shopro/user/Account.php b/application/admin/model/shopro/user/Account.php new file mode 100644 index 0000000..3c0ad15 --- /dev/null +++ b/application/admin/model/shopro/user/Account.php @@ -0,0 +1,26 @@ + '微信零钱', + 'alipay' => '支付宝账户', + 'bank' => '银行卡转账' + ]; + + public function getTypeTextAttr($value, $data) + { + return $this->typeMap[$data['type']]; + } +} diff --git a/application/admin/model/shopro/user/Address.php b/application/admin/model/shopro/user/Address.php new file mode 100644 index 0000000..7624af4 --- /dev/null +++ b/application/admin/model/shopro/user/Address.php @@ -0,0 +1,24 @@ + 'boolean' + ]; + + // 追加属性 + protected $append = []; + + + public function scopeDefault($query) + { + return $query->where('is_default', 1); + } + +} diff --git a/application/admin/model/shopro/user/CheckInfo.php b/application/admin/model/shopro/user/CheckInfo.php new file mode 100644 index 0000000..2511c52 --- /dev/null +++ b/application/admin/model/shopro/user/CheckInfo.php @@ -0,0 +1,24 @@ + 'boolean' + ]; + + // 追加属性 + protected $append = []; + + + public function scopeDefault($query) + { + return $query->where('is_default', 1); + } + +} diff --git a/application/admin/model/shopro/user/Coupon.php b/application/admin/model/shopro/user/Coupon.php new file mode 100644 index 0000000..3de13d0 --- /dev/null +++ b/application/admin/model/shopro/user/Coupon.php @@ -0,0 +1,308 @@ + 'timestamp' + ]; + + // 追加属性 + protected $append = [ + 'name', + 'type', + 'type_text', + 'use_scope', + 'use_scope_text', + 'items', + 'amount', + 'amount_text', + 'enough', + 'limit_num', + 'use_start_time', + 'use_end_time', + 'max_amount', + 'is_double_discount', + 'description', + 'status', + 'status_text', + ]; + + + public function statusList() + { + return [ + 'used' => '已使用', + 'can_use' => '立即使用', + 'expired' => '已过期', + 'cannot_use' => '暂不可用', + + 'geted' => '未使用' // 包括 can_use 和 cannot_use + ]; + } + + + public function scopeGeted($query) + { + return $query->whereNull('use_time')->whereExists(function ($query) { + $table_name = (new CouponModel)->getQuery()->getTable(); + $user_coupon_name = (new self)->getQuery()->getTable(); + + $query->table($table_name)->whereNull('deletetime')->whereIn('status', ['normal', 'hidden']) + ->where($user_coupon_name . '.coupon_id=' . $table_name . '.id') + ->where(function ($query) use ($user_coupon_name) { + $query->where(function ($query) { + $query->where('use_time_type', 'range')->where('use_end_time', '>=', time()); // 可用结束时间,大于当前时间,已经可用,或者暂不可用都算 + })->whereOr(function ($query) use ($user_coupon_name) { + $query->where('use_time_type', 'days') + ->whereRaw($user_coupon_name . '.createtime + ((start_days + days) * 86400) >= ' . time()); // 可用结束结束时间大于当前时间 + }); + }); + }); + } + + + + // 可以使用 + public function scopeCanUse($query) + { + return $query->whereNull('use_time')->whereExists(function ($query) { + $table_name = (new CouponModel)->getQuery()->getTable(); + $user_coupon_name = (new self)->getQuery()->getTable(); + + $query->table($table_name)->whereNull('deletetime')->whereIn('status', ['normal', 'hidden']) + ->where($user_coupon_name . '.coupon_id=' . $table_name . '.id') + ->where(function ($query) use ($user_coupon_name) { + $query->where(function ($query) { + $query->where('use_time_type', 'range')->where('use_start_time', '<=', time())->where('use_end_time', '>=', time()); + })->whereOr(function ($query) use ($user_coupon_name) { + $query->where('use_time_type', 'days') + ->whereRaw($user_coupon_name . '.createtime + (start_days * 86400) <= ' . time()) + ->whereRaw($user_coupon_name . '.createtime + ((start_days + days) * 86400) >= ' . time()); + }); + }); + }); + } + + + // 暂不可用,还没到可使用日期 + public function scopeCannotUse($query) + { + return $query->whereNull('use_time')->whereExists(function ($query) { + $table_name = (new CouponModel)->getQuery()->getTable(); + $user_coupon_name = (new self)->getQuery()->getTable(); + + $query->table($table_name)->whereNull('deletetime')->whereIn('status', ['normal', 'hidden']) + ->where($user_coupon_name . '.coupon_id=' . $table_name . '.id') + ->where(function ($query) use ($user_coupon_name) { + $query->where(function ($query) { + $query->where('use_time_type', 'range')->where('use_start_time', '>', time()); + })->whereOr(function ($query) use ($user_coupon_name) { + $query->where('use_time_type', 'days') + ->whereRaw($user_coupon_name . '.createtime + (start_days * 86400) > ' . time()); + }); + }); + }); + } + + + // 已使用 + public function scopeUsed($query) + { + return $query->whereNotNull('use_time'); + } + + // 未使用,但已过期 + public function scopeExpired($query) + { + return $query->whereNull('use_time')->whereExists(function ($query) { + $table_name = (new CouponModel)->getQuery()->getTable(); + $user_coupon_name = (new self)->getQuery()->getTable(); + + $query->table($table_name)->whereNull('deletetime')->whereIn('status', ['normal', 'hidden']) + ->where($user_coupon_name . '.coupon_id=' . $table_name . '.id') + ->where(function ($query) use ($user_coupon_name) { + $query->where(function ($query) { + $query->where('use_time_type', 'range')->where('use_end_time', '<', time()); + })->whereOr(function ($query) use ($user_coupon_name) { + $query->where('use_time_type', 'days') + ->whereRaw($user_coupon_name . '.createtime + ((start_days + days) * 86400) < ' . time()); + }); + }); + }); + } + + // 未使用,但已过期,或者已使用 + public function scopeInvalid($query) + { + return $query->where(function ($query) { + $query->whereNotNull('use_time')->whereOr(function ($query) { + $query->whereNull('use_time')->whereExists(function ($query) { + $table_name = (new CouponModel)->getQuery()->getTable(); + $user_coupon_name = (new self)->getQuery()->getTable(); + + $query->table($table_name)->whereNull('deletetime')->whereIn('status', ['normal', 'hidden']) + ->where($user_coupon_name . '.coupon_id=' . $table_name . '.id') + ->where(function ($query) use ($user_coupon_name) { + $query->where(function ($query) { + $query->where('use_time_type', 'range')->where('use_end_time', '<', time()); + })->whereOr(function ($query) use ($user_coupon_name) { + $query->where('use_time_type', 'days') + ->whereRaw($user_coupon_name . '.createtime + ((start_days + days) * 86400) < ' . time()); + }); + }); + }); + }); + }); + } + + + public function getNameAttr($value, $data) + { + return $this->coupon ? $this->coupon->name : ''; + } + + public function getTypeAttr($value, $data) + { + return $this->coupon ? $this->coupon->type : ''; + } + + public function getUseScopeAttr($value, $data) + { + return $this->coupon ? $this->coupon->use_scope : ''; + } + + public function getItemsAttr($value, $data) + { + return $this->coupon ? $this->coupon->items : ''; + } + + public function getAmountAttr($value, $data) + { + return $this->coupon ? $this->coupon->amount : ''; + } + + + public function getEnoughAttr($value, $data) + { + return $this->coupon ? $this->coupon->enough : ''; + } + + public function getLimitNumAttr($value, $data) + { + return $this->coupon ? $this->coupon->limit_num : ''; + } + + public function getUseStartTimeAttr($value, $data) + { + if ($this->coupon) { + if ($this->coupon->use_time_type == 'days') { + return date('Y-m-d H:i:s', $data['createtime'] + ($this->coupon->start_days * 86400)); + } + + return $this->coupon->use_start_time; + } + } + + public function getUseEndTimeAttr($value, $data) + { + if ($this->coupon) { + if ($this->coupon->use_time_type == 'days') { + return date('Y-m-d H:i:s', $data['createtime'] + (($this->coupon->start_days + $this->coupon->days) * 86400)); + } + + return $this->coupon->use_end_time; + } + } + + public function getMaxAmountAttr($value, $data) + { + return $this->coupon ? $this->coupon->max_amount : 0; + } + + public function getIsDoubleDiscountAttr($value, $data) + { + return $this->coupon ? $this->coupon->is_double_discount : 0; + } + + public function getDescriptionAttr($value, $data) + { + return $this->coupon ? $this->coupon->description : ''; + } + + public function getUseScopeTextAttr($value, $data) + { + return $this->coupon ? $this->coupon->use_scope : 0; + } + + public function getAmountTextAttr($value, $data) + { + return $this->coupon ? $this->coupon->amount_text : 0; + } + + public function getItemsValueAttr($value, $data) + { + return $this->coupon ? $this->coupon->items_value : 0; + } + + public function getTypeTextAttr($value, $data) + { + return $this->coupon ? $this->coupon->type_text : 0; + } + + + // 我的优惠券使用状态 + public function getStatusAttr($value, $data) + { + if ($data['use_time']) { + $status = 'used'; + } else { + if ($this->use_start_time <= date('Y-m-d H:i:s') && $this->use_end_time >= date('Y-m-d H:i:s')) { + $status = 'can_use'; + } else if ($this->use_end_time <= date('Y-m-d H:i:s')) { + $status = 'expired'; + } else { + // 未到使用日期 + $status = 'cannot_use'; + } + } + + return $status; + } + + public function getStatusTextAttr($value, $data) + { + $value = $value ?: ($this->status ?? ''); + + if (strpos(request()->url(), 'addons/shopro') === false) { + $value = in_array($value, ['can_use', 'cannot_use']) ? 'geted' : $value; // 后台,可以使用和咱不可用 合并 + } + + $list = $this->statusList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + + public function coupon() + { + return $this->belongsTo(CouponModel::class, 'coupon_id'); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } + + public function order() + { + return $this->belongsTo(OrderModel::class, 'use_order_id'); + } +} diff --git a/application/admin/model/shopro/user/GoodsLog.php b/application/admin/model/shopro/user/GoodsLog.php new file mode 100644 index 0000000..80ebf5b --- /dev/null +++ b/application/admin/model/shopro/user/GoodsLog.php @@ -0,0 +1,62 @@ +where('user_id', $user->id)->where('goods_id', $goods->id)->find(); + if ($view) { + $view->updatetime = time(); + $view->save(); + } else { + $view = new self(); + $data = [ + 'goods_id' => $goods->id, + 'user_id' => $user->id, + 'type' => 'views' + ]; + $view->save($data); + } + } + // 增加商品浏览量 + Goods::where('id', $goods['id'])->update(['views' => \think\Db::raw('views+1')]); + } + + public function scopeFavorite($query) + { + return $query->where('type', 'favorite'); + } + + + public function scopeViews($query) // view 和底层方法冲突了 + { + return $query->where('type', 'views'); + } + + + public function goods() + { + return $this->belongsTo(Goods::class, 'goods_id'); + } +} diff --git a/application/admin/model/shopro/user/Invoice.php b/application/admin/model/shopro/user/Invoice.php new file mode 100644 index 0000000..7722142 --- /dev/null +++ b/application/admin/model/shopro/user/Invoice.php @@ -0,0 +1,25 @@ + '个人', + 'company' => '企/事业单位', + ]; + } + +} diff --git a/application/admin/model/shopro/user/User.php b/application/admin/model/shopro/user/User.php new file mode 100644 index 0000000..93ea6fd --- /dev/null +++ b/application/admin/model/shopro/user/User.php @@ -0,0 +1,136 @@ + 'timestamp', + 'jointime' => 'timestamp', + 'prevtime' => 'timestamp', + ]; + + protected $hidden = ['password', 'salt']; + + protected $append = [ + 'status_text', + 'gender_text' + ]; + + + public function getGenderList() + { + return ['1' => __('Male'), '0' => __('Female')]; + } + + /** + * 获取性别文字 + * @param string $value + * @param array $data + * @return object + */ + public function getGenderTextAttr($value, $data) + { + $value = $value ?: ($data['gender'] ?? 0); + + $list = $this->getGenderList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function setMobileAttr($value, $data) + { + if ($value !== "") { + return $value; + } + return null; + } + + public function setUsernameAttr($value, $data) + { + if ($value !== "") { + return $value; + } + return null; + } + + public function getAvatarAttr($value, $data) + { + if (empty($value)) { + $config = sheep_config('shop.user'); + $value = $config['avatar']; + } + return $value; + } + + public function setEmailAttr($value, $data) + { + if ($value !== "") { + return $value; + } + return null; + } + + public function setPasswordAttr($value, $data) + { + $salt = Random::alnum(); + $this->salt = $salt; + return \app\common\library\Auth::instance()->getEncryptPassword($value, $salt); + } + + public function getNicknameHideAttr($value, $data) + { + $value = $value ?: ($data['nickname'] ?? ''); + + return $value ? string_hide($value, 2) : $value; + } + + + public function getMobileAttr($value, $data) + { + $value = $value ?: ($data['mobile'] ?? ''); + + return $value ? (operate_filter(false) ? $value : account_hide($value, 3, 3)) : $value; + } + + + /** + * 获取验证字段数组值 + * @param string $value + * @param array $data + * @return object + */ + public function getVerificationAttr($value, $data) + { + $value = array_filter((array)json_decode($value, true)); + $value = array_merge(['username' => 0, 'password' => 0, 'mobile' => 0], $value); + return (object)$value; + } + + public function parentUser() + { + return $this->hasOne(User::class, 'id', 'parent_user_id')->field(['id', 'avatar', 'nickname', 'gender']); + } + + public function thirdOauth() + { + return $this->hasMany(ThirdOauth::class, 'user_id', 'id'); + } + + + // -- commission code start -- + public function agent() + { + return $this->hasOne(\app\admin\model\shopro\commission\Agent::class, 'user_id', 'id'); + } + // -- commission code end -- +} diff --git a/application/admin/model/shopro/user/WalletLog.php b/application/admin/model/shopro/user/WalletLog.php new file mode 100644 index 0000000..bfe6091 --- /dev/null +++ b/application/admin/model/shopro/user/WalletLog.php @@ -0,0 +1,79 @@ + 'json' + ]; + // 追加属性 + protected $append = [ + 'event_text' + ]; + + const TYPE_MAP = [ + 'money' => '余额', + 'score' => '积分', + 'commission' => '佣金' + ]; + + protected $eventMap = [ + 'score' => [ + 'signin' => '签到-赠送积分', + 'replenish_signin' => '签到-补签', + 'activity_gift' => '活动-赠送积分', + 'score_shop_pay' => '积分商城-积分支付', + 'order_pay' => '商城订单-积分抵扣', + 'order_refund' => '订单退款-退还积分', + 'admin_recharge' => '后台-积分充值', + 'recharge_gift' => '线上充值-赠送积分' + ], + 'money' => [ + 'order_pay' => '商城订单-余额支付', + 'order_recharge' => '线上充值', + 'admin_recharge' => '后台-余额充值', + 'recharge_gift' => '线上充值-赠送余额', + 'activity_gift' => '活动-赠送余额', + 'order_refund' => '订单退款-退还余额', + 'transfer_by_commission' => '佣金-转入到余额' + ], + 'commission' => [ + 'withdraw' => '提现', + 'withdraw_error' => '提现失败-返还佣金', + 'reward_income' => '佣金-收益', + 'reward_back' => '佣金-退还', + 'transfer_to_money' => '佣金-转出到余额' + ] + ]; + + public function getEventMap() { + return $this->eventMap; + } + + public function scopeMoney($query) + { + return $query->where('type', 'money'); + } + + public function scopeCommission($query) + { + return $query->where('type', 'commission'); + } + + public function scopeScore($query) + { + return $query->where('type', 'score'); + } + + public function getEventTextAttr($value, $data) + { + return $this->eventMap[$data['type']][$data['event']] ?? ''; + } +} diff --git a/application/admin/model/shopro/vip/Paylog.php b/application/admin/model/shopro/vip/Paylog.php new file mode 100644 index 0000000..a2868f8 --- /dev/null +++ b/application/admin/model/shopro/vip/Paylog.php @@ -0,0 +1,48 @@ + 'json', + ]; + + // 追加属性 + protected $append = [ + 'type_text' + ]; + + public function typeList() + { + return ['text' => '文字', 'link' => '链接']; + } +} diff --git a/application/admin/model/shopro/wechat/Menu.php b/application/admin/model/shopro/wechat/Menu.php new file mode 100644 index 0000000..d1225a7 --- /dev/null +++ b/application/admin/model/shopro/wechat/Menu.php @@ -0,0 +1,31 @@ + 'json', + 'publishtime' => 'timestamp', + ]; + + // 追加属性 + protected $append = [ + 'status_text' + ]; + + /** + * 状态列表 + * + * @return array + */ + public function statusList() + { + return [0 => '未发布', 1 => '已发布']; + } +} diff --git a/application/admin/model/shopro/wechat/Reply.php b/application/admin/model/shopro/wechat/Reply.php new file mode 100644 index 0000000..5479e18 --- /dev/null +++ b/application/admin/model/shopro/wechat/Reply.php @@ -0,0 +1,63 @@ + '文字', 'link' => '链接', 'image' => '图片', 'voice' => '语音', 'video' => '视频', 'news' => '图文消息']; + } + + /** + * 通用类型获取器 + * + * @param string $value + * @param array $data + * @return string + */ + public function getGroupTextAttr($value, $data) + { + $list = ['keywords' => '关键词回复', 'subscribe' => '关注回复', 'default' => '默认回复']; + + $value = $value ?: ($data['group'] ?? null); + + return isset($list[$value]) ? $list[$value] : ''; + } +} diff --git a/application/admin/model/user/Club.php b/application/admin/model/user/Club.php new file mode 100644 index 0000000..e488341 --- /dev/null +++ b/application/admin/model/user/Club.php @@ -0,0 +1,40 @@ +matchRankingModel = new MatchRanking(); + } + + //排位赛 + public function bestplayersen($match_id,$course,$export = false){ + $matchRankingModel = new MatchRanking(); + $player_res = $matchRankingModel->field('*')->where('match_id',$match_id)->where('course',$course)->group('player_id')->select(); + $h = 0; + foreach ($player_res as $k => &$value){ + $dan_fly = $matchRankingModel->where('match_id',$match_id)->where('course',$course)->where('player_id',$value['player_id'])->select(); + $player_info = $matchRankingModel->field('player_id,player_name,grouping,led_color,channel')->where('match_id',$match_id)->where('course',$course)->where('player_id',$value['player_id'])->find()->toArray(); + // var_dump($player_info);exit; + $dan_data = [];$i = 0; + $sum = 0; + foreach ($dan_fly as $q){ + if(preg_match('/\d/',$q['first_fly_time'])) { + $dan_data[$i] = $this->getAllMs($q['first_fly_time']); + $sum = $sum + $dan_data[$i]; + $i++; + } + if(preg_match('/\d/',$q['second_fly_time'])) { + $dan_data[$i] = $this->getAllMs($q['second_fly_time']); + $sum = $sum + $dan_data[$i]; + $i++; + } + if(preg_match('/\d/',$q['third_fly_time'])) { + $dan_data[$i] = $this->getAllMs($q['third_fly_time']); + $sum = $sum + $dan_data[$i]; + $i++; + } + } + sort($dan_data); + // if($player_info['player_id'] == '10160'){var_dump($sum);exit;} + // + if($i >= 3){ + $player_data[$h]['sum'] = $this->getAllSeconds(intval($sum)); + // if($player_info['player_id'] == '10160'){var_dump($sum);var_dump($player_data[$h]['sum']);exit;} + $player_data[$h]['info'] = $player_info; + $player_data[$h]['fly_num'] = $i; + $player_data[$h]['first'] = $dan_data[0]; + $player_data[$h]['second'] = $dan_data[1]; + $player_data[$h]['third'] = $dan_data[2]; + }elseif($i == 2){ + $player_data[$h]['sum'] = $this->getAllSeconds(intval($sum)); + // var_dump($player_data[$h]['sum']);exit; + $player_data[$h]['info'] = $player_info; + $player_data[$h]['fly_num'] = $i; + $player_data[$h]['first'] = $dan_data[0]; + $player_data[$h]['second'] = $dan_data[1]; + $player_data[$h]['third'] = '0'; + }elseif($i == 1){ + $player_data[$h]['sum'] = $this->getAllSeconds(intval($sum)); + // var_dump($player_data[$h]['sum']);exit; + $player_data[$h]['info'] = $player_info; + $player_data[$h]['fly_num'] = $i; + $player_data[$h]['first'] = $dan_data[0]; + $player_data[$h]['second'] = '0'; + $player_data[$h]['third'] = '0'; + }elseif($i == 0){ + $player_data[$h]['sum'] = $this->getAllSeconds(intval($sum)); + // var_dump($player_data[$h]['sum']);exit; + $player_data[$h]['info'] = $player_info; + $player_data[$h]['fly_num'] = $i; + $player_data[$h]['first'] = '0'; + $player_data[$h]['second'] = '0'; + $player_data[$h]['third'] = '0'; + } + $h++; + // var_dump($value);exit; + // var_dump(($avg_socre));exit; + } + array_multisort(array_column($player_data,'fly_num'), SORT_DESC, array_column($player_data, 'sum'), SORT_ASC, array_column($player_data, 'first'), SORT_ASC,array_column($player_data, 'second'), SORT_ASC,array_column($player_data, 'third'), SORT_ASC,$player_data); + // var_dump($player_data);exit; + if($export === true){ + $round = $this->matchRankingModel->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->max("other_round"); + // $round = 3; + $a = 1; + foreach ($player_data as $k => $val){ + + for ($a = 1; $a <= $round; $a++){ + $round_res = $this->matchRankingModel->field('first_fly_time,second_fly_time,third_fly_time')->where("match_id", "eq", $match_id)->where("course", "eq", $course)->where('other_round',$a)->where('player_id',$val['info']['player_id'])->find(); + $player_data[$k]['round'.$a] = $round_res; + } + + } + // var_dump($player_data);exit; + } + return $player_data; + } + + //资格赛 + public function bestplayerqua($match_id,$course,$export = false){ + // $match_id = 468; $course = 4; + $user = new User(); + $club = new Club(); + $palyer = new Players(); + $ClubInvate = new ClubInvate(); + $matchRankingModel = new MatchRanking(); + if($course == 103) $course = 101; + $player_res = $matchRankingModel->field('*')->where('match_id',$match_id)->where('course',$course)->group('player_id')->select(); + // var_dump($player_res);exit; + $h = 0; + foreach ($player_res as $k => &$value){ + $club_res = []; + // $user_res = $user->where('member_number',$value['player_id'])->find(); + // $player_ress = $palyer->where('member_id',$user_res['id'])->find(); + // $invate_res = $ClubInvate->where('player_id',$player_ress['id'])->where('status',6)->where('deletetime',null)->find(); + // if($invate_res) + // $club_res = $club->field('name_short')->where('id',$invate_res['club_id'])->find(); + + $dan_fly = $matchRankingModel->where('match_id',$match_id)->where('course',$course)->where('player_id',$value['player_id'])->select(); + $player_info = $matchRankingModel->field('player_id,player_name,grouping,led_color,channel,name_short,country,national_flag')->where('match_id',$match_id)->where('course',$course)->where('player_id',$value['player_id'])->find()->toArray(); + // var_dump($player_info);exit; + $dan_data = [];$i = 0; $z = 0; + + foreach ($dan_fly as $q){ + if(preg_match('/\d/',$q['first_fly_time'])) { + $dan_data[$i] = $this->getAllMs($q['first_fly_time']); + $i++; + } + if(preg_match('/\d/',$q['second_fly_time'])) { + $dan_data[$i] = $this->getAllMs($q['second_fly_time']); + $i++; + } + if(preg_match('/\d/',$q['third_fly_time'])) { + $dan_data[$i] = $this->getAllMs($q['third_fly_time']); + $i++; + } + if($q['first_fly_time'] == 'DNF' || $q['second_fly_time'] == 'DNF' || $q['third_fly_time'] == 'DNF'){ + $z++; + } + + } + sort($dan_data); + // var_dump($dan_data);exit; + $j = 0; + if($i >=3 ){ + $avg_socre = ($dan_data[0] + $dan_data[1] + $dan_data[2])/3; + // var_dump($avg_socre);exit; + // var_dump($this->getAllSeconds(intval($avg_socre)));exit; + // exit; + $integerPart = floor($avg_socre); // 获取整数部分 + $decimalPart = fmod($avg_socre, $integerPart); // 获取小数部分 + $lastTwoDigits = substr((string)number_format($decimalPart, 1), -1); + // var_dump($lastTwoDigits);exit; + $player_data[$h]['avg'] = ($this->getAllSeconds(intval($avg_socre))).$lastTwoDigits; + // var_dump($player_data[$h]['avg']);exit; + $player_data[$h]['info'] = $player_info; + $player_data[$h]['club'] = $club_res; + $player_data[$h]['fly_num'] = 3; + $player_data[$h]['first'] = $dan_data[0]; + $player_data[$h]['second'] = $dan_data[1]; + $player_data[$h]['third'] = $dan_data[2]; + $player_data[$h]['dnf'] = 0; + }elseif($i == 2){ + $avg_socre = ($dan_data[0] + $dan_data[1])/2; + $integerPart = floor($avg_socre); // 获取整数部分 + $decimalPart = fmod($avg_socre, $integerPart); // 获取小数部分 + $lastTwoDigits = substr((string)number_format($decimalPart, 1), -1); + + $player_data[$h]['avg'] = ($this->getAllSeconds(intval($avg_socre))).$lastTwoDigits; + $player_data[$h]['info'] = $player_info; + $player_data[$h]['club'] = $club_res; + $player_data[$h]['fly_num'] = 2; + $player_data[$h]['first'] = $dan_data[0]; + $player_data[$h]['second'] = $dan_data[1]; + $player_data[$h]['third'] = '0'; + $player_data[$h]['dnf'] = 0; + }elseif($i == 1){ + $avg_socre = $dan_data[0]; + $player_data[$h]['avg'] = $this->getAllSeconds(intval($avg_socre)); + $player_data[$h]['info'] = $player_info; + $player_data[$h]['club'] = $club_res; + $player_data[$h]['fly_num'] = 1; + $player_data[$h]['first'] = $dan_data[0]; + $player_data[$h]['second'] = '0'; + $player_data[$h]['third'] = '0'; + $player_data[$h]['dnf'] = 0; + }elseif($i == 0){ + if($z > 0){ + $player_data[$h]['avg'] = 'DNF'; + $player_data[$h]['info'] = $player_info; + $player_data[$h]['club'] = $club_res; + $player_data[$h]['fly_num'] = 0; + $player_data[$h]['first'] = '0'; + $player_data[$h]['second'] = '0'; + $player_data[$h]['third'] = '0'; + $player_data[$h]['dnf'] = 0; + }else{ + $player_data[$h]['avg'] = ''; + $player_data[$h]['info'] = $player_info; + $player_data[$h]['club'] = $club_res; + $player_data[$h]['fly_num'] = 0; + $player_data[$h]['first'] = '0'; + $player_data[$h]['second'] = '0'; + $player_data[$h]['third'] = '0'; + $player_data[$h]['dnf'] = 1; + } + + } + $h++; + // var_dump($value);exit; + // var_dump(($avg_socre));exit; + } + // var_dump($player_data);exit; + sort($player_data); + // $player_info = $player_info; + array_multisort(array_column($player_data,'fly_num'), SORT_DESC, array_column($player_data, 'dnf'), SORT_ASC,array_column($player_data, 'avg'), SORT_ASC, array_column($player_data, 'first'), SORT_ASC,array_column($player_data, 'second'), SORT_ASC,array_column($player_data, 'third'), SORT_ASC,$player_data); + + if($export === true){ + $round = $this->matchRankingModel->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->max("other_round"); + // $round = 3; + $a = 1; + foreach ($player_data as $k => $val){ + + for ($a = 1; $a <= $round; $a++){ + $round_res = $this->matchRankingModel->field('first_fly_time,second_fly_time,third_fly_time')->where("match_id", "eq", $match_id)->where("course", "eq", $course)->where('other_round',$a)->where('player_id',$val['info']['player_id'])->find(); + $player_data[$k]['round'.$a] = $round_res; + } + + } + // var_dump($player_data);exit; + } + + + return $player_data; + // var_dump($player_data);exit; + } + + + public function qqq($match_id,$course,$export = false){ + $user = new User(); + $club = new Club(); + $palyer = new Players(); + $ClubInvate = new ClubInvate(); + $matchRankingModel = new MatchRanking(); + if($course == 103) $course = 101; + $player_res = $matchRankingModel->field('*')->where('match_id',$match_id)->where('course',$course)->group('player_id')->order('id')->select(); + // var_dump($player_res);exit; + $round = $matchRankingModel->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->max("other_round"); + $new_arr = []; + foreach ($player_res as $k => &$val){ + $player_rank = $matchRankingModel->where('match_id',$match_id)->where('course',$course)->where('player_id',$val['player_id'])->select(); + $a = 1; + for ($a = 1; $a <= $round; $a++){ + $round_res = $this->matchRankingModel->field('first_fly_time,second_fly_time,third_fly_time')->where("match_id", "eq", $match_id)->where("course", "eq", $course)->where('other_round',$a)->where('player_id',$val['player_id'])->find(); + $player_data[$k]['round'.$a] = $round_res; + $player_data[$k]['info'] = $val; + } + // var_dump($player_data);exit; + // $new_arr=1; + } + // var_dump($player_data);exit; + return $player_data; + } + + public function group_people($match_id,$course = 101,$export = false){ + // var_dump($course);exit; + $matchRankingModel = new MatchRanking(); + $player_res = $matchRankingModel->field('*')->where('match_id',$match_id)->where('course',$course)->group('grouping')->order('id')->find(); + $people_count = $matchRankingModel->field('*')->where('match_id',$match_id)->where('course',$course)->where('grouping',$player_res['grouping'])->where('other_round',1)->count(); + return $people_count; + // var_dump($people_count);exit; + } + + + public function setPlayerScore($param){ + // 判读是否存在该条数据 + $info = $this->getOneRanking([ + 'player_id' => $param['player_id'], + 'match_id' => $param['match_id'], + 'is_finals' => 1, + 'course' => $param['course'], + 'other_round' => $param['other_round'], + ]); + + + $add_data['match_id'] = $param['match_id']; + $add_data['player_id'] = $param['player_id']; + $add_data['fly_num'] = $param['fly_num']; + // $add_data['times'] = $param['achievement']; + $add_data['player_name'] = $param['player_name']; + $add_data['course'] = $param['course']; + // $add_data['id'] = $param['id']; + $add_data['led_color'] = $param['led_color']; + $add_data['channel'] = $param['channel']; + $add_data['other_round'] = $param['other_round']; + $add_data['integral'] = $param['integral']; + $add_data['first_fly_time'] = $param['first_fly_time']; + $add_data['second_fly_time'] = $param['second_fly_time']; + $add_data['third_fly_time'] = $param['third_fly_time']; + $num = 0; + if (preg_match('/\d/',$add_data['first_fly_time'])){ + $first_fly_time = $this->getAllMs($param['first_fly_time']); + $num++; + }else{ + $first_fly_time = 0; + } + if (preg_match('/\d/',$add_data['second_fly_time'])){ + $second_fly_time = $this->getAllMs($param['second_fly_time']); + $num++; + }else{ + $second_fly_time = 0; + } + if (preg_match('/\d/',$add_data['third_fly_time'])){ + $third_fly_time = $this->getAllMs($param['third_fly_time']); + $num++; + }else{ + $third_fly_time = 0; + } + + // if ($num > 0) { + // $add_data['ranking'] = "Y"; + // $first_fly_time = $this->getAllMs($param['first_fly_time']); + // $second_fly_time = $this->getAllMs($param['second_fly_time']); + // $third_fly_time = $this->getAllMs($param['third_fly_time']); + $add_data['achievement'] = $first_fly_time + $second_fly_time + $third_fly_time; + // var_dump($add_data['achievement']);exit; + $add_data['times'] = $this->getAllSeconds($add_data['achievement']); + // } else { + // $add_data['ranking'] = "N"; + // $add_data['achievement'] = 999999999; + // } + // var_dump($param);exit; + if($param['course'] == 103 || $param['course'] == 104){ + $add_data['times'] = $param['times']; + if (preg_match('/\d/',$param['times'])){ + $add_data['achievement'] = $this->getAllMs($param['times']); + }else{ + $add_data['times'] = $param['times']; + $add_data['datetime_value'] = "2023-12-12 00:00:00.000"; + $add_data['achievement'] = 999999999; + } + } + // var_dump($add_data['times']);exit; + // $add_data['times'] = '12:05.877'; + if(!empty($add_data['times'])) { + $strs = explode(":", $add_data['times']); + // var_dump($strs);exit; + if (strlen($strs[0]) == 1) { + $add_data['datetime_value'] = "2023-12-12 00:0{$add_data['times']}"; + } elseif (strlen($strs[0]) == 2) { + $add_data['datetime_value'] = "2023-12-12 00:{$add_data['times']}"; + } + }else{ + $add_data['datetime_value'] = "2023-12-12 00:00:00.000"; + } + // var_dump($add_data['datetime_value']);exit; + + + $add_data['updatetime'] = date("Y-m-d H:i:s", time()); + // var_dump($add_data);exit; + // 更新最新数据 + $res = $this->matchRankingModel->save($add_data,['id'=>$param['id']]); + $this->success('修改成功'); + // var_dump($res);exit; + } + + public function get_final_integral_rank($match_id, $course){ + $matchranking = new MatchRanking(); + $row = $matchranking->field('id,player_id, match_id, player_name, led_color, channel, sum(integral) as sum_integral,grouping,qua_rank')->where('match_id',$match_id)->where('course',104)->group('player_id')->orderRaw('sum_integral desc,CAST(qua_rank AS UNSIGNED)')->select(); + return $row; + } + //->where('id','<',80183) + public function get_final_integral($match_id, $course){ + $matchranking = new MatchRanking(); + $row = $matchranking->field('id,player_id, match_id, player_name, led_color, channel, sum(integral) as sum_integral,grouping,qua_rank')->where('match_id',$match_id)->where('course',104)->group('player_id')->orderRaw('sum_integral desc,CAST(qua_rank AS UNSIGNED)')->select(); + return $row; + } + + public function getrank_course($match_id, $course, $other_round,$all = false,$is_pic = 0){ + $user = new User(); + $club = new Club(); + $player = new Players(); + $ClubInvate = new ClubInvate(); + $matchranking = new MatchRanking(); + $Leagueround = new Leagueround(); + // $row = $matchranking->where('match_id',$match_id)->where('course',$course)->where('other_round',$other_round)->order('id')->select(); + // var_dump($is_pic);exit; + // if($match_id == 1036){ + $row = $matchranking->where('match_id',$match_id)->where('course',$course)->where('other_round',$other_round)->orderRaw('CAST(grouping AS UNSIGNED),CASE led_color + WHEN "红" THEN 1 + WHEN "黄" THEN 2 + WHEN "蓝" THEN 3 + WHEN "绿" THEN 4 + ELSE 5 END')->select(); + // } + if($is_pic == 1){ + foreach ($row as &$value){ + $user_res = $user->where('member_number',$value['player_id'])->find(); + $player_res = $player->where('member_id',$user_res['id'])->find(); + $value['player_pic'] = $player_res['player_pic']; + } + } + + // if($course == 104){ + // $result = $matchranking->where(['match_id'=>$match_id,'course'=>104,'other_round'=>1])->find(); + // // var_dump($result['is_extra']);exit; + // if(!empty($result)){ + // if($result['is_extra'] == 1){ + + // $row = $matchranking->where(['match_id'=>$match_id,'course'=>104,'other_round'=>1])->order('custom_sorting')->select(); + // $j = 1; + // if(!empty($row)){ + // // var_dump($row);exit; + // foreach ($row as $v){ + // $q = $v->toArray(); + // $q['group_sort'] = $j; + // $obj_arr[] = $q; + // $j++; + // } + // $row = $obj_arr; + // $row = $this->array_sort($row,'id','asc'); + // } + // }else{ + // $row = $Leagueround->final_rank($match_id); + // $row = $this->array_sort($row,'id','asc'); + // // var_dump($row);exit; + // } + // } + // } + + return $row; + } + + public function array_sort($array,$keys,$type='asc'){ + if(!isset($array) || !is_array($array) || empty($array)){ + return $array; + } + if(!isset($keys) || trim($keys)==''){ + return $array; + } + if(!isset($type) || $type=='' || !in_array(strtolower($type),array('asc','desc'))){ + return $array; + } + $keysvalue=array(); + foreach($array as $key=>$val){ + $val[$keys] = str_replace('-','',$val[$keys]); + $val[$keys] = str_replace(' ','',$val[$keys]); + $val[$keys] = str_replace(':','',$val[$keys]); + $keysvalue[] =$val[$keys]; + } + asort($keysvalue); //key值排序 + reset($keysvalue); //指针重新指向数组第一个 + foreach($keysvalue as $key=>$vals) { + $keysort[] = $key; + } + $keysvalue = array(); + $count=count($keysort); + if(strtolower($type) != 'asc'){ + for($i=$count-1; $i>=0; $i--) { + $keysvalue[] = $array[$keysort[$i]]; + } + }else{ + for($i=0; $i<$count; $i++){ + $keysvalue[] = $array[$keysort[$i]]; + } + } + return $keysvalue; + } + + + /** + * 获取指定选手成绩 + * @Author:Soar + * @Time:2023/11/20 10:58 + * @param $where + * @return array|bool|\PDOStatement|string|\think\Model|null + */ + public function getOneRanking($where) + { + try { + return $this->matchRankingModel->where($where)->find(); + } catch (\Exception $exception) { + return []; + } + } + + /** + * 计时换算成毫秒 + * @Created by PhpStorm. + * @Author:Soar + * @Time:2023/11/15 15:11 + * @param $times + * @return float|int|mixed|string + */ + private function getAllMs($times) + { + $minute_ms = 0; + $second_ms = 0; + $ms = 0; + // 截取最后的毫秒 + $allTime = explode(".", $times); + // var_dump($allTime);exit; + if (count($allTime) != 2) { + $this->error("成绩格式有误!"); + } + $ms = $allTime[1]; + // 截取分秒 + $minute = explode(":", $allTime[0]); + if (count($minute) != 2) { + $this->error("成绩格式有误!"); + } + if ($minute[0] != 00) { + // 如果第一位不是 00 则代表每分钟 + $minute_ms = $minute[0] * 60 * 1000; + } + + if ($minute[1] != 00) { + $second_ms = $minute[1] * 1000; + } + // var_dump($second_ms);exit; + return $ms + $second_ms + $minute_ms; + } + + + private function getAllSeconds($seconds){ + // var_dump($seconds); + $lastTwoDigits = substr((string)$seconds, -3); + $newStr = substr((string)$seconds, 0, -3); + // var_dump(floor($newStr / 60));exit; + $minutes = floor($newStr / 60); + $remainingSeconds = $newStr % 60; + $res = sprintf("%02d:%02d", $minutes, $remainingSeconds); + // var_dump($res.'.'.$lastTwoDigits);exit; + return $res.'.'.$lastTwoDigits; + } + + private function getSeconds($seconds){ + // $number = 123.456; + $seconds = '2,242.667'; + if(intval($seconds) != $seconds){ + // var_dump($seconds);exit; + $integerPart = floor($seconds); // 获取整数部分 + $decimalPart = fmod($seconds, $integerPart); // 获取小数部分 + } + // var_dump($seconds);exit; + $lastTwoDigits = substr((string)$integerPart, -2); + $newStr = substr((string)$integerPart, 0, -2); + // var_dump($seconds);exit; + $minutes = floor($newStr / 60); + $remainingSeconds = $newStr % 60; + $res = sprintf("%02d:%02d", $minutes, $remainingSeconds); + // var_dump($res.'.'.$lastTwoDigits);exit; + return $res.'.'.$lastTwoDigits.$decimalPart; + } + +} \ No newline at end of file diff --git a/application/admin/service/RankingService.php b/application/admin/service/RankingService.php new file mode 100644 index 0000000..a45b966 --- /dev/null +++ b/application/admin/service/RankingService.php @@ -0,0 +1,1712 @@ +matchRankingModel = new MatchRanking(); + } + + /** + * 添加成绩 + * @Created by PhpStorm. + * @Author:Soar + * @Time:2023/11/15 16:07 + * @param $file_name + * @param $course + * @return true + * @throws \PhpOffice\PhpSpreadsheet\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function upload_excel($file_name, $course = 32, $match_id) + { + $this->matchRankingModel = new MatchRanking(); + + $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); + $file = ROOT_PATH."public". $file_name; + $PHPExcel = $reader->load($file); + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $allRow = $currentSheet->getHighestRow(); //取得一共有多少行 + if ($allRow <= 2) { + error_stop('您的成绩列表为空'); + } + + $id = 0; + for ($currentRow = 4; $currentRow <= $allRow; $currentRow++) + { + if (!empty($currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue())) { + if ($currentSheet->getCellByColumnAndRow(1, $currentRow)->getMergeRange()) { + $explode = explode(":", $currentSheet->getCellByColumnAndRow(1, $currentRow)->getMergeRange()); + $player[$id]['grouping'] = $currentSheet->getCell($explode[0])->getValue(); + preg_match_all("/[a-zA-Z]/u",$player[$id]['grouping'],$arr); + $player[$id]['grouping'] = implode($arr[0]); + } else { + $player[$id]['grouping'] = $currentSheet->getCellByColumnAndRow(1, $currentRow)->getValue(); + preg_match_all("/[a-zA-Z]/u",$player[$id]['grouping'],$arr); + $player[$id]['grouping'] = implode($arr[0]); + } + + $player[$id]['player_id'] = $currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue(); + $player[$id]['player_name'] = $currentSheet->getCellByColumnAndRow(3, $currentRow)->getValue(); + $player[$id]['fly_num'] = $currentSheet->getCellByColumnAndRow(4, $currentRow)->getValue(); + $player[$id]['times'] = $currentSheet->getCellByColumnAndRow(5, $currentRow)->getValue(); + //$player[$id]['grouping'] = $currentSheet->getCellByColumnAndRow(6, $currentRow)->getValue(); + + if (!empty($player[$id]['times'])) { + if (is_numeric($player[$id]['fly_num'])) { + $player[$id]['achievement'] = $this->getAllMs($player[$id]['times']); + } else { + $player[$id]['achievement'] = 999999999; + } + } else { + $player[$id]['achievement'] = 999999999; + } + + if (!empty($player[$id]['times'])) { + $strs = explode(":", $player[$id]['times']); + + if (strlen($strs[0]) == 1) { + $player[$id]['datetime_value'] = "2023-12-12 00:0{$player[$id]['times']}"; + } elseif (strlen($strs[0]) == 2) { + $player[$id]['datetime_value'] = "2023-12-12 00:{$player[$id]['times']}"; + } + } + + $player[$id]['match_id'] = $match_id; + + if ($player[$id]['fly_num'] < 3) { + $player[$id]['ranking'] = "N"; + } else { + $player[$id]['ranking'] = "Y"; + } + + $player[$id]['promoted'] = 0; + $player[$id]['course'] = $course; + if ($course == 1) { + $player[$id]['is_finals'] = 1; + } + + if ($id <= ($course - 1)) { + $player[$id]['promoted'] = 1; + } + + $PDOStatement = $this->matchRankingModel->where([ + 'player_id' => $player[$id]['player_id'], + 'match_id' => $player[$id]['match_id'], + 'course' => $player[$id]['course'] + ])->find(); + + if (empty($PDOStatement)) { + $inserts[] = $this->matchRankingModel->insert($player[$id]); + } else { + $player[$id]['id'] = $PDOStatement->id; + $inserts[] = $this->matchRankingModel->update($player[$id]); + } + + ++$id; + } + } + + // $this->updatePlayerRanking($match_id, $course); + $this->rankingSort(['match_id' => $match_id, 'course' => $course]); + + return true; + } + + /** + * 上传赛事的参赛选手数据 + * @Author:Soar + * @Time:2023/11/20 10:59 + * @param $filename + * @param $match_id + * @return ExceptionThrower|true + * @throws \PhpOffice\PhpSpreadsheet\Exception + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function uploadPlayer($filename, $match_id = 0, $course = 32) + { + if ($match_id == 0) { + return new ExceptionThrower("赛事id不能为空"); + } + $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); + $file = ROOT_PATH."public". $filename; + $PHPExcel = $reader->load($file); + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $allRow = $currentSheet->getHighestRow(); //取得一共有多少行 + if ($allRow <= 2) { + error_stop('您的选手列表为空'); + } + + Db::startTrans(); + + for ($currentRow = 3; $currentRow <= $allRow; $currentRow++) + { + + if ($currentRow == $this->fist_row) { // 获取选手第一行 + // 获取赛程轮次 + $this->round = $currentSheet->getCellByColumnAndRow(6, $this->fist_row)->getValue(); + } + + $player = []; + if (!empty($currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue())) { + if ($currentSheet->getCellByColumnAndRow(1, $currentRow)->getMergeRange()) { + $explode = explode(":", $currentSheet->getCellByColumnAndRow(1, $currentRow)->getMergeRange()); + $player['grouping'] = $currentSheet->getCell($explode[0])->getValue(); + // var_dump($player['grouping']);exit; + preg_match_all("/[0-9]/u",$player['grouping'],$arr); + // var_dump($arr);exit; + $player['grouping'] = implode($arr[0]); + } else { + $player['grouping'] = $currentSheet->getCellByColumnAndRow(1, $currentRow)->getValue(); + preg_match_all("/[0-9]u",$player['grouping'],$arr); + $player['grouping'] = implode($arr[0]); + } + + $player['player_id'] = $currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue(); + $player['player_name'] = $currentSheet->getCellByColumnAndRow(3, $currentRow)->getValue(); + $player['channel'] = $currentSheet->getCellByColumnAndRow(4, $currentRow)->getValue(); + $player['led_color'] = $currentSheet->getCellByColumnAndRow(5, $currentRow)->getValue(); + $player['match_id'] = $match_id; + $player['course'] = $course; + if ($course == 1) { + $player['is_finals'] = 1; + } + + if (!empty($this->round)) { + for ($i = 1; $i <= $this->round; $i++) { + $PDOStatement = $this->matchRankingModel->where([ + "player_id" => $player['player_id'], + "match_id" => $player['match_id'], + "course" => $player['course'], + "other_round" => $i + ])->find(); + + if ($i == 1) { + $player['bast_performance'] = 1; + } else { + $player['bast_performance'] = 0; + } + + if (empty($PDOStatement)) { + $player['other_round'] = $i; + try { + $this->matchRankingModel->insert($player); + } catch (DbException $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } + } + } + /* + * + * 废弃方法 新方法添加了轮次 + if (empty($PDOStatement)) { + try { + $info = $this->matchRankingModel->insert($player); + } catch (DbException $exception) { + $this->error($exception->getMessage()); + } + + } + */ + + } + + } + Db::commit(); + //$this->rankingSort(["match_id" => $match_id, "course" => $course]); + //$this->updatePlayerRanking($match_id, $course); + $this->rankingSort(['match_id' => $match_id, 'course' => $course]); + return true; + } + + + //2024大师赛单轮导入 + public function singleRoundMasterExport($filename, $match_id = 0, $course = 101) + { + if ($match_id == 0) { + return new ExceptionThrower("赛事id不能为空"); + } + $ClubInvate = new ClubInvate(); + $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); + $file = ROOT_PATH."public". $filename; + $PHPExcel = $reader->load($file); + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $allRow = $currentSheet->getHighestRow(); //取得一共有多少行 + if ($allRow <= 2) { + error_stop('您的选手列表为空'); + } + + Db::startTrans(); + $a = 0; + for ($currentRow = 3; $currentRow <= $allRow; $currentRow++) + { + + if ($currentRow == $this->fist_row) { // 获取选手第一行 + // 获取赛程轮次 + $this->round = $currentSheet->getCellByColumnAndRow(7, $this->fist_row)->getValue(); + } + + $player = []; + if (!empty($currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue())) { + if ($currentSheet->getCellByColumnAndRow(1, $currentRow)->getMergeRange()) { + $explode = explode(":", $currentSheet->getCellByColumnAndRow(1, $currentRow)->getMergeRange()); + $player['grouping'] = $currentSheet->getCell($explode[0])->getValue(); + // var_dump($player['grouping']);exit; + preg_match_all("/[0-9]/u",$player['grouping'],$arr); + // var_dump($arr);exit; + $player['grouping'] = implode($arr[0]); + } else { + $player['grouping'] = $currentSheet->getCellByColumnAndRow(1, $currentRow)->getValue(); + preg_match_all("/[0-9]u",$player['grouping'],$arr); + $player['grouping'] = implode($arr[0]); + } + + $player['player_id'] = $currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue(); + $player['player_name'] = $currentSheet->getCellByColumnAndRow(3, $currentRow)->getValue(); + $player['country'] = $currentSheet->getCellByColumnAndRow(4, $currentRow)->getValue(); + $player['channel'] = $currentSheet->getCellByColumnAndRow(5, $currentRow)->getValue(); + $player['led_color'] = $currentSheet->getCellByColumnAndRow(6, $currentRow)->getValue(); + $player['match_id'] = $match_id; + $player['course'] = $course; + if ($course == 1) { + $player['is_finals'] = 1; + } + // var_dump($this->round);exit; + if (!empty($this->round)) { + $PDOStatement = $this->matchRankingModel->where([ + "player_id" => $player['player_id'], + "match_id" => $player['match_id'], + "course" => $player['course'], + "other_round" => $this->round + ])->find(); + if(empty($player['country'])){ + $player['country'] = '中国 China'; + } + $countryflag_info = model('countryflag')->where('country',$player['country'])->find(); + // var_dump($countryflag_info['national_flag']);exit; + if(empty($countryflag_info)) { + return $currentRow; + + } + $player['national_flag'] = $countryflag_info['national_flag']; + + + if (empty($PDOStatement)) { + // var_dump(123);exit; + $player['other_round'] = $this->round; + try { + if($a == 0){ + $this->matchRankingModel->where('match_id',$match_id)->where('course',$course)->where('other_round',$this->round)->delete(); + $a++; + } + $this->matchRankingModel->insert($player); + } catch (DbException $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + }else{ + + // var_dump(2314);exit; + $player['other_round'] = $this->round; + try { + if($a == 0){ + $this->matchRankingModel->where('match_id',$match_id)->where('course',$course)->where('other_round',$this->round)->delete(); + $a++; + } + $this->matchRankingModel->insert($player); + } catch (DbException $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } + + } + + } + + } + Db::commit(); + //$this->rankingSort(["match_id" => $match_id, "course" => $course]); + //$this->updatePlayerRanking($match_id, $course); + $this->rankingSort(['match_id' => $match_id, 'course' => $course]); + return true; + } + + //单轮导入 + public function singleRoundExport($filename, $match_id = 0, $course = 101) + { + if ($match_id == 0) { + return new ExceptionThrower("赛事id不能为空"); + } + $ClubInvate = new ClubInvate(); + $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); + $file = ROOT_PATH."public". $filename; + $PHPExcel = $reader->load($file); + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $allRow = $currentSheet->getHighestRow(); //取得一共有多少行 + if ($allRow <= 2) { + error_stop('您的选手列表为空'); + } + + Db::startTrans(); + $a = 0; + for ($currentRow = 3; $currentRow <= $allRow; $currentRow++) + { + + if ($currentRow == $this->fist_row) { // 获取选手第一行 + // 获取赛程轮次 + $this->round = $currentSheet->getCellByColumnAndRow(7, $this->fist_row)->getValue(); + } + + $player = []; + if (!empty($currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue())) { + if ($currentSheet->getCellByColumnAndRow(1, $currentRow)->getMergeRange()) { + $explode = explode(":", $currentSheet->getCellByColumnAndRow(1, $currentRow)->getMergeRange()); + $player['grouping'] = $currentSheet->getCell($explode[0])->getValue(); + // var_dump($player['grouping']);exit; + preg_match_all("/[0-9]/u",$player['grouping'],$arr); + // var_dump($arr);exit; + $player['grouping'] = implode($arr[0]); + } else { + $player['grouping'] = $currentSheet->getCellByColumnAndRow(1, $currentRow)->getValue(); + preg_match_all("/[0-9]u",$player['grouping'],$arr); + $player['grouping'] = implode($arr[0]); + } + + $player['player_id'] = $currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue(); + $player['player_name'] = $currentSheet->getCellByColumnAndRow(3, $currentRow)->getValue(); + $player['name_short'] = $currentSheet->getCellByColumnAndRow(4, $currentRow)->getValue(); + $player['channel'] = $currentSheet->getCellByColumnAndRow(5, $currentRow)->getValue(); + $player['led_color'] = $currentSheet->getCellByColumnAndRow(6, $currentRow)->getValue(); + $player['match_id'] = $match_id; + $player['course'] = $course; + if ($course == 1) { + $player['is_finals'] = 1; + } + // var_dump($this->round);exit; + if (!empty($this->round)) { + $PDOStatement = $this->matchRankingModel->where([ + "player_id" => $player['player_id'], + "match_id" => $player['match_id'], + "course" => $player['course'], + "other_round" => $this->round + ])->find(); + $invate_info = $ClubInvate->where(['name_short'=>$player['name_short'],'status'=>6,'member_number'=>$player['player_id']])->find(); + if(empty($invate_info)) { + return $currentRow; + + } + + + + if (empty($PDOStatement)) { + // var_dump(123);exit; + $player['other_round'] = $this->round; + try { + if($a == 0){ + $this->matchRankingModel->where('match_id',$match_id)->where('course',$course)->where('other_round',$this->round)->delete(); + $a++; + } + $this->matchRankingModel->insert($player); + } catch (DbException $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + }else{ + + // var_dump(2314);exit; + $player['other_round'] = $this->round; + try { + if($a == 0){ + $this->matchRankingModel->where('match_id',$match_id)->where('course',$course)->where('other_round',$this->round)->delete(); + $a++; + } + $this->matchRankingModel->insert($player); + } catch (DbException $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } + + } + + } + + } + Db::commit(); + //$this->rankingSort(["match_id" => $match_id, "course" => $course]); + //$this->updatePlayerRanking($match_id, $course); + $this->rankingSort(['match_id' => $match_id, 'course' => $course]); + return true; + } + + + //联赛导入 + public function uploadPlayers($filename, $match_id = 0, $course = 32) + { + if ($match_id == 0) { + return new ExceptionThrower("赛事id不能为空"); + } + $ClubInvate = new ClubInvate(); + $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); + $file = ROOT_PATH."public". $filename; + $PHPExcel = $reader->load($file); + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $currentSheet = $PHPExcel->getSheet(0); //读取文件中的第一个工作表 + $allRow = $currentSheet->getHighestRow(); //取得一共有多少行 + if ($allRow <= 2) { + error_stop('您的选手列表为空'); + } + + Db::startTrans(); + + for ($currentRow = 3; $currentRow <= $allRow; $currentRow++) + { + + if ($currentRow == $this->fist_row) { // 获取选手第一行 + // 获取赛程轮次 + $this->round = $currentSheet->getCellByColumnAndRow(7, $this->fist_row)->getValue(); + } + + $player = []; + if (!empty($currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue())) { + if ($currentSheet->getCellByColumnAndRow(1, $currentRow)->getMergeRange()) { + $explode = explode(":", $currentSheet->getCellByColumnAndRow(1, $currentRow)->getMergeRange()); + $player['grouping'] = $currentSheet->getCell($explode[0])->getValue(); + // var_dump($player['grouping']);exit; + preg_match_all("/[0-9]/u",$player['grouping'],$arr); + // var_dump($arr);exit; + $player['grouping'] = implode($arr[0]); + } else { + $player['grouping'] = $currentSheet->getCellByColumnAndRow(1, $currentRow)->getValue(); + preg_match_all("/[0-9]u",$player['grouping'],$arr); + $player['grouping'] = implode($arr[0]); + } + + $player['player_id'] = $currentSheet->getCellByColumnAndRow(2, $currentRow)->getValue(); + $player['player_name'] = $currentSheet->getCellByColumnAndRow(3, $currentRow)->getValue(); + $player['name_short'] = $currentSheet->getCellByColumnAndRow(4, $currentRow)->getValue(); + $player['channel'] = $currentSheet->getCellByColumnAndRow(5, $currentRow)->getValue(); + $player['led_color'] = $currentSheet->getCellByColumnAndRow(6, $currentRow)->getValue(); + $player['match_id'] = $match_id; + $player['course'] = $course; + if ($course == 1) { + $player['is_finals'] = 1; + } + // var_dump($player);exit; + if (!empty($this->round)) { + for ($i = 1; $i <= $this->round; $i++) { + $PDOStatement = $this->matchRankingModel->where([ + "player_id" => $player['player_id'], + "match_id" => $player['match_id'], + "course" => $player['course'], + "other_round" => $i + ])->find(); + $invate_info = $ClubInvate->where(['name_short'=>$player['name_short'],'status'=>6,'member_number'=>$player['player_id']])->find(); + if(empty($invate_info)) { + return $currentRow; + // var_dump( $currentRow);exit; + + } + // if ($i == 1) { + // $player['bast_performance'] = 1; + // } else { + // $player['bast_performance'] = 0; + // } + + if (empty($PDOStatement)) { + $player['other_round'] = $i; + try { + $this->matchRankingModel->insert($player); + } catch (DbException $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } + } + } + /* + * + * 废弃方法 新方法添加了轮次 + if (empty($PDOStatement)) { + try { + $info = $this->matchRankingModel->insert($player); + } catch (DbException $exception) { + $this->error($exception->getMessage()); + } + + } + */ + + } + + } + Db::commit(); + //$this->rankingSort(["match_id" => $match_id, "course" => $course]); + //$this->updatePlayerRanking($match_id, $course); + $this->rankingSort(['match_id' => $match_id, 'course' => $course]); + return true; + } + + /** + * 添加飞手成绩 + * @Author:Soar + * @Time:2023/11/20 10:59 + * @param $param + * @return MatchRanking|int|string + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function setToPlayer($param) + { + // 判断是否为决赛 + if (!empty($param['is_finals']) && $param['is_finals'] != 'false') { + + // 如果是决赛 + // 判读是否存在该条数据 + $info = $this->getOneRanking([ + 'player_id' => $param['player_id'], + 'match_id' => $param['match_id'], + 'is_finals' => 1, + 'course' => $param['course'], + 'other_round' => $param['other_round'], + ]); + + + if (empty($info)) { + // 获取用户信息 + $user = new User(); + $user_info = $user->with("players")->where('member_number', '=', $param['player_id'])->find(); + // 添加数据 + $add_data['match_id'] = $param['match_id']; + $add_data['player_id'] = $param['player_id']; + $add_data['player_name'] = $user_info->players->real_name; + $add_data['fly_num'] = $param['fly_num']; + $add_data['times'] = $param['achievement']; + $add_data['course'] = $param['course']; + $add_data['is_finals'] = 1; + $add_data['finals_round'] = $param['finals_round']; // 轮次 + $add_data['integral'] = $param['integral']; // 积分 + $add_data['ranking'] = "Y"; + if (is_numeric($add_data['fly_num'])) { + $add_data['achievement'] = $this->getAllMs($param['achievement']); + } else { + $add_data['achievement'] = 999999999; + } + $add_data['grouping'] = $param['group']; + $add_data['led_color'] = $param['led_color']; + $add_data['channel'] = $param['channel']; + $add_data['other_round'] = $param['other_round']; + + $add_data['createtime'] = date("Y-m-d H:i:s", time()); + + if (!empty($add_data['times'])) { + $strs = explode(":", $add_data['times']); + + if (strlen($strs[0]) == 1) { + $add_data['datetime_value'] = "2023-12-12 00:0{$add_data['times']}"; + } elseif (strlen($strs[0]) == 2) { + $add_data['datetime_value'] = "2023-12-12 00:{$add_data['times']}"; + } + } + + + + $res = $this->matchRankingModel->insert($add_data); + } else { + $add_data['match_id'] = $param['match_id']; + $add_data['player_id'] = $param['player_id']; + $add_data['fly_num'] = $param['fly_num']; + $add_data['times'] = $param['achievement']; + $add_data['course'] = $param['course']; + $add_data['ranking'] = "Y"; + if (is_numeric($add_data['fly_num'])) { + $add_data['achievement'] = $this->getAllMs($param['achievement']); + } else { + $add_data['achievement'] = 999999999; + } + $add_data['is_finals'] = 1; + $add_data['finals_round'] = $param['finals_round']; // 轮次 + $add_data['integral'] = $param['integral']; // 积分 + $add_data['led_color'] = $param['led_color']; + $add_data['channel'] = $param['channel']; + $add_data['other_round'] = $param['other_round']; + $add_data['updatetime'] = date("Y-m-d H:i:s", time()); + + if (!empty($add_data['times'])) { + $strs = explode(":", $add_data['times']); + + if (strlen($strs[0]) == 1) { + $add_data['datetime_value'] = "2023-12-12 00:0{$add_data['times']}"; + } elseif (strlen($strs[0]) == 2) { + $add_data['datetime_value'] = "2023-12-12 00:{$add_data['times']}"; + } + } + + $res = $this->matchRankingModel->update($add_data, ['id' => $info->id]); + } + } else { + // 判读是否存在该条数据 + $info = $this->getOneRanking([ + 'player_id' => $param['player_id'], + 'match_id' => $param['match_id'], + 'course' => $param['course'], + 'other_round' => $param['other_round'] + ]); + + if (empty($info)) { + // 获取用户信息 +// $user = new User(); +// $user_info = $user->with("players")->where('member_number', '=', $param['player_id'])->find(); + // 添加数据 + $add_data['match_id'] = $param['match_id']; + $add_data['player_id'] = $param['player_id']; +// $add_data['player_name'] = $user_info->players->real_name; + $add_data['player_name'] = $param['player_name']; + $add_data['fly_num'] = $param['fly_num']; + $add_data['times'] = $param['achievement']; + $add_data['course'] = $param['course']; + $add_data['grouping'] = $param['group']; + $add_data['led_color'] = $param['led_color']; + $add_data['channel'] = $param['channel']; + $add_data['other_round'] = $param['other_round']; + if (is_numeric($add_data['fly_num'])) { + $add_data['ranking'] = "Y"; + $add_data['achievement'] = $this->getAllMs($param['achievement']); + } else { + $add_data['ranking'] = "N"; + $add_data['achievement'] = 999999999; + } + + if (!empty($add_data['times'])) { + $strs = explode(":", $add_data['times']); + + if (strlen($strs[0]) == 1) { + $add_data['datetime_value'] = "2023-12-12 00:0{$add_data['times']}"; + } elseif (strlen($strs[0]) == 2) { + $add_data['datetime_value'] = "2023-12-12 00:{$add_data['times']}"; + } + } + + $add_data['createtime'] = date("Y-m-d H:i:s", time()); + + $res = $this->matchRankingModel->insert($add_data); + + } else { + + $add_data['match_id'] = $param['match_id']; + $add_data['player_id'] = $param['player_id']; + $add_data['fly_num'] = $param['fly_num']; + $add_data['times'] = $param['achievement']; + $add_data['player_name'] = $param['player_name']; + $add_data['course'] = $param['course']; + $add_data['id'] = $info->id; + $add_data['led_color'] = $param['led_color']; + $add_data['channel'] = $param['channel']; + $add_data['other_round'] = $param['other_round']; + if (is_numeric($add_data['fly_num'])) { + $add_data['ranking'] = "Y"; + $add_data['achievement'] = $this->getAllMs($param['achievement']); + } else { + $add_data['ranking'] = "N"; + $add_data['achievement'] = 999999999; + } + + if (!empty($add_data['times'])) { + $strs = explode(":", $add_data['times']); + + if (strlen($strs[0]) == 1) { + $add_data['datetime_value'] = "2023-12-12 00:0{$add_data['times']}"; + } elseif (strlen($strs[0]) == 2) { + $add_data['datetime_value'] = "2023-12-12 00:{$add_data['times']}"; + } + } + + + $add_data['updatetime'] = date("Y-m-d H:i:s", time()); + + // 更新最新数据 + $res = $this->matchRankingModel->update($add_data); + } + } + + if ($param['is_finals'] == "false") { + // $this->updatePlayerRankingToRound($param); + $this->update_sort_ranking($param); + $this->updatePlayerRanking($param['match_id'], $param['course']); + } + + return $res; + + } + + + + /** + * 获取指定选手成绩 + * @Author:Soar + * @Time:2023/11/20 10:58 + * @param $where + * @return array|bool|\PDOStatement|string|\think\Model|null + */ + public function getOneRanking($where) + { + try { + return $this->matchRankingModel->where($where)->find(); + } catch (\Exception $exception) { + return []; + } + } + + /** + * 删除参赛选手成绩 + * @Author:Soar + * @Time:2023/11/20 10:58 + * @param $ranking_id + * @return false|int|void + */ + public function delRanking($ranking_id) + { + try { + return $this->matchRankingModel->where("id", "=", $ranking_id)->delete(); + }catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + /** + * 根据赛事id获取所有参赛选手 + * @Author:Soar + * @Time:2023/11/20 11:00 + * @param $match_id + * @return void + */ + public function getCompetitionList($match_id) + { + $matchCompetition = new MatchCompetition(); + + return $matchCompetition->where("match_id", $match_id)->select(); + } + + + /*** + * 更新排名 + * @Author:Soar + * @Time:2023/12/13 10:37 + * @param $match_id + * @param $course + * @return true + */ + public function updatePlayerRanking($match_id, $course) + { + // 获取所有参赛选手 + $all_players = $this->matchRankingModel->where([ + 'match_id' => $match_id, + 'course' => $course, + ])->count(); + + $flyNumIsNull = $this->matchRankingModel->where([ + 'match_id' => $match_id, + 'course' => $course, + ])->whereNull("fly_num")->count(); + + if ($flyNumIsNull == $all_players) { + // 说明赛程未开始,不计算排名 + return true; + } + + $all_ranking_bast = $this->matchRankingModel + ->where("match_id" , "eq", $match_id) + ->where("course", "eq", $course) + ->where("bast_performance", 1) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement") + //->order("round_score") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->select(); + + $rankingNum = 1; + + foreach ($all_ranking_bast as $vals) { + $vals->update(['group_score' => $rankingNum++], ['id' => $vals->id]); + } + + return true; + } + + #[Deprecated] + public function getSemifinalList($match_id) + { + $semifinalList = $this->matchRankingModel->where([ + 'match_id' => $match_id, + 'course' => 4, + 'promoted' => 1, + ])->order("group_score", "ASC")->field("id, player_id, player_name")->select(); + if (empty($semifinalList)) { + return []; + } + + foreach ($semifinalList as $value) { + $value->other = $this->matchRankingModel->where([ + 'match_id' => $match_id, + 'course' => 1, + 'player_id' => $value->player_id + ])->where("led_color", "<>", "") + ->where("channel", "<>", "") + ->field("led_color, channel, player_name") + ->order("id", "DESC") + ->find(); + } + + return $semifinalList; + } + + public function getSemifinalListForRanking($match_id) + { + $semifinalList = $this->matchRankingModel->where("match_id" , "eq", $match_id) + ->where("course", "eq", 4) + ->where("bast_performance", 1) + ->field("player_name, course, match_id, promoted, fly_num, times, grouping, course, player_id") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->limit(4) + ->select(); + + if (empty($semifinalList)) { + return []; + } + + foreach ($semifinalList as $value) { + $value->other = $this->matchRankingModel->where([ + 'match_id' => $match_id, + 'course' => 1, + 'player_id' => $value->player_id + ])->where("led_color", "<>", "") + ->where("channel", "<>", "") + ->field("led_color, channel, player_name") + ->order("id", "DESC") + ->find(); + } + + return $semifinalList; + } + + /** + * 清空指定赛程 + * @Author:Soar + * @Time:2023/11/23 9:47 + * @param $param + * @return true|void + */ + public function clearRanking($param) + { + Db::startTrans(); + try { + + if (empty($param['course'])) { + unset($param['course']); + } + + if ($this->matchRankingModel->where($param)->delete()) { + Db::commit(); + return true; + } + } catch (\Exception $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } + + public function getRankingForCourses ($match_id, $course, $sorting, $other_round) + { + $list = $this->matchRankingModel->where("match_id" , "eq", $match_id)->where("course", "eq", $course); + // 如果是最优成绩 + if ($other_round == $this->BAST_SCORE) { + // 开启排序 + if ($sorting) { + // 是否决赛 + if ($course == 1) { + $list->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, finals_round, integral, id, match_id,achievement"); + $list->order("integral", "DESC"); // 积分倒叙 + } else { + $list->where("other_round", "eq", $other_round); // 轮次 + $list->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement"); + $list->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc'); + } + } else { + // 默认排序 + if ($course == 1) { + $list->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, finals_round, integral, id, match_id,achievement"); + } else { + $list->where("other_round", "eq", $other_round) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement"); + } + } + } else { // 非最优成绩 + if ($sorting) { // 按成绩排序 + if ($course == 1) { // 是否决赛 + $list->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, finals_round, integral, id, match_id,achievement, custom_sorting, group_score") + ->order('datetime_value', 'asc'); + } else { + $list + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement, group_score, custom_sorting") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc'); + $list->where("other_round", "eq", $other_round); + } + + } else { // 默认排序 + if ($course == 1) { // 是否决赛 + $list->where("bast_performance", "eq", 1) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, finals_round, integral, id, match_id,achievement, custom_sorting, group_score"); + } else { + $list->where("bast_performance", "eq", 1) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement,custom_sorting, group_score"); + } + } + } + + $list = $list->select(); + if ($sorting) { + if (!empty($list[0]->custom_sorting)) { + $last_ages = array_column($list,'custom_sorting'); + array_multisort($last_ages ,SORT_ASC,$list); + } + } + + $round = $this->matchRankingModel->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->max("other_round"); + + $res['item'] = $list; + $res['round'] = $round; + + return $res; + } + + public function getRankingForCourse($match_id, $course, $sorting, $other_round) + { + if ($other_round != "最佳成绩") { + if ($sorting) { // 是否排序 + if ($course == 1) { // 是否决赛 + $list = $this->matchRankingModel->where("match_id" , "eq", $match_id) + ->where("course", "eq", $course) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, finals_round, integral, id, match_id,achievement") + ->order("integral", "DESC") + ->select(); + } else { + $list = $this->matchRankingModel + ->where("match_id" , "eq", $match_id) + ->where("course", "eq", $course) + ->where("other_round", "eq", $other_round) // 轮次 + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement") + //->order("round_score") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->select(); + } + + } else { + if ($course == 1) { + $list = $this->matchRankingModel->where("match_id" , "eq", $match_id) + ->where("course", "eq", $course) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, finals_round, integral, id, match_id,achievement") + // ->order('id') + ->select(); + } else { + // var_dump($match_id);exit; + $list = $this->matchRankingModel->where("match_id" , "eq", $match_id) + ->where("course", "eq", $course) + ->where("other_round", "eq", $other_round) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement") + ->order('id') + ->select(); + // var_dump($list);exit; + } + } + } else { + if ($sorting) { + if ($course == 1) { + $list = $this->matchRankingModel->where("match_id" , "eq", $match_id) + ->where("course", "eq", $course) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, finals_round, integral, id, match_id,achievement, custom_sorting, group_score") + ->order('datetime_value', 'asc') + ->select(); + + } else { + + $list = $this->matchRankingModel + ->where("match_id", $match_id) + ->where("course", $course) + ->where("bast_performance", "eq", 1) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement, group_score, custom_sorting") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->select(); + + if (!empty($list[0]->custom_sorting)) { + $last_ages = array_column($list,'custom_sorting'); + array_multisort($last_ages ,SORT_ASC,$list); + } + } + + } else { + if ($course == 1) { + $list = $this->matchRankingModel->where("match_id" , "eq", $match_id) + ->where("course", "eq", $course) + ->where("bast_performance", "eq", 1) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, finals_round, integral, id, match_id,achievement, custom_sorting, group_score") + ->select(); + } else { + $list = $this->matchRankingModel->where("match_id" , "eq", $match_id) + ->where("course", "eq", $course) + ->where("bast_performance", "eq", 1) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement,custom_sorting, group_score") + ->select(); + } + } + } + + $round = $this->matchRankingModel->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->max("other_round"); + + $res['item'] = $list; + $res['round'] = $round; + + return $res; + + } + + /** + * 计算该选手所有轮次的排名 已废弃 + * @Author:Soar + * @Time:2023/12/11 16:54 + * @param $param + * @return void + * @throws DbException + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + */ + #[Deprecated] + public function updatePlayerRankingToRound($param) + { + $all_players = $this->matchRankingModel->where([ + 'match_id' => $param['match_id'], + 'course' => $param['course'], + ])->field("player_id")->group("player_id")->select(); + + foreach ($all_players as $values) { + + // 获取该选手所有飞行三圈数据 + $fly_num3 = $this->matchRankingModel->where([ + 'match_id' => $param['match_id'], + 'course' => $param['course'], + 'fly_num' => 3, + 'player_id' => $values->player_id + ])->order('achievement', 'ASC')->select(); + + // 获取该选手所有飞行二圈数据 + $fly_num2 = $this->matchRankingModel->where([ + 'match_id' => $param['match_id'], + 'course' => $param['course'], + 'fly_num' => 2, + 'player_id' => $values->player_id + ])->order('achievement', 'ASC')->select(); + + // 获取该选手所有飞行一圈数据 + $fly_num1 = $this->matchRankingModel->where([ + 'match_id' => $param['match_id'], + 'course' => $param['course'], + 'fly_num' => 1, + 'player_id' => $values->player_id + ])->order('achievement', 'ASC')->select(); + + //获取飞行DNF数据 + $fly_DNF = $this->matchRankingModel->where([ + 'match_id' => $param['match_id'], + 'course' => $param['course'], + 'fly_num' => "DNF", + 'player_id' => $values->player_id + ])->order('id', 'ASC')->select(); + + // 获取未完成比赛的成绩 + $fly_null = $this->matchRankingModel->where([ + 'match_id' => $param['match_id'], + 'course' => $param['course'], + 'player_id' => $values->player_id + ])->whereNull("fly_num")->order('id', 'ASC')->select(); + + $rankingNum = 1; + + if (!empty($fly_num3)) { + foreach ($fly_num3 as $vals) { + $vals->update(['bast_performance' => $rankingNum++], ['id' => $vals->id]); + } + } + + if (!empty($fly_num2)) { + foreach ($fly_num2 as $vals) { + $vals->update(['bast_performance' => $rankingNum++], ['id' => $vals->id]); + } + } + + + + if (!empty($fly_num1)) { + foreach ($fly_num1 as $vals) { + $vals->update(['bast_performance' => $rankingNum++], ['id' => $vals->id]); + } + } + + if (!empty($fly_DNF)) { + foreach ($fly_DNF as $vals) { + $vals->update(['bast_performance' => $rankingNum++], ['id' => $vals->id]); + } + } + + if (!empty($fly_null)) { + foreach ($fly_null as $vals) { + $vals->update(['bast_performance' => $rankingNum++], ['id' => $vals->id]); + } + } + } + + $this->rankingSort($param); + } + + /** + * 更新每轮赛事的轮次排名 + * @Author:Soar + * @Time:2023/12/11 16:26 + * @return void + * @throws DbException + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + */ + public function rankingSort($param) + { + $match_id = $param['match_id']; + $course = $param['course'];; + + // 获取当前赛事最多轮次 + $round = $this->matchRankingModel->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->max("other_round"); + + // 获取每一轮的赛事 + for ($i = 1; $i <= $round ; $i++) { + $sort = 1; + // 获取该轮的赛事 + $round_list_three = $this->matchRankingModel + ->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->where("other_round", "eq", $i) + ->where("fly_num", "eq", 3) + ->order('achievement', 'ASC') + ->select(); + + $round_list_three = collection($round_list_three)->toArray(); + + foreach ($round_list_three as $value) { + $this->matchRankingModel->where("id", "eq", $value['id']) + ->update(['round_score' => $sort++]); + } + + $round_list_two = $this->matchRankingModel + ->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->where("other_round", "eq", $i) + ->where("fly_num", "eq", 2) + ->order('achievement', 'ASC') + ->select(); + + $round_list_two = collection($round_list_two)->toArray(); + + foreach ($round_list_two as $value) { + $this->matchRankingModel->where("id", "eq", $value['id']) + ->update(['round_score' => $sort++]); + } + + $round_list_one = $this->matchRankingModel + ->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->where("other_round", "eq", $i) + ->where("fly_num", "eq", 1) + ->order('achievement', 'ASC') + ->select(); + + $round_list_one = collection($round_list_one)->toArray(); + + foreach ($round_list_one as $value) { + $this->matchRankingModel->where("id", "eq", $value['id']) + ->update(['round_score' => $sort++]); + } + + $round_list_dnf = $this->matchRankingModel + ->where("match_id", "eq", $match_id) + ->where("course", "eq", $course) + ->where("other_round", "eq", $i) + ->where("fly_num", "eq", "DNF") + ->order('id') + ->select(); + + $round_list_dnf = collection($round_list_dnf)->toArray(); + + foreach ($round_list_dnf as $value) { + $this->matchRankingModel->where("id", "eq", $value['id']) + ->update(['round_score' => $sort++]); + } + + + $round_list_null = $this->matchRankingModel->where([ + 'match_id' => $match_id, + 'course' => $course + ])->where("other_round", "eq", $i)->whereNull("fly_num")->order('id', 'ASC')->select(); + + $round_list_null = collection($round_list_null)->toArray(); + + foreach ($round_list_null as $value) { + $this->matchRankingModel->where("id", "eq", $value['id']) + ->update(['round_score' => $sort++]); + } + + } + } + + public function testRanking() + { + + $player_ids = $this->matchRankingModel + ->where("match_id", 127) + ->where("course", 16) + ->field("player_id") + ->select(); + + $player_ids = collection($player_ids)->toArray(); + $player_ids = array_map("unserialize", array_unique(array_map("serialize", $player_ids))); + + // 获取该选手的最佳成绩 + foreach ($player_ids as $index => $player_id) { + $list = $this->matchRankingModel + ->where("match_id" , "eq", 127) + ->where("course", "eq", 16) + ->where("player_id", "eq", $player_id['player_id']) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement") + //->order("round_score") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->select(); + + $list = collection($list)->toArray(); + + // 获取第一条数据,更新为最佳数据 + $this->matchRankingModel->update(['bast_performance' => 1], ['id' => $list[0]['id']]); + } + + exit; + + $list = $this->matchRankingModel + ->where("match_id", 127) + ->where("match_id", 127) + ->select(); + + $list = collection($list)->toArray(); + foreach ($list as $value) { + if (!empty($value['times'])) { + $strs = explode(":", $value['times']); + + if (strlen($strs[0]) == 1) { + $datetime_value = "2023-12-12 00:0{$value['times']}"; + } elseif (strlen($strs[0]) == 2) { + $datetime_value = "2023-12-12 00:{$value['times']}"; + } + print_r($datetime_value); + echo "
    "; + $this->matchRankingModel->update(["datetime_value" => $datetime_value], ['id' => $value['id']]); + } + } + + } + + public function update_sort_ranking($param) + { + if (!empty($param['player_id'])) { + $player_id = $param['player_id']; + // 获取该选手的最佳成绩 + $list = $this->matchRankingModel + ->where("match_id", $param['match_id']) + ->where("course", $param['course']) + ->where("player_id", "eq", $player_id) + ->field("player_id, player_name, id, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->select(); + + $list = collection($list)->toArray(); + + // 获取第一条数据,更新为最佳数据 + $this->matchRankingModel->update(['bast_performance' => 1, 'round_score' => 1], ['id' => $list[0]['id']]); + + for ($i = 1; $i < count($list); $i++) { + // 更新其他数据 + $this->matchRankingModel->update(['bast_performance' => null, 'round_score' => ($i + 1)], ['id' => $list[$i]['id']]); + } + + return; + } + + $player_ids = $this->matchRankingModel + ->where("match_id", $param['match_id']) + ->where("course", $param['course']) + ->field("player_id") + ->select(); + + $player_ids = collection($player_ids)->toArray(); + $player_ids = array_map("unserialize", array_unique(array_map("serialize", $player_ids))); + + // 获取该选手的最佳成绩 + foreach ($player_ids as $index => $player_id) { + $list = $this->matchRankingModel + ->where("match_id", $param['match_id']) + ->where("course", $param['course']) + ->where("player_id", "eq", $player_id['player_id']) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->select(); + + $list = collection($list)->toArray(); + + // 获取第一条数据,更新为最佳数据 + $this->matchRankingModel->update(['bast_performance' => 1], ['id' => $list[0]['id']]); + + for ($i = 1; $i < count($list); $i++) { + // 更新其他数据 + $this->matchRankingModel->update(['bast_performance' => null], ['id' => $list[$i]['id']]); + } + } + } + + /** + * 自定义排序 + * @Author:Soar + * @Time:2024/1/9 11:11 + * @param $form_sort 原排名 + * @param $to_sort 更新后排名 + * @param $match_id 赛事ID + * @param $course 赛程 + */ + public function CustomSorting($form_sort, $to_sort, $match_id, $course) + { + $list = $this->matchRankingModel + ->where("match_id", $match_id) + ->where("course", $course) + ->where("bast_performance", "eq", 1) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement, group_score, custom_sorting") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('group_score', 'asc') + ->select(); + + if (!empty($list[0]->custom_sorting)) { + $last_ages = array_column($list,'custom_sorting'); + array_multisort($last_ages ,SORT_ASC,$list); + } + + $list = collection($list)->toArray(); + + $this->adjustArrayPosition($list, ($form_sort - 1), ($to_sort - 1)); + + Db::startTrans(); + try { + foreach ($list as $key => $val) { + $this->matchRankingModel->update(["custom_sorting" => ($key + 1)], ["id" => $val['id']]); + } + } catch (\Exception $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + + Db::commit(); + + return true; + } + + function customSort($a, $b) + { + // 定义排序规则 + $flyNumOrder = ['3', '2', '1', 'DNF', null]; + + // 按照 fly_num 字段排序 + $flyNumA = array_search($a['fly_num'], $flyNumOrder); + $flyNumB = array_search($b['fly_num'], $flyNumOrder); + if ($flyNumA !== $flyNumB) { + return $flyNumA - $flyNumB; + } + + // 按照 datetime_value 字段排序 + return strtotime($a['datetime_value']) - strtotime($b['datetime_value']); + } + + public function getOtherRanking(array $data, $round = 0, $res = []) + { + // 存储不同的数据 + $differentData = []; + + $round_max = $this->matchRankingModel->where("match_id", "eq", 127) + ->where("course", "eq", 32) + ->max("other_round"); + + // 获取这些人的id + // 获取这些人的第二优先成绩 + $list = $this->matchRankingModel + ->where("match_id", 127) + ->where("course", 32) + ->whereIn("player_id", implode(",", array_column($data, "player_id"))) + ->where("round_score", (++$round)) + ->field("player_id, player_name, id, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement, round_score") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->select(); + + $datas = collection($list)->toArray(); + if (empty($datas)) { + // 说明一直到最后一轮的成绩都一样 + return ['data' => "all_equation"]; + } + + $groupedData = []; + + foreach ($datas as $val) { + $groupedData[$val['fly_num'] . '-' . $val['times']][] = $val; + } + + if (count($groupedData) == count($datas)) { + $groupedData = $datas; + } + + + if ($round != $round_max) { + foreach ($groupedData as $val) { + if (count($val) > 1) { + $differentData = array_merge($differentData, $this->getOtherRanking($val, $round)); + } else { + $differentData = array_merge($differentData, $val); + } + } + } else { + $differentData = array_merge($differentData, $groupedData); + } + return $differentData; + + } + + public function getFinals($param) + { + $query = $this->matchRankingModel->where('match_id', $param['match_id'])->where("course", 1); + if (!empty($param['round'])) { + + } + if ($param['other_round'] == MatchRanking::$totalScore) { // 总积分 + $query + ->group("player_id") + ->field("player_id, match_id, player_name, led_color, channel, sum(integral) as sum_integral, grouping"); + + if ($param['sorting'] == "true") { + $query->order("sum_integral", "DESC"); + } + } else if ($param['other_round'] == MatchRanking::$extraRanking) { // 加赛 + $query->where("other_round", "加赛") + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement, group_score, custom_sorting, integral"); + if ($param['sorting'] == "true") { + $query->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END")->order('datetime_value', 'asc'); + } + } else { // 分组 + $query->where("other_round", $param['other_round']) + ->field("player_id, player_name, fly_num, times, grouping, course, led_color, channel, id, match_id,achievement, group_score, custom_sorting, integral"); + if ($param['sorting'] == "true") { + $query->order('integral', 'DESC'); + } + } + + $data['item'] = $query->select(); + + $data['round'] = $this->matchRankingModel->where("match_id", "eq", $param['match_id']) + ->where("course", "eq", 1) + ->where("other_round", "neq", "加赛") + ->max("other_round"); + + return $data; + } + + /** + * 计时换算成毫秒 + * @Created by PhpStorm. + * @Author:Soar + * @Time:2023/11/15 15:11 + * @param $times + * @return float|int|mixed|string + */ + private function getAllMs($times) + { + $minute_ms = 0; + $second_ms = 0; + $ms = 0; + // 截取最后的毫秒 + $allTime = explode(".", $times); + if (count($allTime) != 2) { + $this->error("成绩格式有误!"); + } + $ms = $allTime[1]; + // 截取分秒 + $minute = explode(":", $allTime[0]); + if (count($minute) != 2) { + $this->error("成绩格式有误!"); + } + if ($minute[0] != 00) { + // 如果第一位不是 00 则代表每分钟 + $minute_ms = $minute[0] * 60 * 100; + } + + if ($minute[1] != 00) { + $second_ms = $minute[1] * 100; + } + + return $ms + $second_ms + $minute_ms; + } + + /** + * 调整数组排序 + * @Author:Soar + * @Time:2023/12/18 9:38 + * @param $arr + * @param $fromKey + * @param $toKey + * @return void + */ + private function adjustArrayPosition(&$arr, $fromKey, $toKey) + { + $element = $arr[$fromKey]; // 保存要调整的元素 + + if ($fromKey < $toKey) { + // 从前往后调整位置 + for ($i = $fromKey; $i < $toKey; $i++) { + $arr[$i] = $arr[$i + 1]; + } + } elseif ($fromKey > $toKey) { + // 从后往前调整位置 + for ($i = $fromKey; $i > $toKey; $i--) { + $arr[$i] = $arr[$i - 1]; + } + } + + $arr[$toKey] = $element; // 将元素放到目标位置 + } +} \ No newline at end of file diff --git a/application/admin/service/ScheduleService.php b/application/admin/service/ScheduleService.php new file mode 100644 index 0000000..644ec26 --- /dev/null +++ b/application/admin/service/ScheduleService.php @@ -0,0 +1,88 @@ +matchScheduleModel = new MatchSchedule(); + } + + public function getOne($scheduleId) + { + return $this->matchScheduleModel->get($scheduleId); + } + + public function getByWhere($param) + { + return $this->matchScheduleModel->all($param); + } + + public function addSchedule($add_data) + { + $data['match_id'] = $add_data['match_id']; + $data['title'] = $add_data['title']; + $data['content'] = $add_data['content']; + $data['schedule_date'] = $add_data['date']; + $data['weigh'] = $add_data['weigh']; + + Db::startTrans(); + + try { + if ($this->matchScheduleModel->insertGetId($data)) { + Db::commit(); + } + return true; + } catch (Exception $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } + + public function editSchedule($edit_data) + { + $data['match_id'] = $edit_data['match_id']; + $data['title'] = $edit_data['title']; + $data['content'] = $edit_data['content']; + $data['schedule_date'] = $edit_data['date']; + $data['weigh'] = $edit_data['weigh']; + $id = $edit_data['id']; + + Db::startTrans(); + + try { + if ($this->matchScheduleModel->update($data, ['id' => $id])) { + Db::commit(); + } + return true; + } catch (Exception $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } + + public function delSchedule($scheduleId) + { + Db::startTrans(); + try { + if ($this->matchScheduleModel->where('id', $scheduleId)->delete()) { + Db::commit(); + } + return true; + } catch (Exception $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } + +} \ No newline at end of file diff --git a/application/admin/service/ScreenService.php b/application/admin/service/ScreenService.php new file mode 100644 index 0000000..275ca1f --- /dev/null +++ b/application/admin/service/ScreenService.php @@ -0,0 +1,60 @@ +matchScreenModel = new MatchScreen(); + } + + public function addScreen($param) + { + $param['look_pwd'] = md5($param['look_pwd']); + + $screen_info = $this->matchScreenModel->where("match_id", "eq", $param['match_id']) + ->where("type", $param['type']) + ->find(); + if (!empty($screen_info)) { + $param['id'] = $screen_info->id; + + if ($param['nopass'] == "false") { + $param['look_pwd'] = null; + } + + unset($param['nopass']); + try { + return $this->matchScreenModel->update($param); + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + + } else { + unset($param['nopass']); + try { + return $this->matchScreenModel->insert($param); + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } + + } + + public function getScreen($param) + { + try { + return $this->matchScreenModel->where($param)->field("title, id, backound_img, font_rgb")->find(); + } catch (\Exception $exception) { + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/application/admin/service/Service.php b/application/admin/service/Service.php new file mode 100644 index 0000000..10ef788 --- /dev/null +++ b/application/admin/service/Service.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +// 应用行为扩展定义文件 +return [ + // 应用结束 + 'app_end' => [ + 'app\\admin\\behavior\\AdminLog', + ], +]; diff --git a/application/admin/validate/Admin.php b/application/admin/validate/Admin.php new file mode 100644 index 0000000..92a8d36 --- /dev/null +++ b/application/admin/validate/Admin.php @@ -0,0 +1,57 @@ + 'require|regex:\w{3,30}|unique:admin', + 'nickname' => 'require', + 'password' => 'require|regex:\S{32}', + 'email' => 'require|email|unique:admin,email', + 'mobile' => 'regex:1[3-9]\d{9}|unique:admin,mobile', + ]; + + /** + * 提示消息 + */ + protected $message = [ + ]; + + /** + * 字段描述 + */ + protected $field = [ + ]; + + /** + * 验证场景 + */ + protected $scene = [ + 'add' => ['username', 'email', 'nickname', 'password', 'mobile'], + 'edit' => ['username', 'email', 'nickname', 'password', 'mobile'], + ]; + + public function __construct(array $rules = [], $message = [], $field = []) + { + $this->field = [ + 'username' => __('Username'), + 'nickname' => __('Nickname'), + 'password' => __('Password'), + 'email' => __('Email'), + 'mobile' => __('Mobile'), + ]; + $this->message = array_merge($this->message, [ + 'username.regex' => __('Please input correct username'), + 'password.regex' => __('Please input correct password') + ]); + parent::__construct($rules, $message, $field); + } + +} diff --git a/application/admin/validate/AuthRule.php b/application/admin/validate/AuthRule.php new file mode 100644 index 0000000..3919ea0 --- /dev/null +++ b/application/admin/validate/AuthRule.php @@ -0,0 +1,52 @@ + '[a-z0-9_\/]+']; + + /** + * 验证规则 + */ + protected $rule = [ + 'name' => 'require|unique:AuthRule', + 'title' => 'require', + ]; + + /** + * 提示消息 + */ + protected $message = [ + 'name.format' => 'URL规则只能是小写字母、数字、下划线和/组成' + ]; + + /** + * 字段描述 + */ + protected $field = [ + ]; + + /** + * 验证场景 + */ + protected $scene = [ + ]; + + public function __construct(array $rules = [], $message = [], $field = []) + { + $this->field = [ + 'name' => __('Name'), + 'title' => __('Title'), + ]; + $this->message['name.format'] = __('Name only supports letters, numbers, underscore and slash'); + parent::__construct($rules, $message, $field); + } + +} diff --git a/application/admin/validate/Category.php b/application/admin/validate/Category.php new file mode 100644 index 0000000..8b67fdc --- /dev/null +++ b/application/admin/validate/Category.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/Command.php b/application/admin/validate/Command.php new file mode 100644 index 0000000..9da8fa6 --- /dev/null +++ b/application/admin/validate/Command.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/Countryflag.php b/application/admin/validate/Countryflag.php new file mode 100644 index 0000000..730d4a8 --- /dev/null +++ b/application/admin/validate/Countryflag.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/Renzheng.php b/application/admin/validate/Renzheng.php new file mode 100644 index 0000000..3072d3c --- /dev/null +++ b/application/admin/validate/Renzheng.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/Renzheng/Php.php b/application/admin/validate/Renzheng/Php.php new file mode 100644 index 0000000..85a6312 --- /dev/null +++ b/application/admin/validate/Renzheng/Php.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/Third.php b/application/admin/validate/Third.php new file mode 100644 index 0000000..f9766b4 --- /dev/null +++ b/application/admin/validate/Third.php @@ -0,0 +1,26 @@ + [], + 'edit' => [], + ]; +} diff --git a/application/admin/validate/User.php b/application/admin/validate/User.php new file mode 100644 index 0000000..7d7e0f5 --- /dev/null +++ b/application/admin/validate/User.php @@ -0,0 +1,50 @@ + 'require|regex:\w{3,30}|unique:user', + 'nickname' => 'require|unique:user', + 'password' => 'regex:\S{6,30}', + 'email' => 'require|email|unique:user', + 'mobile' => 'unique:user' + ]; + + /** + * 字段描述 + */ + protected $field = [ + ]; + /** + * 提示消息 + */ + protected $message = [ + ]; + /** + * 验证场景 + */ + protected $scene = [ + 'add' => [], + 'edit' => ['username', 'nickname', 'password', 'email', 'mobile'], + ]; + + public function __construct(array $rules = [], $message = [], $field = []) + { + $this->field = [ + 'username' => __('Username'), + 'nickname' => __('Nickname'), + 'password' => __('Password'), + 'email' => __('Email'), + 'mobile' => __('Mobile') + ]; + parent::__construct($rules, $message, $field); + } + +} diff --git a/application/admin/validate/UserGroup.php b/application/admin/validate/UserGroup.php new file mode 100644 index 0000000..b7fa1ee --- /dev/null +++ b/application/admin/validate/UserGroup.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/UserRule.php b/application/admin/validate/UserRule.php new file mode 100644 index 0000000..2a2475b --- /dev/null +++ b/application/admin/validate/UserRule.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/cms/Archives.php b/application/admin/validate/cms/Archives.php new file mode 100644 index 0000000..513b6fd --- /dev/null +++ b/application/admin/validate/cms/Archives.php @@ -0,0 +1,26 @@ + [], + 'edit' => [], + ]; +} diff --git a/application/admin/validate/cms/Autolink.php b/application/admin/validate/cms/Autolink.php new file mode 100644 index 0000000..6308004 --- /dev/null +++ b/application/admin/validate/cms/Autolink.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/cms/Block.php b/application/admin/validate/cms/Block.php new file mode 100644 index 0000000..9157bf3 --- /dev/null +++ b/application/admin/validate/cms/Block.php @@ -0,0 +1,26 @@ + [], + 'edit' => [], + ]; +} diff --git a/application/admin/validate/cms/Channel.php b/application/admin/validate/cms/Channel.php new file mode 100644 index 0000000..26081f2 --- /dev/null +++ b/application/admin/validate/cms/Channel.php @@ -0,0 +1,26 @@ + [], + 'edit' => [], + ]; +} diff --git a/application/admin/validate/cms/Comment.php b/application/admin/validate/cms/Comment.php new file mode 100644 index 0000000..3997fcb --- /dev/null +++ b/application/admin/validate/cms/Comment.php @@ -0,0 +1,26 @@ + [], + 'edit' => [], + ]; +} diff --git a/application/admin/validate/cms/Fields.php b/application/admin/validate/cms/Fields.php new file mode 100644 index 0000000..eebbca7 --- /dev/null +++ b/application/admin/validate/cms/Fields.php @@ -0,0 +1,58 @@ + 'require|unique:cms_fields,source^source_id^name', + 'title|管理员' => 'require', + 'source|来源' => 'require', + 'source_id|来源ID' => 'require|integer', + 'status|状态' => 'require|in:normal,hidden', + ]; + + /** + * 提示消息 + */ + protected $message = [ + ]; + + /** + * 验证场景 + */ + protected $scene = [ + 'add' => [ + 'name', + 'title', + 'source', + 'source_id', + 'status' + ], + 'edit' => [ + 'name', + 'title', + 'source', + 'source_id', + 'status' + ], + ]; + + public function __construct(array $rules = array(), $message = array(), $field = array()) + { + //如果是编辑模式,则排除下主键 + $ids = request()->param("ids"); + if ($ids) { + $this->rule['name|名称'] = "require|unique:cms_fields,source^source_id^name,{$ids},id"; + } else { + $this->rule['name|名称'] = "require|unique:cms_fields,source^source_id^name"; + } + parent::__construct($rules, $message, $field); + } +} diff --git a/application/admin/validate/cms/Modelx.php b/application/admin/validate/cms/Modelx.php new file mode 100644 index 0000000..f566bed --- /dev/null +++ b/application/admin/validate/cms/Modelx.php @@ -0,0 +1,26 @@ + [], + 'edit' => [], + ]; +} diff --git a/application/admin/validate/cms/Order.php b/application/admin/validate/cms/Order.php new file mode 100644 index 0000000..97c4fd8 --- /dev/null +++ b/application/admin/validate/cms/Order.php @@ -0,0 +1,26 @@ + [], + 'edit' => [], + ]; +} diff --git a/application/admin/validate/cms/Page.php b/application/admin/validate/cms/Page.php new file mode 100644 index 0000000..bd447f7 --- /dev/null +++ b/application/admin/validate/cms/Page.php @@ -0,0 +1,26 @@ + [], + 'edit' => [], + ]; +} diff --git a/application/admin/validate/cms/SearchLog.php b/application/admin/validate/cms/SearchLog.php new file mode 100644 index 0000000..9fe0386 --- /dev/null +++ b/application/admin/validate/cms/SearchLog.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/cms/Special.php b/application/admin/validate/cms/Special.php new file mode 100644 index 0000000..6607956 --- /dev/null +++ b/application/admin/validate/cms/Special.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/cms/SpiderLog.php b/application/admin/validate/cms/SpiderLog.php new file mode 100644 index 0000000..77bd6d6 --- /dev/null +++ b/application/admin/validate/cms/SpiderLog.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/cms/Tag.php b/application/admin/validate/cms/Tag.php new file mode 100644 index 0000000..9d27a27 --- /dev/null +++ b/application/admin/validate/cms/Tag.php @@ -0,0 +1,26 @@ + [], + 'edit' => [], + ]; +} diff --git a/application/admin/validate/fastim/CsrGroup.php b/application/admin/validate/fastim/CsrGroup.php new file mode 100644 index 0000000..8f8b78f --- /dev/null +++ b/application/admin/validate/fastim/CsrGroup.php @@ -0,0 +1,25 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/fastim/FastReply.php b/application/admin/validate/fastim/FastReply.php new file mode 100644 index 0000000..ac20676 --- /dev/null +++ b/application/admin/validate/fastim/FastReply.php @@ -0,0 +1,25 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/fastim/Groupchat.php b/application/admin/validate/fastim/Groupchat.php new file mode 100644 index 0000000..7b885a6 --- /dev/null +++ b/application/admin/validate/fastim/Groupchat.php @@ -0,0 +1,25 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/fastim/Kbs.php b/application/admin/validate/fastim/Kbs.php new file mode 100644 index 0000000..518ff6c --- /dev/null +++ b/application/admin/validate/fastim/Kbs.php @@ -0,0 +1,25 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/fastim/Records.php b/application/admin/validate/fastim/Records.php new file mode 100644 index 0000000..784d8b9 --- /dev/null +++ b/application/admin/validate/fastim/Records.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/fastim/Report.php b/application/admin/validate/fastim/Report.php new file mode 100644 index 0000000..34e53f4 --- /dev/null +++ b/application/admin/validate/fastim/Report.php @@ -0,0 +1,25 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/fastim/Session.php b/application/admin/validate/fastim/Session.php new file mode 100644 index 0000000..4fcae11 --- /dev/null +++ b/application/admin/validate/fastim/Session.php @@ -0,0 +1,25 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/fastim/User.php b/application/admin/validate/fastim/User.php new file mode 100644 index 0000000..aebfcbb --- /dev/null +++ b/application/admin/validate/fastim/User.php @@ -0,0 +1,25 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/shopro/Category.php b/application/admin/validate/shopro/Category.php new file mode 100644 index 0000000..9a53afc --- /dev/null +++ b/application/admin/validate/shopro/Category.php @@ -0,0 +1,31 @@ + 'require', + 'style' => 'require', + ]; + /** + * 提示消息 + */ + protected $message = [ + 'name.require' => '请填写自定义分类名称', + 'style.require' => '请选择分类样式', + ]; + /** + * 验证场景 + */ + protected $scene = [ + 'add' => ['name', 'style'], + 'edit' => ['name', 'style'], + ]; + +} diff --git a/application/admin/validate/shopro/Coupon.php b/application/admin/validate/shopro/Coupon.php new file mode 100644 index 0000000..399b6f3 --- /dev/null +++ b/application/admin/validate/shopro/Coupon.php @@ -0,0 +1,44 @@ + 'require', + 'type' => 'require', + 'use_scope' => 'require', + 'items' => 'requireIfAll:use_scope,goods,disabled_goods,category', // 当 use_scope 在 goods,disabled_goods,category 时,items 必填 + 'amount' => 'require', + 'enough' => 'require', + 'stock' => 'require', + 'get_time' => 'require', + 'use_time_type' => 'require', + 'use_time' => 'requireIf:use_time_type,range', // 固定区间时必填 + 'days' => 'requireIf:use_time_type,days', // 相对天数时必填 + ]; + + protected $message = [ + 'name.require' => '请填写优惠券名称', + 'type.require' => '请选择优惠券类型', + 'use_scope.require' => '请选择可用范围', + 'items.requireIfAll' => '请选择可用范围值', + 'amount.require' => '请填写优惠券面额', + 'enough.require' => '请填写优惠券消费门槛', + 'stock.require' => '请填写优惠券发放数量', + 'get_time.require' => '请选择优惠券发放时间', + 'use_time_type.require' => '请选择优惠券使用时间类型', + 'use_time.requireIf' => '请选择优惠券可使用时间', + 'days.requireIf' => '请填写优惠券有效天数', + ]; + + + protected $scene = [ + 'add' => ['name', 'type', 'use_scope', 'items', 'amount', 'enough', 'stock', 'get_time', 'use_time_type', 'use_time', 'days'], + ]; +} diff --git a/application/admin/validate/shopro/PayConfig.php b/application/admin/validate/shopro/PayConfig.php new file mode 100644 index 0000000..909e8b1 --- /dev/null +++ b/application/admin/validate/shopro/PayConfig.php @@ -0,0 +1,71 @@ + 'require', + 'type' => 'require', + 'params' => 'require|array', + + + 'mode' => 'require', // 模式必选 + + 'mch_id' => 'require', + 'mch_secret_key' => 'require', + 'mch_secret_cert' => 'require', + 'mch_public_cert_path' => 'require', + 'sub_mch_id' => 'requireIf:mode,2', + 'app_id' => 'requireIf:mode,2', // 这个支付宝也用这个 app_id 参数名,这里有点问题,支付宝普通模式如果不填这里验证拦不住 + 'sub_mch_secret_key' => 'requireIf:mode,2', + 'sub_mch_secret_cert' => 'requireIf:mode,2', + 'sub_mch_public_cert_path' => 'requireIf:mode,2', + + // 支付宝参数校验 + 'service_provider_id' => 'requireIf:mode,service', + 'alipay_public_cert_path' => 'require', + 'app_public_cert_path' => 'require', + 'alipay_root_cert_path' => 'require', + 'private_key' => 'require', + ]; + + protected $message = [ + 'name.require' => '请填写支付配置名称', + 'type.require' => '请选择支付配置类型', + 'params.require' => '请填写正确的支付参数', + 'params.array' => '请填写正确的支付参数', + + // 微信支付参数校验 + 'mode.require' => '请选择商户类型', + 'app_id.requireIf' => '请填写商户相关 AppId', // 这个支付宝也用这个 app_id 参数名 + + 'mch_id.require' => '请填写商户 ID', + 'mch_secret_key.require' => '请填写商户密钥', + 'mch_secret_cert.require' => '请上传商户 key 证书', + 'mch_public_cert_path.require' => '请上传商户证书', + 'sub_mch_id.requireIf' => '请填写子商户 ID', + 'sub_mch_secret_key.requireIf' => '请填写子商户密钥', + 'sub_mch_secret_cert.requireIf' => '请上传子商户 key 证书', + 'sub_mch_public_cert_path.requireIf' => '请上传子商户证书', + + + // 支付宝参数校验 + 'service_provider_id.requireIf' => '请填写主商户 ID', + 'alipay_public_cert_path.require' => '请上传支付宝公钥证书', + 'app_public_cert_path.require' => '请上传应用公钥证书', + 'alipay_root_cert_path.require' => '请上传支付宝根证书', + 'app_secret_cert.require' => '请填写支付宝私钥', + ]; + + + protected $scene = [ + 'add' => ['name', 'type', 'params'], + + 'wechat' => ['mode', 'mch_id', 'mch_secret_key', 'mch_secret_cert', 'mch_public_cert_path', 'app_id', 'sub_mch_id'], + + 'alipay' => ['mode', 'service_provider_id', 'app_id', 'alipay_public_cert_path', 'app_public_cert_path', 'alipay_root_cert_path', 'app_secret_cert'], + ]; +} diff --git a/application/admin/validate/shopro/Vip.php b/application/admin/validate/shopro/Vip.php new file mode 100644 index 0000000..209bf39 --- /dev/null +++ b/application/admin/validate/shopro/Vip.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/shopro/activity/Activity.php b/application/admin/validate/shopro/activity/Activity.php new file mode 100644 index 0000000..06f40b4 --- /dev/null +++ b/application/admin/validate/shopro/activity/Activity.php @@ -0,0 +1,30 @@ + 'require', + 'type' => 'require', + 'start_time' => 'require', + 'end_time' => 'require', + 'rules' => 'require|array', + ]; + + protected $message = [ + 'title.require' => '请填写活动标题', + 'type.require' => '活动类型不正确', + 'start_time.require' => '请选择活动开始时间', + 'end_time.require' => '请选择活动结束时间', + 'rules.require' => '缺少活动规则', + 'rules.array' => '活动规则不正确', + ]; + + + protected $scene = [ + 'add' => ['title', 'type', 'start_time', 'end_time', 'rules'], + ]; +} diff --git a/application/admin/validate/shopro/app/ScoreShop.php b/application/admin/validate/shopro/app/ScoreShop.php new file mode 100644 index 0000000..32bb744 --- /dev/null +++ b/application/admin/validate/shopro/app/ScoreShop.php @@ -0,0 +1,27 @@ + 'require', + 'sku_prices' => 'require|array', + + + ]; + + protected $message = [ + 'goods_id.require' => '请选择要添加的商品', + 'sku_prices.require' => '请填写积分商品规格', + 'sku_prices.array' => '请填写积分商品规格', + ]; + + + protected $scene = [ + 'add' => ['goods_id', 'sku_prices'], + 'edit' => ['sku_prices'] + ]; +} diff --git a/application/admin/validate/shopro/chat/CommonWord.php b/application/admin/validate/shopro/chat/CommonWord.php new file mode 100644 index 0000000..4123c1c --- /dev/null +++ b/application/admin/validate/shopro/chat/CommonWord.php @@ -0,0 +1,25 @@ + 'require', + 'name' => 'require', + 'content' => 'require', + ]; + + protected $message = [ + 'room_id.require' => '客服房间号必须填写', + 'name.require' => '常用语名称必须填写', + 'content.require' => '常用于内容必须填写', + ]; + + + protected $scene = [ + 'add' => ['room_id', 'name', 'content'] + ]; +} diff --git a/application/admin/validate/shopro/chat/CustomerService.php b/application/admin/validate/shopro/chat/CustomerService.php new file mode 100644 index 0000000..0fb0199 --- /dev/null +++ b/application/admin/validate/shopro/chat/CustomerService.php @@ -0,0 +1,29 @@ + 'require', + 'avatar' => 'require', + 'room_id' => 'require', + 'auth' => 'require', + 'auth_id' => 'require', + ]; + + protected $message = [ + 'name.require' => '客服名称必须填写', + 'avatar.require' => '客服头像必须上传', + 'room_id.require' => '客服房间号必须填写', + 'auth.require' => '客服所属身份必须选择', + 'auth_id.require' => '客服所属身份必须选择', + ]; + + + protected $scene = [ + 'add' => ['name', 'avatar', 'room_id', 'auth', 'auth_id'] + ]; +} diff --git a/application/admin/validate/shopro/chat/Question.php b/application/admin/validate/shopro/chat/Question.php new file mode 100644 index 0000000..36ea25b --- /dev/null +++ b/application/admin/validate/shopro/chat/Question.php @@ -0,0 +1,25 @@ + 'require', + 'title' => 'require', + 'content' => 'require', + ]; + + protected $message = [ + 'room_id.require' => '客服房间号必须填写', + 'title.require' => '猜你想问标题必须填写', + 'content.require' => '猜你想问内容必须填写', + ]; + + + protected $scene = [ + 'add' => ['room_id', 'title', 'content'] + ]; +} diff --git a/application/admin/validate/shopro/data/FakeUser.php b/application/admin/validate/shopro/data/FakeUser.php new file mode 100644 index 0000000..5d68720 --- /dev/null +++ b/application/admin/validate/shopro/data/FakeUser.php @@ -0,0 +1,36 @@ + 'require|alphaDash|length:5,20', + 'nickname' => 'require|chsDash|length:2,15', + 'mobile' => 'regex:/^1[3-9]\d{9}$/', + 'avatar' => 'require', + 'email' => 'email', + 'password' => 'length:6,16', + ]; + + protected $message = [ + 'username.require' => '用户名必须填写', + 'username.alphaDash' => '用户名只能是字母和数字,下划线_及破折号-', + 'username.length' => '用户名长度必须在 5-20 位', + 'nickname.require' => '昵称必须填写', + 'nickname.chsDash' => '昵称名称只能是汉字、字母、数字和下划线_及破折号-', + 'nickname.length' => '昵称长度必须在 2-15 位', + 'mobile.regex' => '手机号格式不正确', + 'avatar.require' => '头像必须上传', + 'email.email' => '邮箱格式不正确', + 'password.length' => '密码长度必须在 6-16 位', + ]; + + + protected $scene = [ + 'add' => ['username', 'nickname', 'mobile', 'password', 'avatar', 'email'], + 'edit' => ['username', 'nickname', 'mobile', 'password', 'avatar', 'email'], + ]; +} diff --git a/application/admin/validate/shopro/data/Faq.php b/application/admin/validate/shopro/data/Faq.php new file mode 100644 index 0000000..3b7cced --- /dev/null +++ b/application/admin/validate/shopro/data/Faq.php @@ -0,0 +1,33 @@ + 'require', + 'content' => 'require', + 'status' => 'require', + ]; + /** + * 提示消息 + */ + protected $message = [ + 'title.require' => '请填写 Faq 标题', + 'content.require' => '请填写 Faq 内容', + 'status.require' => '请选择 Faq 状态', + ]; + /** + * 验证场景 + */ + protected $scene = [ + 'add' => ['title', 'content', 'status'], + 'edit' => ['title', 'content', 'status'], + ]; + +} diff --git a/application/admin/validate/shopro/dispatch/Dispatch.php b/application/admin/validate/shopro/dispatch/Dispatch.php new file mode 100644 index 0000000..92dd510 --- /dev/null +++ b/application/admin/validate/shopro/dispatch/Dispatch.php @@ -0,0 +1,41 @@ + 'require', + 'express' => 'array', + 'autosend' => 'array', + + // express 表数据 + 'type' => 'require', + 'first_num' => 'require', + 'first_price' => 'require', + 'additional_num' => 'require', + 'additional_price' => 'require', + ]; + + protected $message = [ + 'name.require' => '请填写自定义分类名称', + 'express.array' => '请填写正确的模板规则', + 'autosend.array' => '请填写正确的自动发货规则', + + 'type.require' => '请选择计价方式', + 'first_num.require' => '请填写初始计价数量', + 'first_price.require' => '请填写初始配送价格', + 'additional_num.require' => '请填写追加计价数量', + 'additional_price.require' => '请填写追加计价价格', + ]; + + + protected $scene = [ + 'add' => ['name', 'express'], + 'express' => ['type', 'first_num', 'first_price', 'additional_num', 'additional_price'], + + 'autosend' => ['type', 'content'] + ]; +} diff --git a/application/admin/validate/shopro/goods/Comment.php b/application/admin/validate/shopro/goods/Comment.php new file mode 100644 index 0000000..7fe4370 --- /dev/null +++ b/application/admin/validate/shopro/goods/Comment.php @@ -0,0 +1,30 @@ + 'require', + 'user_id' => 'require', + 'level' => 'require|number|between:1,5', + 'content' => 'require', + ]; + + protected $message = [ + 'goods_id.require' => '请选择商品', + 'user_id.require' => '请选择用户', + 'level.require' => '请选择评价星级', + 'level.number' => '请选择评价星级', + 'level.between' => '请选择评价星级', + 'content.require' => '请填写内容', + ]; + + + protected $scene = [ + 'add' => ['goods_id', 'user_id', 'level', 'content'], + 'reply' => ['content'], + ]; +} diff --git a/application/admin/validate/shopro/goods/Goods.php b/application/admin/validate/shopro/goods/Goods.php new file mode 100644 index 0000000..fd59c7c --- /dev/null +++ b/application/admin/validate/shopro/goods/Goods.php @@ -0,0 +1,45 @@ + 'require', + // 'subtitle' => 'require', + 'category_ids' => 'require', + 'image' => 'require', + 'images' => 'require|array', + 'is_sku' => 'require', + // 'cost_price' => 'require', + // 'original_price' => 'requireIf:is_sku,0', + 'price' => 'requireIf:is_sku,0', + 'dispatch_id' => 'requireIfAll:dispatch_type,express,autosend', + + + ]; + + protected $message = [ + 'title.require' => '请填写商品标题', + // 'subtitle.require' => '请填写商品副标题', + 'category_ids.require' => '请选择商品分类', + 'image.require' => '请选择商品封面图', + 'images.require' => '请选择商品轮播图', + 'images.array' => '请选择商品轮播图', + // 'cost_price.require' => '请填写商品成本价', + // 'original_price.require' => '请填写商品原价', + 'price.requireIf' => '请填写商品现价', + 'dispatch_id.requireIfAll' => '请选择配送模板', + ]; + + + protected $scene = [ + 'add' => ['title', 'image', 'images', 'price', 'dispatch_id'], + 'sku_params' => ['price'] + ]; +} diff --git a/application/admin/validate/shopro/goods/Service.php b/application/admin/validate/shopro/goods/Service.php new file mode 100644 index 0000000..c5e0fd0 --- /dev/null +++ b/application/admin/validate/shopro/goods/Service.php @@ -0,0 +1,21 @@ + 'require', + ]; + + protected $message = [ + 'name.require' => '请填写服务保障名称', + ]; + + + protected $scene = [ + 'add' => ['name'] + ]; +} diff --git a/application/admin/validate/shopro/goods/StockWarning.php b/application/admin/validate/shopro/goods/StockWarning.php new file mode 100644 index 0000000..d03a2cf --- /dev/null +++ b/application/admin/validate/shopro/goods/StockWarning.php @@ -0,0 +1,23 @@ + 'require|integer|gt:0', + ]; + + protected $message = [ + 'stock.require' => '请填写补货数量', + 'stock.integer' => '请填写补货数量', + 'stock.gt' => '请填写正确的补货数量' + ]; + + + protected $scene = [ + 'add_stock' => ['stock'] + ]; +} diff --git a/application/admin/validate/shopro/order/Aftersale.php b/application/admin/validate/shopro/order/Aftersale.php new file mode 100644 index 0000000..f9cba38 --- /dev/null +++ b/application/admin/validate/shopro/order/Aftersale.php @@ -0,0 +1,29 @@ + 'require', + 'refund_money' => 'require|float|gt:0', + 'content' => 'require' + ]; + + protected $message = [ + 'refuse_msg.require' => '请输入拒绝原因', + 'refund_money.require' => '请输入正确的退款金额', + 'refund_money.float' => '请输入正确的退款金额', + 'refund_money.gt' => '请输入正确的退款金额', + 'content.require' => '请输入留言内容', + ]; + + + protected $scene = [ + 'refuse' => ['refuse_msg'], + 'refund' => ['refund_money'], + 'add_log' => ['content'], + ]; +} diff --git a/application/admin/validate/shopro/order/Order.php b/application/admin/validate/shopro/order/Order.php new file mode 100644 index 0000000..c0ffeaf --- /dev/null +++ b/application/admin/validate/shopro/order/Order.php @@ -0,0 +1,75 @@ + 'require', + 'order_item_ids' => 'require', + 'custom_type' => 'require', + 'custom_content' => 'require', + + 'pay_fee' => 'require|float|gt:0', + 'change_msg' => 'require', + + // 编辑订单收货地址 + 'consignee' => 'require', + 'mobile' => 'require', + 'province_name' => 'require', + 'city_name' => 'require', + 'district_name' => 'require', + 'address' => 'require', + 'province_id' => 'require', + 'city_id' => 'require', + 'district_id' => 'require', + + // 编辑卖家备注 + 'memo' => 'require', + + // 订单退款 + 'refund_money' => 'require|float|gt:0' + ]; + + protected $message = [ + 'order_id' => '参数错误', + 'order_item_ids' => '参数错误', + 'custom_type' => '请选择发货内容类型', + 'custom_content' => '请填写发货内容', + + 'pay_fee.require' => '请输入正确的应支付金额', + 'pay_fee.float' => '请输入正确的应支付金额', + 'pay_fee.gt' => '请输入正确的应支付金额', + 'change_msg.require' => '请输入改价备注', + + // 编辑订单收货地址 + 'consignee.require' => '请填写收货人信息', + 'mobile.require' => '请填写手机号', + 'province_name.require' => '请选择省份', + 'city_name.require' => '请选择城市', + 'district_name.require' => '请选择地区', + 'address.require' => '请填写详细收货信息', + 'province_id.require' => '请选择省份', + 'city_id.require' => '请选择城市', + 'district_id.require' => '请选择地区', + + // 编辑卖家备注 + 'memo.require' => '请输入卖家备注', + + // 订单退款 + 'refund_money.require' => '请输入正确的退款金额', + 'refund_money.float' => '请输入正确的退款金额', + 'refund_money.gt' => '请输入正确的退款金额', + ]; + + + protected $scene = [ + 'custom_dispatch' => ['order_id', 'order_item_ids', 'custom_type', 'custom_content'], + 'change_fee' => ['pay_fee', 'change_msg'], + 'edit_consignee' => ['consignee', 'mobile', 'province_name', 'city_name', 'district_name', 'address', 'province_id', 'city_id', 'district_id'], + 'edit_memo' => ['memo'], + 'refund' => ['refund_money'], + ]; +} diff --git a/application/admin/validate/shopro/traits/CustomRule.php b/application/admin/validate/shopro/traits/CustomRule.php new file mode 100644 index 0000000..7f816ea --- /dev/null +++ b/application/admin/validate/shopro/traits/CustomRule.php @@ -0,0 +1,31 @@ + '/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]+\S{5,12}$/', + 'notPureNumber' => '^[a-zA-Z][a-zA-Z0-9_]{4,15}$', + 'mobile' => '/^1[3456789]\d{9}$/', + ]; + + protected $rule = [ + 'username' => 'alphaDash|length:5,12|unique:user|regex:notPureNumber', + 'nickname' => 'require|length:1,10', + 'mobile' => 'regex:mobile', + 'password' => 'length:6,16|regex:password', + 'oldPassword' => 'require', + 'newPassword' => 'require|length:6,16|regex:password', + 'avatar' => 'require', + 'email' => 'email|unique:user', + 'code' => 'require', + 'gender' => 'in:0,1' + ]; + + protected $message = [ + 'username.require' => '用户名必须填写', + 'username.alphaDash' => '用户名只能包含字母,数字,_和-', + 'username.length' => '用户名长度必须在 5-12 位', + 'username.unique' => '用户名已被占用', + 'username.regx' => '用户名需以字母开头', + + 'nickname.require' => '昵称必须填写', + 'nickname.chsDash' => '昵称只能包含汉字,字母,数字,_和-', + 'nickname.length' => '昵称长度必须在 2-10 位', + + 'mobile.require' => '手机号必须填写', + 'mobile.regex' => '手机号格式不正确', + 'mobile.unique' => '手机号已被占用', + + 'password.require' => '请填写密码', + 'password.length' => '密码长度必须在 6-16 位', + 'password.regx' => '密码必须包含字母和数字', + + 'oldPassword.require' => '请填写旧密码', + + 'newPassword.require' => '请填写新密码', + 'newPassword.length' => '密码长度必须在 6-16 位', + 'newPassword.regx' => '密码必须包含字母和数字', + + 'avatar.require' => '头像必须上传', + + 'email.email' => '邮箱格式不正确', + 'email.unique' => '邮箱已被占用', + + 'code.require' => '请填写验证码', + + 'gender.in' => '请选择性别' + ]; + + + protected $scene = [ + 'edit' => ['username', 'nickname', 'mobile', 'password', 'avatar', 'gender', 'email', 'status'] + ]; + + /** + * 验证是否唯一 + * @access public + * @param mixed $value 字段值 + * @param mixed $rule 验证规则 格式:数据表,字段名,排除ID,主键名 + * @param array $data 数据 + * @param string $field 验证字段名 + * @return bool + */ + public function unique($value, $rule, $data = [], $field = '') + { + if (is_string($rule)) { + $rule = explode(',', $rule); + } + + if (false !== strpos($rule[0], '\\')) { + // 指定模型类 + $db = new $rule[0]; + } else { + try { + $db = Loader::model($rule[0]); + } catch (ClassNotFoundException $e) { + $db = Db::name($rule[0]); + } + } + + $key = $rule[1] ?? $field; + $map = []; + + if (strpos($key, '^')) { + // 支持多个字段验证 + $fields = explode('^', $key); + foreach ($fields as $key) { + if (isset($data[$key])) { + $map[$key] = ['=', $data[$key]]; + } + } + } elseif (isset($data[$field])) { + $map[$key] = ['=', $data[$field]]; + } else { + $map = []; + } + + $pk = !empty($rule[3]) ? $rule[3] : $db->getPk(); + + if (is_string($pk)) { + if (isset($rule[2])) { + $map[$pk] = ['neq', $rule[2]]; + } elseif (isset($data[$pk])) { + $map[$pk] = ['neq', $data[$pk]]; + } + } + + if ($db->where($map)->field($pk)->find()) { + return false; + } + + return true; + } +} diff --git a/application/admin/validate/shopro/vip/Paylog.php b/application/admin/validate/shopro/vip/Paylog.php new file mode 100644 index 0000000..8e10fc2 --- /dev/null +++ b/application/admin/validate/shopro/vip/Paylog.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/user/Club.php b/application/admin/validate/user/Club.php new file mode 100644 index 0000000..62dd4dd --- /dev/null +++ b/application/admin/validate/user/Club.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/view/addon/add.html b/application/admin/view/addon/add.html new file mode 100644 index 0000000..e69de29 diff --git a/application/admin/view/addon/config.html b/application/admin/view/addon/config.html new file mode 100644 index 0000000..036afad --- /dev/null +++ b/application/admin/view/addon/config.html @@ -0,0 +1,136 @@ +
    + {if $addon.tips && $addon.tips.value} +
    + {if $addon.tips.title} + {$addon.tips.title}
    + {/if} + {$addon.tips.value} +
    + {/if} + +
    + {if count($groupList)>1} +
    + +
    + {/if} + +
    +
    + {foreach name="groupList" id="group" key="groupName"} +
    + + + + {foreach name="$addon.config" id="item"} + {if ((!isset($item['group']) || $item['group']=='') && $groupName=='other') || (isset($item['group']) && $item['group']==$group)} + + + + + {/if} + {/foreach} + +
    {$item.title} +
    +
    + {switch $item.type} + {case string} + + {/case} + {case password} + + {/case} + {case text} + + {/case} + {case array} +
    +
    + {:__('Array key')} + {:__('Array value')} +
    +
    {:__('Append')}
    + +
    + {/case} + {case date} + + {/case} + {case time} + + {/case} + {case datetime} + + {/case} + {case number} + + {/case} + {case checkbox} + {foreach name="item.content" item="vo"} + + {/foreach} + + {/case} + {case radio} + {foreach name="item.content" item="vo"} + + {/foreach} + + {/case} + {case value="select" break="0"}{/case} + {case value="selects"} + + {/case} + {case value="image" break="0"}{/case} + {case value="images"} +
    + + + +
      + +
      + {/case} + {case value="file" break="0"}{/case} + {case value="files"} +
      + + + + +
      + {/case} + {case bool} + + + {/case} + {default /}{$item.value} + {/switch} +
      +
      +
      + +
      +
      + {/foreach} + +
      +
      +
      +
      diff --git a/application/admin/view/addon/index.html b/application/admin/view/addon/index.html new file mode 100644 index 0000000..1222332 --- /dev/null +++ b/application/admin/view/addon/index.html @@ -0,0 +1,302 @@ + +
      +
      + {:build_heading(null,FALSE)} + {if $Think.config.fastadmin.api_url} + + {/if} +
      + +
      +
      +
      +
      +
      + + {if $Think.config.fastadmin.api_url} + + + {:__('Userinfo')} + {/if} +
      + + +
      + +
      +
      + +
      +
      +
      + + + + + + + + diff --git a/application/admin/view/auth/admin/add.html b/application/admin/view/auth/admin/add.html new file mode 100644 index 0000000..3cb174e --- /dev/null +++ b/application/admin/view/auth/admin/add.html @@ -0,0 +1,52 @@ +
      + {:token()} +
      + +
      + {:build_select('group[]', $groupdata, null, ['class'=>'form-control selectpicker', 'multiple'=>'', 'data-rule'=>'required'])} +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])} +
      +
      + +
      diff --git a/application/admin/view/auth/admin/edit.html b/application/admin/view/auth/admin/edit.html new file mode 100644 index 0000000..a36d009 --- /dev/null +++ b/application/admin/view/auth/admin/edit.html @@ -0,0 +1,58 @@ +
      + {:token()} +
      + +
      + {:build_select('group[]', $groupdata, $groupids, ['class'=>'form-control selectpicker', 'multiple'=>'', 'data-rule'=>'required'])} +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])} +
      +
      + +
      diff --git a/application/admin/view/auth/admin/index.html b/application/admin/view/auth/admin/index.html new file mode 100644 index 0000000..17d7466 --- /dev/null +++ b/application/admin/view/auth/admin/index.html @@ -0,0 +1,21 @@ +
      + {:build_heading()} + +
      +
      +
      +
      +
      + {:build_toolbar('refresh,add,delete')} +
      + +
      +
      +
      + +
      +
      +
      diff --git a/application/admin/view/auth/adminlog/detail.html b/application/admin/view/auth/adminlog/detail.html new file mode 100644 index 0000000..49722a5 --- /dev/null +++ b/application/admin/view/auth/adminlog/detail.html @@ -0,0 +1,27 @@ + + + + + + + + + + {volist name="row" id="vo" } + + + + + {/volist} + +
      {:__('Title')}{:__('Content')}
      {:__($key)}{if $key=='createtime'}{$vo|datetime}{else/}{$vo|htmlentities}{/if}
      + diff --git a/application/admin/view/auth/adminlog/index.html b/application/admin/view/auth/adminlog/index.html new file mode 100644 index 0000000..d5deee7 --- /dev/null +++ b/application/admin/view/auth/adminlog/index.html @@ -0,0 +1,21 @@ +
      + {:build_heading()} + +
      +
      +
      +
      +
      + {:build_toolbar('refresh,delete')} +
      + +
      +
      +
      + +
      +
      +
      diff --git a/application/admin/view/auth/group/add.html b/application/admin/view/auth/group/add.html new file mode 100644 index 0000000..3c281ee --- /dev/null +++ b/application/admin/view/auth/group/add.html @@ -0,0 +1,38 @@ +
      + {:token()} + +
      + +
      + {:build_select('row[pid]', $groupdata, null, ['class'=>'form-control selectpicker', 'data-rule'=>'required'])} +
      +
      +
      + +
      + +
      +
      +
      + +
      + + + +
      +
      +
      +
      + +
      + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])} +
      +
      + +
      diff --git a/application/admin/view/auth/group/edit.html b/application/admin/view/auth/group/edit.html new file mode 100644 index 0000000..a062e43 --- /dev/null +++ b/application/admin/view/auth/group/edit.html @@ -0,0 +1,38 @@ +
      + {:token()} + +
      + +
      + {:build_select('row[pid]', $groupdata, $row['pid'], ['class'=>'form-control selectpicker', 'data-rule'=>'required', 'data-id'=>$row['id'], 'data-pid'=>$row['pid']])} +
      +
      +
      + +
      + +
      +
      +
      + +
      + + + +
      +
      +
      +
      + +
      + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])} +
      +
      + +
      diff --git a/application/admin/view/auth/group/index.html b/application/admin/view/auth/group/index.html new file mode 100644 index 0000000..15f8c36 --- /dev/null +++ b/application/admin/view/auth/group/index.html @@ -0,0 +1,21 @@ +
      + {:build_heading()} + +
      +
      +
      +
      +
      + {:build_toolbar('refresh,add,delete')} +
      + +
      +
      +
      + +
      +
      +
      diff --git a/application/admin/view/auth/rule/add.html b/application/admin/view/auth/rule/add.html new file mode 100644 index 0000000..33fd459 --- /dev/null +++ b/application/admin/view/auth/rule/add.html @@ -0,0 +1,87 @@ +
      + {:token()} +
      + +
      + {:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')])} +
      +
      +
      + +
      + {:build_select('row[pid]', $ruledata, null, ['class'=>'form-control', 'required'=>''])} +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + {:build_radios('row[menutype]', $menutypeList)} +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])} +
      +
      + +
      +{include file="auth/rule/tpl" /} diff --git a/application/admin/view/auth/rule/edit.html b/application/admin/view/auth/rule/edit.html new file mode 100644 index 0000000..658a408 --- /dev/null +++ b/application/admin/view/auth/rule/edit.html @@ -0,0 +1,87 @@ +
      + {:token()} +
      + +
      + {:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')], $row['ismenu'])} +
      +
      +
      + +
      + {:build_select('row[pid]', $ruledata, $row['pid'], ['class'=>'form-control', 'required'=>''])} +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + {:build_radios('row[menutype]', $menutypeList, $row['menutype'])} +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])} +
      +
      + +
      +{include file="auth/rule/tpl" /} diff --git a/application/admin/view/auth/rule/index.html b/application/admin/view/auth/rule/index.html new file mode 100644 index 0000000..be73a56 --- /dev/null +++ b/application/admin/view/auth/rule/index.html @@ -0,0 +1,35 @@ + + diff --git a/application/admin/view/auth/rule/tpl.html b/application/admin/view/auth/rule/tpl.html new file mode 100644 index 0000000..9413dde --- /dev/null +++ b/application/admin/view/auth/rule/tpl.html @@ -0,0 +1,43 @@ + + \ No newline at end of file diff --git a/application/admin/view/category/add.html b/application/admin/view/category/add.html new file mode 100644 index 0000000..e92f56c --- /dev/null +++ b/application/admin/view/category/add.html @@ -0,0 +1,100 @@ +
      + {:token()} +
      + {:__('Category warmtips')} +
      + +
      + +
      + + + +
      +
      +
      + +
      + + + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + + + +
      +
      +
      + +
      +
      + +
      + + +
      + +
      +
        +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])} +
        +
        + +
        diff --git a/application/admin/view/category/edit.html b/application/admin/view/category/edit.html new file mode 100644 index 0000000..4beea83 --- /dev/null +++ b/application/admin/view/category/edit.html @@ -0,0 +1,96 @@ +
        + {:token()} +
        + +
        + + + +
        +
        +
        + +
        + + + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + + + +
        +
        +
        + +
        +
        + +
        + + +
        + +
        +
          +
          +
          +
          + +
          + +
          +
          +
          + +
          + +
          +
          +
          + +
          + +
          +
          +
          + +
          + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])} +
          +
          + +
          diff --git a/application/admin/view/category/index.html b/application/admin/view/category/index.html new file mode 100644 index 0000000..c257520 --- /dev/null +++ b/application/admin/view/category/index.html @@ -0,0 +1,36 @@ +
          +
          + {:build_heading(null,FALSE)} + + +
          +
          +
          +
          +
          +
          + {:build_toolbar('refresh,add,edit,del')} + +
          + +
          +
          +
          + +
          +
          +
          diff --git a/application/admin/view/club/complain/add.html b/application/admin/view/club/complain/add.html new file mode 100644 index 0000000..cc4ea3f --- /dev/null +++ b/application/admin/view/club/complain/add.html @@ -0,0 +1,28 @@ +
          + +
          + +
          + +
          +
          +
          + +
          + +
          +
          +
          + +
          + +
          +
          + +
          diff --git a/application/admin/view/club/complain/edit.html b/application/admin/view/club/complain/edit.html new file mode 100644 index 0000000..252de48 --- /dev/null +++ b/application/admin/view/club/complain/edit.html @@ -0,0 +1,28 @@ +
          + +
          + +
          + +
          +
          + + + + + + +
          + +
          + {:build_radios('row[status]', ['1'=>__('未处理'), '2'=>__('已处理')], $row['status'])} +
          +
          + +
          diff --git a/application/admin/view/club/complain/index.html b/application/admin/view/club/complain/index.html new file mode 100644 index 0000000..e8c618d --- /dev/null +++ b/application/admin/view/club/complain/index.html @@ -0,0 +1,35 @@ +
          + {:build_heading()} + + +
          diff --git a/application/admin/view/club/invate/add.html b/application/admin/view/club/invate/add.html new file mode 100644 index 0000000..4490707 --- /dev/null +++ b/application/admin/view/club/invate/add.html @@ -0,0 +1,40 @@ +
          + +
          + +
          + +
          +
          +
          + +
          + +
          +
          +
          + +
          + +
          + {foreach name="statusList" item="vo"} + + {/foreach} +
          + +
          +
          +
          + +
          + +
          +
          + +
          diff --git a/application/admin/view/club/invate/edit.html b/application/admin/view/club/invate/edit.html new file mode 100644 index 0000000..24f2dfe --- /dev/null +++ b/application/admin/view/club/invate/edit.html @@ -0,0 +1,49 @@ +
          + +
          + +
          + +
          +
          + +
          + +
          + +
          +
          + + + + + + + + + + + + + + + + + + {if ($row.status == 5) || ($row.status == 6) || ($row.status == 7)} +
          + +
          + + {:build_radios('row[status]', [ '6' => '通过', '7' => '拒绝'] )} +
          +
          + {/if} + +
          diff --git a/application/admin/view/club/invate/index.html b/application/admin/view/club/invate/index.html new file mode 100644 index 0000000..3a9928d --- /dev/null +++ b/application/admin/view/club/invate/index.html @@ -0,0 +1,45 @@ +
          + +
          + {:build_heading(null,FALSE)} + +
          + + +
          +
          + + +
          +
          +
          diff --git a/application/admin/view/club/invate/recyclebin.html b/application/admin/view/club/invate/recyclebin.html new file mode 100644 index 0000000..9eaf11b --- /dev/null +++ b/application/admin/view/club/invate/recyclebin.html @@ -0,0 +1,25 @@ +
          + {:build_heading()} + +
          +
          +
          + +
          + +
          +
          +
          diff --git a/application/admin/view/cms/archives/add.html b/application/admin/view/cms/archives/add.html new file mode 100644 index 0000000..613bc8f --- /dev/null +++ b/application/admin/view/cms/archives/add.html @@ -0,0 +1,289 @@ +{include file="cms/archives/common" /} +
          + +
          +
          +
          +
          + +
          +
          + +
          +
          +
          + +
          + +
          +
          +
          + +
          + +
          +
          +
          + +
          + +
          +
          +
          + +
          + +
          +
          +
          + +
          +
          + + + + + + +
          +
          +
          +
          + +
          +
          + +
          + + +
          + +
          +
            +
            +
            + +
            + +
            +
            + +
            + + +
            + +
            +
              +
              +
              +
              + +
              + +
              +
              + +
              + +
              + +
              +
              + +
              +
              {:__('Price')}
              +
              + +
              +
              +
              +
              {:__('Outlink')}
              +
              + +
              +
              +
              + +
              + +
              +
              +
              + +
              + +
              +
              +
              + +
              + +
              +
              +
              +
              +
              +
              + +
              + +
              +
              +
              +
              + +
              +
              +
              +
              +
              + +
              +
              + + + +
              +
              +
              +
              + +
              +
              + + + +
              +
              +
              +
              + +
              +
              + + + +
              +
              +
              +
              + +
              +
              + + +
              +
              +
              +
              + +
              + +
              +
              +
              + +
              +
              +
              +
              +
              + +
              +
              +
              +
              +
              + +
              + + + +
              +
              +
              + +
              + + + +
              +
              +
              + +
              + + + + +
              +
              +
              + +
              + + + + +
              +
              +
              + +
              + +
              +
              +
              +
              +
              +
              +
              +
              + +
              diff --git a/application/admin/view/cms/archives/club_match_list.html b/application/admin/view/cms/archives/club_match_list.html new file mode 100644 index 0000000..4289587 --- /dev/null +++ b/application/admin/view/cms/archives/club_match_list.html @@ -0,0 +1,105 @@ + +
              +
              +
              +
              +
              +
              + {:build_toolbar('refresh')} +
              + +
              +
              +
              + +
              +
              +
              +
              \ No newline at end of file diff --git a/application/admin/view/cms/archives/common.html b/application/admin/view/cms/archives/common.html new file mode 100644 index 0000000..2c6a7bb --- /dev/null +++ b/application/admin/view/cms/archives/common.html @@ -0,0 +1,71 @@ + + + + diff --git a/application/admin/view/cms/archives/content.html b/application/admin/view/cms/archives/content.html new file mode 100644 index 0000000..a6d897b --- /dev/null +++ b/application/admin/view/cms/archives/content.html @@ -0,0 +1,42 @@ + + +
              + +
              + {:build_heading(null,FALSE)} + +
              + +
              +
              +
              +
              +
              + {:build_toolbar('refresh,edit,del')} +
              + +
              +
              +
              + +
              +
              +
              \ No newline at end of file diff --git a/application/admin/view/cms/archives/contestant.html b/application/admin/view/cms/archives/contestant.html new file mode 100644 index 0000000..4289587 --- /dev/null +++ b/application/admin/view/cms/archives/contestant.html @@ -0,0 +1,105 @@ + +
              +
              +
              +
              +
              +
              + {:build_toolbar('refresh')} +
              + +
              +
              +
              + +
              +
              +
              +
              \ No newline at end of file diff --git a/application/admin/view/cms/archives/course.html b/application/admin/view/cms/archives/course.html new file mode 100644 index 0000000..ebb7386 --- /dev/null +++ b/application/admin/view/cms/archives/course.html @@ -0,0 +1,1662 @@ + + + + + + + + + + + +
              +
              刷新
              +
              +
                +
              • 资格赛
              • +
              • 排位赛
              • +
              • 淘汰赛
              • +
              • 决赛
              • +
              +
              + +
              +
              + +
              +
              + +
              +
              + +
              + +
              + +
              +
              +
              +
              +
              +
              +
              +
              + + + +
              +
              + +
              +
              +
              + +
              + +
              +
              +
              + 分组导出 +
              +
              +
              +
              + +
              +
              + +
              +
              + +
              + +
              + +
              +
              +
              + 加赛 + 分组导出 +
              +
              +
              +
              +
              +
              + + + + + + + + + + + + diff --git a/application/admin/view/cms/archives/edit.html b/application/admin/view/cms/archives/edit.html new file mode 100644 index 0000000..758a918 --- /dev/null +++ b/application/admin/view/cms/archives/edit.html @@ -0,0 +1,300 @@ +{include file="cms/archives/common" /} +
              + + +
              +
              +
              +
              + +
              +
              + +
              +
              +
              + +
              + +
              +
              +
              + +
              + +
              +
              +
              + +
              + +
              +
              +
              + +
              + +
              +
              +
              + +
              +
              + + + + + + +
              +
              +
              + +
              + +
              +
              + +
              + + +
              + +
              +
                +
                +
                +
                + +
                +
                + +
                + + +
                + +
                +
                  +
                  +
                  +
                  + +
                  + +
                  +
                  + +
                  + +
                  + +
                  +
                  +
                  + + +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  +
                  + +
                  +
                  +
                  +
                  +
                  + +
                  +
                  + + + +
                  +
                  +
                  +
                  + +
                  +
                  + + + +
                  +
                  +
                  +
                  + +
                  +
                  + + + +
                  +
                  +
                  +
                  + +
                  +
                  + + +
                  +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  +
                  +
                  +
                  +
                  + +
                  +
                  +
                  +
                  +
                  + +
                  + + + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + + + + +
                  +
                  +
                  + +
                  + + + + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  +
                  +
                  +
                  +
                  +
                  + +
                  diff --git a/application/admin/view/cms/archives/index.html b/application/admin/view/cms/archives/index.html new file mode 100644 index 0000000..6e9199a --- /dev/null +++ b/application/admin/view/cms/archives/index.html @@ -0,0 +1,211 @@ + +
                  + +
                  +
                  +
                  + {:build_heading(null,FALSE)} + +
                  + +
                  +
                  +
                  + + + + + diff --git a/application/admin/view/cms/archives/league.html b/application/admin/view/cms/archives/league.html new file mode 100644 index 0000000..79806d8 --- /dev/null +++ b/application/admin/view/cms/archives/league.html @@ -0,0 +1,432 @@ + + + + + + Document + + + + + +
                  +
                  +
                  + + + + + + + + + diff --git a/application/admin/view/cms/archives/league_allplayer.html b/application/admin/view/cms/archives/league_allplayer.html new file mode 100644 index 0000000..4289587 --- /dev/null +++ b/application/admin/view/cms/archives/league_allplayer.html @@ -0,0 +1,105 @@ + +
                  +
                  +
                  +
                  +
                  +
                  + {:build_toolbar('refresh')} +
                  + +
                  +
                  +
                  + +
                  +
                  +
                  +
                  \ No newline at end of file diff --git a/application/admin/view/cms/archives/league_course.html b/application/admin/view/cms/archives/league_course.html new file mode 100644 index 0000000..529ce70 --- /dev/null +++ b/application/admin/view/cms/archives/league_course.html @@ -0,0 +1,1656 @@ + + + + + + + + + + + +
                  +
                  刷新
                  +
                  +
                    +
                  • 资格赛
                  • +
                  • 排位赛
                  • +
                  • 淘汰赛
                  • +
                  • 决赛
                  • +
                  +
                  + + +
                  +
                  + +
                  +
                  + +
                  +
                  + +
                  + +
                  + +
                  +
                  +
                  +
                  +
                  +
                  +
                  +
                  + + + +
                  +
                  + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + 分组导出 +
                  +
                  +
                  +
                  + +
                  +
                  + +
                  +
                  + +
                  + +
                  + +
                  +
                  +
                  + 加赛 + 分组导出 +
                  +
                  +
                  +
                  +
                  +
                  + + + + + + + + + + + + diff --git a/application/admin/view/cms/archives/league_wholeplayer.html b/application/admin/view/cms/archives/league_wholeplayer.html new file mode 100644 index 0000000..93f06e5 --- /dev/null +++ b/application/admin/view/cms/archives/league_wholeplayer.html @@ -0,0 +1,153 @@ + +
                  +
                  +
                  +
                  +
                  +
                  + {:build_toolbar('refresh')} +
                  + +
                  +
                  +
                  + +
                  +
                  +
                  +
                  \ No newline at end of file diff --git a/application/admin/view/cms/archives/leaguecontestant.html b/application/admin/view/cms/archives/leaguecontestant.html new file mode 100644 index 0000000..d9ba07d --- /dev/null +++ b/application/admin/view/cms/archives/leaguecontestant.html @@ -0,0 +1,146 @@ + +
                  +
                  +
                  +
                  +
                  +
                  + {:build_toolbar('refresh')} +
                  + +
                  +
                  +
                  + +
                  +
                  +
                  +
                  \ No newline at end of file diff --git a/application/admin/view/cms/archives/recyclebin.html b/application/admin/view/cms/archives/recyclebin.html new file mode 100644 index 0000000..ab39d45 --- /dev/null +++ b/application/admin/view/cms/archives/recyclebin.html @@ -0,0 +1,23 @@ +
                  + {:build_heading()} + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  diff --git a/application/admin/view/cms/archives/search.html b/application/admin/view/cms/archives/search.html new file mode 100644 index 0000000..ee3392d --- /dev/null +++ b/application/admin/view/cms/archives/search.html @@ -0,0 +1,45 @@ + + + + diff --git a/application/admin/view/cms/archives/test.html b/application/admin/view/cms/archives/test.html new file mode 100644 index 0000000..637ae7f --- /dev/null +++ b/application/admin/view/cms/archives/test.html @@ -0,0 +1,432 @@ + + + + + + Document + + + + + +
                  +
                  +
                  + + + + + + + + + diff --git a/application/admin/view/cms/archives/uploadfile.html b/application/admin/view/cms/archives/uploadfile.html new file mode 100644 index 0000000..ea20424 --- /dev/null +++ b/application/admin/view/cms/archives/uploadfile.html @@ -0,0 +1,139 @@ + + + +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  + + + + diff --git a/application/admin/view/cms/archives/visualization.html b/application/admin/view/cms/archives/visualization.html new file mode 100644 index 0000000..64d46b1 --- /dev/null +++ b/application/admin/view/cms/archives/visualization.html @@ -0,0 +1,417 @@ + + + + + + Document + + + + + +
                  +
                  +
                  + + + + + + + + + diff --git a/application/admin/view/cms/autolink/add.html b/application/admin/view/cms/autolink/add.html new file mode 100644 index 0000000..42e16e9 --- /dev/null +++ b/application/admin/view/cms/autolink/add.html @@ -0,0 +1,64 @@ +
                  + +
                  + +
                  + +
                  +
                  +
                  + +
                  +
                  + + + 选择链接 + + +
                  +
                  +
                  +
                  + +
                  + + + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + +
                  + {foreach name="statusList" item="vo"} + + {/foreach} +
                  + +
                  +
                  + +
                  diff --git a/application/admin/view/cms/autolink/edit.html b/application/admin/view/cms/autolink/edit.html new file mode 100644 index 0000000..a4baeb0 --- /dev/null +++ b/application/admin/view/cms/autolink/edit.html @@ -0,0 +1,64 @@ +
                  + +
                  + +
                  + +
                  +
                  +
                  + +
                  +
                  + + + 选择链接 + + +
                  +
                  +
                  +
                  + +
                  + + + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  + +
                  + {foreach name="statusList" item="vo"} + + {/foreach} +
                  + +
                  +
                  + +
                  diff --git a/application/admin/view/cms/autolink/index.html b/application/admin/view/cms/autolink/index.html new file mode 100644 index 0000000..2c233d5 --- /dev/null +++ b/application/admin/view/cms/autolink/index.html @@ -0,0 +1,44 @@ +
                  + +
                  + {:build_heading(null,FALSE)} + +
                  + + + +
                  diff --git a/application/admin/view/cms/autolink/recyclebin.html b/application/admin/view/cms/autolink/recyclebin.html new file mode 100644 index 0000000..ac43c48 --- /dev/null +++ b/application/admin/view/cms/autolink/recyclebin.html @@ -0,0 +1,25 @@ +
                  + {:build_heading()} + +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  diff --git a/application/admin/view/cms/block/add.html b/application/admin/view/cms/block/add.html new file mode 100644 index 0000000..299e011 --- /dev/null +++ b/application/admin/view/cms/block/add.html @@ -0,0 +1,114 @@ +
                  + {:token()} +
                  + +
                  + +
                  +
                  +
                  + +
                  +
                  + + + + +
                  +
                  +
                  +
                  + +
                  + +
                  +
                  +
                  + +
                  +
                  + +
                  + + +
                  + +
                  +
                    +
                    +
                    +
                    + +
                    +
                    + + + 选择链接 + + +
                    +
                    +
                    +
                    + +
                    + +
                    +
                    +
                    + +
                    + + + + +
                    +
                    +
                    + +
                    +
                    + + + + +
                    +
                    +
                    +
                    + +
                    +
                    + + + + +
                    +
                    +
                    +
                    {include file="cms/common/fields"}
                    +
                    + +
                    + +
                    + {foreach name="statusList" item="vo"} + + {/foreach} +
                    + +
                    +
                    + +
                    diff --git a/application/admin/view/cms/block/edit.html b/application/admin/view/cms/block/edit.html new file mode 100644 index 0000000..74fe652 --- /dev/null +++ b/application/admin/view/cms/block/edit.html @@ -0,0 +1,120 @@ +
                    + {:token()} +
                    + +
                    + +
                    +
                    +
                    + +
                    +
                    + + + + +
                    +
                    +
                    +
                    + +
                    + +
                    +
                    +
                    + +
                    +
                    + +
                    + + +
                    + +
                    +
                      +
                      +
                      +
                      + +
                      +
                      + + + 选择链接 + + +
                      +
                      +
                      +
                      + +
                      + +
                      +
                      +
                      + +
                      + + + + +
                      +
                      +
                      + +
                      + +
                      +
                      +
                      + +
                      +
                      + + + + +
                      +
                      +
                      +
                      + +
                      +
                      + + + + +
                      +
                      +
                      +
                      {include file="cms/common/fields"}
                      +
                      + +
                      + +
                      + {foreach name="statusList" item="vo"} + + {/foreach} +
                      + +
                      +
                      + +
                      diff --git a/application/admin/view/cms/block/index.html b/application/admin/view/cms/block/index.html new file mode 100644 index 0000000..ae24bc5 --- /dev/null +++ b/application/admin/view/cms/block/index.html @@ -0,0 +1,39 @@ +
                      +
                      + {:build_heading(null,FALSE)} + +
                      + +
                      +
                      +
                      +
                      +
                      + {:build_toolbar('refresh,add,edit,del')} + + {if $auth->check('cms/fields/index')} + {:__('自定义字段')} + {/if} +
                      + +
                      +
                      +
                      + +
                      +
                      +
                      diff --git a/application/admin/view/cms/builder/index.html b/application/admin/view/cms/builder/index.html new file mode 100644 index 0000000..271ccec --- /dev/null +++ b/application/admin/view/cms/builder/index.html @@ -0,0 +1,898 @@ + +
                      + + + +
                      +
                      +
                      +
                      +
                      +
                      +
                      + 基础配置 +
                      + {foreach name="configList" id="item"} + + {/foreach} +
                      +
                      +
                      + 其它 +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + + +
                      +
                      +
                      +
                      +
                      +
                      + 全局参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + 特有参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + + +
                      +
                      +
                      +
                      +
                      +
                      + 全局参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + 特有参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + + +
                      +
                      +
                      +
                      +
                      +
                      + 全局参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + 特有参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + + +
                      +
                      +
                      +
                      +
                      +
                      + 全局参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + 特有参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + + +
                      +
                      +
                      +
                      +
                      +
                      + 全局参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + 特有参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + + +
                      +
                      +
                      +
                      +
                      +
                      + 全局参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + 特有参数 +
                      +
                      + + +
                      +
                      +
                      +
                      + + +
                      +
                      +
                      +
                      +
                      +
                      + 全局参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + 特有参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + + +
                      +
                      +
                      +
                      +
                      +
                      + 全局参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + 特有参数 +
                      +
                      + + +
                      +
                      + + +
                      +
                      +
                      +
                      + + +
                      +
                      +
                      +
                      +
                      +
                      +
                      + 标签预览 + +
                      +
                      + + +
                      +
                      + 执行结果 + +
                      +
                      +
                      +
                      + +
                      +
                      +
                      + + +{literal} + + + + + + + + + + + + + + + + + +{/literal} + diff --git a/application/admin/view/cms/channel/add.html b/application/admin/view/cms/channel/add.html new file mode 100644 index 0000000..d767484 --- /dev/null +++ b/application/admin/view/cms/channel/add.html @@ -0,0 +1,204 @@ +
                      + +
                      + +
                      + +
                      + {foreach name="typeList" item="vo"} + + {/foreach} +
                      +
                      +
                      +
                      + +
                      + +
                      +
                      +
                      + +
                      + + + +
                      +
                      +
                      + +
                      + +
                      +
                      +
                      + +
                      + + + +
                      +
                      +
                      + +
                      +
                      + +
                      + + +
                      + +
                      +
                        +
                        +
                        +
                        + +
                        + +
                        +
                        +
                        + +
                        + +
                        +
                        +
                        + +
                        + +
                        +
                        +
                        + +
                        + +
                        +
                        + +
                        + +
                        + +
                        +
                        +
                        + +
                        + +
                        +
                        +
                        + +
                        + +
                        +
                        +
                        + +
                        + +
                        +
                        +
                        + +
                        + +
                        +
                        +
                        + +
                        + {if $vipList} + + {else/} +
                        未安装或未配置VIP会员组
                        + {/if} +
                        +
                        +
                        + +
                        + + + + +
                        +
                        +
                        + +
                        + + + + +
                        +
                        +
                        {include file="cms/common/fields"}
                        +
                        + +
                        + +
                        + {foreach name="statusList" item="vo"} + + {/foreach} +
                        + +
                        +
                        + +
                        + + diff --git a/application/admin/view/cms/channel/admin.html b/application/admin/view/cms/channel/admin.html new file mode 100644 index 0000000..126e3e7 --- /dev/null +++ b/application/admin/view/cms/channel/admin.html @@ -0,0 +1,30 @@ +
                        + + {:build_heading()} + +
                        +
                        +
                        + {if !$isChannelAllocate} +
                        当前未开启栏目授权功能,以下的设置不会生效,请在站点配置中开启栏目授权功能。
                        + {/if} +
                        +
                        + {:build_toolbar('refresh')} +
                        + +
                        +
                        +
                        + +
                        +
                        +
                        + + diff --git a/application/admin/view/cms/channel/edit.html b/application/admin/view/cms/channel/edit.html new file mode 100644 index 0000000..532d37c --- /dev/null +++ b/application/admin/view/cms/channel/edit.html @@ -0,0 +1,219 @@ +
                        + +
                        + +
                        +
                        + {foreach name="typeList" item="vo"} + + {/foreach} +
                        +
                        +
                        + +
                        + +
                        + + + +
                        +
                        +
                        + +
                        + +
                        +
                        +
                        + +
                        + + + +
                        +
                        +
                        + +
                        +
                        + +
                        + + +
                        + +
                        +
                          +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          + +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + {if $vipList} + + {else/} +
                          未安装或未配置VIP会员插件
                          + {/if} +
                          +
                          +
                          + +
                          +
                          +
                          +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + + + + +
                          +
                          +
                          + +
                          + + + + +
                          +
                          +
                          {include file="cms/common/fields"}
                          +
                          + +
                          + +
                          + {foreach name="statusList" item="vo"} + + {/foreach} +
                          + +
                          +
                          + +
                          + + diff --git a/application/admin/view/cms/channel/index.html b/application/admin/view/cms/channel/index.html new file mode 100644 index 0000000..d83fd0e --- /dev/null +++ b/application/admin/view/cms/channel/index.html @@ -0,0 +1,44 @@ +
                          + +
                          + {:build_heading(null,FALSE)} + +
                          + +
                          +
                          +
                          +
                          +
                          + {:build_toolbar('refresh,add,edit,del')} + + + {if $auth->check('cms/channel/admin')} + {:__('栏目授权')} + {/if} + {if $auth->check('cms/fields/index')} + {:__('自定义字段')} + {/if} +
                          + +
                          +
                          +
                          + +
                          +
                          +
                          diff --git a/application/admin/view/cms/comment/add.html b/application/admin/view/cms/comment/add.html new file mode 100644 index 0000000..a7ae024 --- /dev/null +++ b/application/admin/view/cms/comment/add.html @@ -0,0 +1,82 @@ +
                          + +
                          + +
                          + + + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + {:build_radios('row[subscribe]', [1=>'是', 0=>'否'])} +
                          +
                          +
                          + +
                          + +
                          + {foreach name="statusList" item="vo"} + + {/foreach} +
                          + +
                          +
                          + +
                          diff --git a/application/admin/view/cms/comment/edit.html b/application/admin/view/cms/comment/edit.html new file mode 100644 index 0000000..eedab38 --- /dev/null +++ b/application/admin/view/cms/comment/edit.html @@ -0,0 +1,82 @@ +
                          + +
                          + +
                          + + + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          + +
                          + {:build_radios('row[subscribe]', [1=>'是', 0=>'否'], $row['subscribe'])} +
                          +
                          +
                          + +
                          + +
                          + {foreach name="statusList" item="vo"} + + {/foreach} +
                          + +
                          +
                          + +
                          diff --git a/application/admin/view/cms/comment/index.html b/application/admin/view/cms/comment/index.html new file mode 100644 index 0000000..211177a --- /dev/null +++ b/application/admin/view/cms/comment/index.html @@ -0,0 +1,42 @@ +
                          +
                          + {:build_heading(null,FALSE)} + +
                          + + +
                          diff --git a/application/admin/view/cms/comment/recyclebin.html b/application/admin/view/cms/comment/recyclebin.html new file mode 100644 index 0000000..3153d67 --- /dev/null +++ b/application/admin/view/cms/comment/recyclebin.html @@ -0,0 +1,23 @@ +
                          + {:build_heading()} + +
                          +
                          +
                          + +
                          + +
                          +
                          +
                          diff --git a/application/admin/view/cms/common/fields.html b/application/admin/view/cms/common/fields.html new file mode 100644 index 0000000..177bfa8 --- /dev/null +++ b/application/admin/view/cms/common/fields.html @@ -0,0 +1,157 @@ + + +{foreach $fields as $item} + +
                          +
                          {$item.title}
                          +
                          + {switch $item.type} + {case string} + + {/case} + {case password} + + {/case} + {case value="text" break="0"}{/case} + {case editor} + + {/case} + {case array} + {if $item.name=='downloadurl'} + {php}$item['value']=isset($values[$item['name']])?$item['value']:$item['download_list'];{/php} +
                          +
                          + 来源 + 地址 + 密码(可为空) +
                          +
                          {:__('Append')}
                          + +
                          + {else /} + {php}$arrList=isset($values[$item['name']])?(array)json_decode($item['value'],true):$item['content_list'];{/php} +
                          +
                          + {:isset($item["setting"]["key"])&&$item["setting"]["key"]?$item["setting"]["key"]:__('Array key')} + {:isset($item["setting"]["value"])&&$item["setting"]["value"]?$item["setting"]["value"]:__('Array value')} +
                          + +
                          {:__('Append')}
                          + +
                          + {/if} + {/case} + {case date} + + {/case} + {case time} + + {/case} + {case datetime} + + {/case} + {case datetimerange} + + {/case} + {case number} + + {/case} + {case checkbox} + {foreach name="item.content_list" item="vo"} +
                          + +
                          + {/foreach} + + {/case} + {case radio} + {foreach name="item.content_list" item="vo"} +
                          + +
                          + {/foreach} + + {/case} + {case value="select" break="0"}{/case} + {case value="selects"} + + {/case} + {case value="image" break="0"}{/case} + {case value="images"} +
                          + +
                          + + +
                          + +
                          +
                            + {/case} + {case value="file" break="0"}{/case} + {case value="files"} +
                            + +
                            + + +
                            + +
                            + {/case} + {case switch} + + + + + {/case} + {case bool} + + + {/case} + {case city} +
                            + +
                            + {/case} + {case value="selectpage" break="0"}{/case} + {case value="selectpages"} + + {/case} + {case custom} + {$item.extend_html} + {/case} + {/switch} +
                            +
                            +{/foreach} + + + diff --git a/application/admin/view/cms/common/links.html b/application/admin/view/cms/common/links.html new file mode 100644 index 0000000..f123d09 --- /dev/null +++ b/application/admin/view/cms/common/links.html @@ -0,0 +1,106 @@ +
                            +
                            + {:build_heading(null,FALSE)} + +
                            + +
                            +
                            +
                            +
                            + + +
                            +
                            +
                            + +
                            +
                            +
                            + + diff --git a/application/admin/view/cms/diydata/add.html b/application/admin/view/cms/diydata/add.html new file mode 100644 index 0000000..4967818 --- /dev/null +++ b/application/admin/view/cms/diydata/add.html @@ -0,0 +1,29 @@ +
                            + +
                            + +
                            + +
                            +
                            + {include file="cms/common/fields"} +
                            + +
                            + +
                            +
                            +
                            + +
                            + {:Form::radios("row[status]", ['normal'=>__('Normal'), 'hidden'=>__('Hidden'), 'rejected'=>__('Rejected')], 'normal')} +
                            +
                            + +
                            diff --git a/application/admin/view/cms/diydata/edit.html b/application/admin/view/cms/diydata/edit.html new file mode 100644 index 0000000..5b1d6dd --- /dev/null +++ b/application/admin/view/cms/diydata/edit.html @@ -0,0 +1,29 @@ +
                            + +
                            + +
                            + +
                            +
                            + {include file="cms/common/fields"} +
                            + +
                            + +
                            +
                            +
                            + +
                            + {:Form::radios("row[status]", ['normal'=>__('Normal'), 'hidden'=>__('Hidden'), 'rejected'=>__('Rejected')], $row['status'])} +
                            +
                            + +
                            diff --git a/application/admin/view/cms/diydata/fields.html b/application/admin/view/cms/diydata/fields.html new file mode 100644 index 0000000..9dd2e47 --- /dev/null +++ b/application/admin/view/cms/diydata/fields.html @@ -0,0 +1,102 @@ +{foreach $fields as $item} + +
                            +
                            {$item.title}
                            +
                            + {switch $item.type} + {case string} + + {/case} + {case value="text" break="0"}{/case} + {case editor} + + {/case} + {case array} + {php}$arrList=isset($values[$item['name']])?(array)json_decode($item['value'],true):$item['content_list'];{/php} +
                            +
                            + {:__('Array key')} + {:__('Array value')} +
                            + + {foreach $arrList as $key => $vo} +
                            + + + + +
                            + {/foreach} +
                            {:__('Append')}
                            +
                            + {/case} + {case date} + + {/case} + {case time} + + {/case} + {case datetime} + + {/case} + {case number} + + {/case} + {case checkbox} + {foreach name="item.content_list" item="vo"} + + {/foreach} + {/case} + {case radio} + {foreach name="item.content_list" item="vo"} + + {/foreach} + {/case} + {case value="select" break="0"}{/case} + {case value="selects"} + + {/case} + {case value="image" break="0"}{/case} + {case value="images"} +
                            + +
                            + + +
                            + +
                            +
                              + {/case} + {case value="file" break="0"}{/case} + {case value="files"} +
                              + +
                              + + +
                              + +
                              + {/case} + {case switch} + + + + + {/case} + {case bool} + + + {/case} + {case custom} + {$item.content} + {/case} + {/switch} +
                              +
                              +{/foreach} diff --git a/application/admin/view/cms/diydata/index.html b/application/admin/view/cms/diydata/index.html new file mode 100644 index 0000000..2b1d44f --- /dev/null +++ b/application/admin/view/cms/diydata/index.html @@ -0,0 +1,37 @@ +
                              + +
                              + {:build_heading(null,FALSE)} + +
                              + +
                              +
                              +
                              +
                              +
                              + {:build_toolbar('refresh,add,edit,del')} + +
                              + +
                              +
                              +
                              + +
                              +
                              +
                              diff --git a/application/admin/view/cms/diyform/add.html b/application/admin/view/cms/diyform/add.html new file mode 100644 index 0000000..5b44f68 --- /dev/null +++ b/application/admin/view/cms/diyform/add.html @@ -0,0 +1,151 @@ +
                              + +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + + + + +
                              +
                              +
                              + +
                              + + + + +
                              +
                              +
                              + +
                              + + + + +
                              +
                              +
                              + +
                              + + + + +
                              +
                              + +
                              + +
                              + + + + +
                              +
                              +
                              + +
                              + {:Form::radios("row[usermode]", ['all'=>'全部', 'user'=>'仅用户本人数据'], 'user')} +
                              +
                              +
                              + +
                              + {:Form::radios("row[statusmode]", ['all'=>'全部', 'normal'=>'仅已审核数据(状态正常)'], 'normal')} +
                              +
                              +
                              + +
                              + {:Form::radios("row[status]", $statusList, 'normal')} +
                              +
                              + +
                              diff --git a/application/admin/view/cms/diyform/edit.html b/application/admin/view/cms/diyform/edit.html new file mode 100644 index 0000000..11cd2d4 --- /dev/null +++ b/application/admin/view/cms/diyform/edit.html @@ -0,0 +1,151 @@ +
                              + +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + + + + +
                              +
                              +
                              + +
                              + + + + +
                              +
                              +
                              + +
                              + + + + +
                              +
                              +
                              + +
                              + + + + +
                              +
                              + +
                              + +
                              + + + + +
                              +
                              +
                              + +
                              + {:Form::radios("row[usermode]", ['all'=>'全部', 'user'=>'仅用户本人数据'], $row['usermode'])} +
                              +
                              +
                              + +
                              + {:Form::radios("row[statusmode]", ['all'=>'全部', 'normal'=>'仅已审核数据(状态正常)'], $row['statusmode'])} +
                              +
                              +
                              + +
                              + {:Form::radios("row[status]", $statusList, $row['status'])} +
                              +
                              + +
                              diff --git a/application/admin/view/cms/diyform/index.html b/application/admin/view/cms/diyform/index.html new file mode 100644 index 0000000..8be8631 --- /dev/null +++ b/application/admin/view/cms/diyform/index.html @@ -0,0 +1,34 @@ +
                              + +
                              + {:build_heading(null,FALSE)} + +
                              + +
                              +
                              +
                              +
                              +
                              + {:build_toolbar('refresh,add,edit,del')} +
                              + +
                              +
                              +
                              + +
                              +
                              +
                              diff --git a/application/admin/view/cms/diyform/tpl.html b/application/admin/view/cms/diyform/tpl.html new file mode 100644 index 0000000..7b49a6b --- /dev/null +++ b/application/admin/view/cms/diyform/tpl.html @@ -0,0 +1,66 @@ + + + \ No newline at end of file diff --git a/application/admin/view/cms/fields/add.html b/application/admin/view/cms/fields/add.html new file mode 100644 index 0000000..2cb89d7 --- /dev/null +++ b/application/admin/view/cms/fields/add.html @@ -0,0 +1,250 @@ +
                              + + +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              + + + + + + + + + + + + + + + + {if in_array($source, ['model', 'diyform'])} +
                              + +
                              + + + + +
                              +
                              + + {/if} +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + + +
                              + + + + +
                              +
                              + {if in_array($source, ['model', 'diyform'])} +
                              + +
                              + + + + +
                              +
                              + {/if} +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              + {foreach name="statusList" item="vo"} + + {/foreach} +
                              + +
                              +
                              + +
                              diff --git a/application/admin/view/cms/fields/archives.html b/application/admin/view/cms/fields/archives.html new file mode 100644 index 0000000..774bdfb --- /dev/null +++ b/application/admin/view/cms/fields/archives.html @@ -0,0 +1,60 @@ +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              + +
                              + + +
                              + + + + +
                              +
                              +
                              + +
                              + +
                              用于前台列表筛选使用
                              +
                              +
                              +
                              + + +
                              + + + + +
                              +
                              + {if in_array($row.name, ["channel_ids", "image", "images", "tags", "price", "outlink", "content", "keywords", "description"])} +
                              + +
                              + + + + +
                              +
                              + {/if} + +
                              diff --git a/application/admin/view/cms/fields/edit.html b/application/admin/view/cms/fields/edit.html new file mode 100644 index 0000000..bb83403 --- /dev/null +++ b/application/admin/view/cms/fields/edit.html @@ -0,0 +1,207 @@ +
                              + + + +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              + + + + + + + + + + + {if in_array($row.source, ['model', 'diyform'])} +
                              + + +
                              + + + + +
                              +
                              +
                              + +
                              + +
                              用于前台列表筛选使用
                              +
                              +
                              + {/if} +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + + +
                              + + + + +
                              +
                              + {if in_array($row.source, ['model', 'diyform'])} +
                              + +
                              + + + + +
                              +
                              + {/if} +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              + {foreach name="statusList" item="vo"} + + {/foreach} +
                              + +
                              +
                              + +
                              diff --git a/application/admin/view/cms/fields/index.html b/application/admin/view/cms/fields/index.html new file mode 100644 index 0000000..e03c140 --- /dev/null +++ b/application/admin/view/cms/fields/index.html @@ -0,0 +1,38 @@ +
                              + +
                              + {:build_heading(null,FALSE)} + {if(in_array($source,['model','diyform']))} + + {/if} +
                              + +
                              +
                              +
                              +
                              +
                              + {:build_toolbar('refresh,add,edit,del')} + +
                              + +
                              +
                              +
                              + +
                              +
                              +
                              diff --git a/application/admin/view/cms/modelx/add.html b/application/admin/view/cms/modelx/add.html new file mode 100644 index 0000000..6e61028 --- /dev/null +++ b/application/admin/view/cms/modelx/add.html @@ -0,0 +1,39 @@ +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              + +
                              diff --git a/application/admin/view/cms/modelx/edit.html b/application/admin/view/cms/modelx/edit.html new file mode 100644 index 0000000..5d8dc13 --- /dev/null +++ b/application/admin/view/cms/modelx/edit.html @@ -0,0 +1,43 @@ +
                              +
                              + 温馨提示
                              + 模型在使用相应的模板前,请确保添加相应的字段,否则前台页面浏览时会报错 +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              + +
                              diff --git a/application/admin/view/cms/modelx/index.html b/application/admin/view/cms/modelx/index.html new file mode 100644 index 0000000..cb47e7d --- /dev/null +++ b/application/admin/view/cms/modelx/index.html @@ -0,0 +1,25 @@ +
                              + {:build_heading()} + +
                              +
                              +
                              +
                              +
                              + {:build_toolbar('refresh,add,edit,del')} +
                              + +
                              +
                              +
                              + +
                              +
                              +
                              diff --git a/application/admin/view/cms/modelx/tpl.html b/application/admin/view/cms/modelx/tpl.html new file mode 100644 index 0000000..975712a --- /dev/null +++ b/application/admin/view/cms/modelx/tpl.html @@ -0,0 +1,66 @@ + + + diff --git a/application/admin/view/cms/order/add.html b/application/admin/view/cms/order/add.html new file mode 100644 index 0000000..97239f4 --- /dev/null +++ b/application/admin/view/cms/order/add.html @@ -0,0 +1,82 @@ +
                              + +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              + {foreach name="statusList" item="vo"} + + {/foreach} +
                              + +
                              +
                              + +
                              diff --git a/application/admin/view/cms/order/edit.html b/application/admin/view/cms/order/edit.html new file mode 100644 index 0000000..0a6568f --- /dev/null +++ b/application/admin/view/cms/order/edit.html @@ -0,0 +1,82 @@ +
                              + +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              + {foreach name="statusList" item="vo"} + + {/foreach} +
                              + +
                              +
                              + +
                              diff --git a/application/admin/view/cms/order/index.html b/application/admin/view/cms/order/index.html new file mode 100644 index 0000000..48fa9b4 --- /dev/null +++ b/application/admin/view/cms/order/index.html @@ -0,0 +1,43 @@ +
                              + +
                              + {:build_heading(null,FALSE)} + +
                              + + + +
                              diff --git a/application/admin/view/cms/page/add.html b/application/admin/view/cms/page/add.html new file mode 100644 index 0000000..4534b50 --- /dev/null +++ b/application/admin/view/cms/page/add.html @@ -0,0 +1,126 @@ +
                              + {:token()} + +
                              + +
                              + +
                              +
                              +
                              + +
                              + +
                              +
                              +
                              + +
                              + + + +
                              +
                              +
                              + +
                              +
                              + +
                              + + +
                              + +
                              +
                                +
                                +
                                + +
                                + +
                                + +
                                +
                                +
                                + +
                                + +
                                +
                                +
                                + +
                                + +
                                +
                                +
                                + +
                                + +
                                +
                                +
                                + +
                                + +
                                +
                                +
                                + +
                                + +
                                +
                                +
                                + +
                                + + + + +
                                +
                                +
                                + +
                                + + + + +
                                +
                                +
                                {include file="cms/common/fields"}
                                +
                                + +
                                + +
                                + {foreach name="statusList" item="vo"} + + {/foreach} +
                                + +
                                +
                                + +
                                diff --git a/application/admin/view/cms/page/edit.html b/application/admin/view/cms/page/edit.html new file mode 100644 index 0000000..88cb83b --- /dev/null +++ b/application/admin/view/cms/page/edit.html @@ -0,0 +1,138 @@ +
                                + {:token()} + +
                                + +
                                + +
                                +
                                +
                                + +
                                + +
                                +
                                +
                                + +
                                + + + +
                                +
                                +
                                + +
                                +
                                + +
                                + + +
                                + +
                                +
                                  +
                                  +
                                  +
                                  + + +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + + + + +
                                  +
                                  +
                                  + +
                                  + + + + +
                                  +
                                  +
                                  {include file="cms/common/fields"}
                                  +
                                  + +
                                  + +
                                  + {foreach name="statusList" item="vo"} + + {/foreach} +
                                  + +
                                  +
                                  + +
                                  diff --git a/application/admin/view/cms/page/index.html b/application/admin/view/cms/page/index.html new file mode 100644 index 0000000..91f83f4 --- /dev/null +++ b/application/admin/view/cms/page/index.html @@ -0,0 +1,42 @@ +
                                  +
                                  + {:build_heading(null,FALSE)} + +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  + {:build_toolbar('refresh,add,edit,del')} + {:__('Recycle bin')} + + + + {if $auth->check('cms/fields/index')} + {:__('自定义字段')} + {/if} +
                                  + +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  diff --git a/application/admin/view/cms/page/recyclebin.html b/application/admin/view/cms/page/recyclebin.html new file mode 100644 index 0000000..d1cbda6 --- /dev/null +++ b/application/admin/view/cms/page/recyclebin.html @@ -0,0 +1,23 @@ +
                                  + {:build_heading()} + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  diff --git a/application/admin/view/cms/page/select.html b/application/admin/view/cms/page/select.html new file mode 100644 index 0000000..d39d6f3 --- /dev/null +++ b/application/admin/view/cms/page/select.html @@ -0,0 +1,29 @@ +
                                  +
                                  + {:build_heading(null,FALSE)} + +
                                  + +
                                  +
                                  +
                                  +
                                  +
                                  + {:build_toolbar('refresh,add,edit')} +
                                  + +
                                  +
                                  +
                                  + +
                                  +
                                  +
                                  diff --git a/application/admin/view/cms/search_log/add.html b/application/admin/view/cms/search_log/add.html new file mode 100644 index 0000000..209766b --- /dev/null +++ b/application/admin/view/cms/search_log/add.html @@ -0,0 +1,32 @@ +
                                  + +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  +
                                  + {foreach name="statusList" item="vo"} + + {/foreach} +
                                  +
                                  +
                                  + +
                                  diff --git a/application/admin/view/cms/search_log/edit.html b/application/admin/view/cms/search_log/edit.html new file mode 100644 index 0000000..cda4025 --- /dev/null +++ b/application/admin/view/cms/search_log/edit.html @@ -0,0 +1,32 @@ +
                                  + +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  +
                                  + {foreach name="statusList" item="vo"} + + {/foreach} +
                                  +
                                  +
                                  + +
                                  diff --git a/application/admin/view/cms/search_log/index.html b/application/admin/view/cms/search_log/index.html new file mode 100644 index 0000000..e115f13 --- /dev/null +++ b/application/admin/view/cms/search_log/index.html @@ -0,0 +1,32 @@ +
                                  + {:build_heading()} + + +
                                  diff --git a/application/admin/view/cms/special/add.html b/application/admin/view/cms/special/add.html new file mode 100644 index 0000000..d1f2e6d --- /dev/null +++ b/application/admin/view/cms/special/add.html @@ -0,0 +1,144 @@ +
                                  + +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  + + + +
                                  +
                                  +
                                  + +
                                  + +
                                  +
                                  +
                                  + +
                                  +
                                  + +
                                  + + +
                                  + +
                                  +
                                    +
                                    +
                                    +
                                    + +
                                    +
                                    + +
                                    + + +
                                    + +
                                    +
                                      +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + + + + +
                                      +
                                      +
                                      {include file="cms/common/fields"}
                                      +
                                      + +
                                      + +
                                      + {foreach name="statusList" item="vo"} + + {/foreach} +
                                      + +
                                      +
                                      + +
                                      diff --git a/application/admin/view/cms/special/edit.html b/application/admin/view/cms/special/edit.html new file mode 100644 index 0000000..4cf2ef4 --- /dev/null +++ b/application/admin/view/cms/special/edit.html @@ -0,0 +1,144 @@ +
                                      + +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      + + + +
                                      +
                                      +
                                      + +
                                      + +
                                      +
                                      +
                                      + +
                                      +
                                      + +
                                      + + +
                                      + +
                                      +
                                        +
                                        +
                                        +
                                        + +
                                        +
                                        + +
                                        + + +
                                        + +
                                        +
                                          +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + + + + +
                                          +
                                          +
                                          {include file="cms/common/fields"}
                                          +
                                          + +
                                          + +
                                          + {foreach name="statusList" item="vo"} + + {/foreach} +
                                          + +
                                          +
                                          + +
                                          diff --git a/application/admin/view/cms/special/index.html b/application/admin/view/cms/special/index.html new file mode 100644 index 0000000..ba5b7b6 --- /dev/null +++ b/application/admin/view/cms/special/index.html @@ -0,0 +1,48 @@ +
                                          + +
                                          + {:build_heading(null,FALSE)} + +
                                          + + +
                                          +
                                          + + +
                                          +
                                          +
                                          diff --git a/application/admin/view/cms/special/recyclebin.html b/application/admin/view/cms/special/recyclebin.html new file mode 100644 index 0000000..19795e0 --- /dev/null +++ b/application/admin/view/cms/special/recyclebin.html @@ -0,0 +1,23 @@ +
                                          + {:build_heading()} + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          diff --git a/application/admin/view/cms/spider_log/add.html b/application/admin/view/cms/spider_log/add.html new file mode 100644 index 0000000..10cf9bc --- /dev/null +++ b/application/admin/view/cms/spider_log/add.html @@ -0,0 +1,58 @@ +
                                          + +
                                          + +
                                          + + + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          + +
                                          diff --git a/application/admin/view/cms/spider_log/edit.html b/application/admin/view/cms/spider_log/edit.html new file mode 100644 index 0000000..497fa8c --- /dev/null +++ b/application/admin/view/cms/spider_log/edit.html @@ -0,0 +1,64 @@ +
                                          + +
                                          + +
                                          + + + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          + +
                                          diff --git a/application/admin/view/cms/spider_log/index.html b/application/admin/view/cms/spider_log/index.html new file mode 100644 index 0000000..261bd33 --- /dev/null +++ b/application/admin/view/cms/spider_log/index.html @@ -0,0 +1,36 @@ +
                                          + +
                                          + {:build_heading(null,FALSE)} + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          diff --git a/application/admin/view/cms/statistics/index.html b/application/admin/view/cms/statistics/index.html new file mode 100644 index 0000000..2a6de3f --- /dev/null +++ b/application/admin/view/cms/statistics/index.html @@ -0,0 +1,537 @@ + + +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          总订单金额

                                          +

                                          ¥{$totalOrderAmount|sprintf='%.2f',###}

                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          今日订单金额

                                          +

                                          ¥{$todayOrderAmount|sprintf='%.2f',###} {:$todayOrderRatio>=0?'+':''}{$todayOrderRatio}%

                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          总用户数

                                          +

                                          {$totalUser}

                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          今日新增用户数

                                          +

                                          {$todayUser} {:$todayUserRatio>=0?'+':''}{$todayUserRatio}%

                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          总文档数

                                          + {$totalArchives} 篇 +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          待审核文档

                                          + {$unsettleArchives} 篇 +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          总评论数

                                          + {$totalComment} 条 +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          待审核评论

                                          + {$unsettleComment} 条 +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          + +
                                          +
                                          +
                                          +
                                          +

                                          今日付费文章排行

                                          + + + + + + + + + + {foreach name="todayPaidList" id="item" empty=""} + + + + + + {/foreach} + +
                                          标题金额占比
                                          暂无数据
                                          +

                                          {$item.archives.title}

                                          +
                                          +
                                          {$item.amount}
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          本周付费文章排行

                                          + + + + + + + + + + {foreach name="weekPaidList" id="item" empty=""} + + + + + + {/foreach} + +
                                          标题金额占比
                                          暂无数据
                                          +

                                          {$item.archives.title}

                                          +
                                          +
                                          {$item.amount}
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          本月付费文章排行

                                          + + + + + + + + + + {foreach name="monthPaidList" id="item" empty=""} + + + + + + {/foreach} + +
                                          标题金额占比
                                          暂无数据
                                          +

                                          {$item.archives.title}

                                          +
                                          +
                                          {$item.amount}
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          + +
                                          +
                                          +
                                          +
                                          +

                                          热门搜索

                                          + + + + + + + + + {foreach name="hotSearchList" id="item" empty=" + + + + "} + + + + + {/foreach} + +
                                          关键字搜索次数
                                          暂无数据
                                          +

                                          {$item.keywords}

                                          +
                                          +
                                          {$item.nums}
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          热门标签

                                          + + + + + + + + + {foreach name="hotTagList" id="item" empty=" + + + + "} + + + + + {/foreach} + +
                                          名称文档数量
                                          暂无数据
                                          +

                                          {$item.name}

                                          +
                                          +
                                          {$item.nums}
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          热门文章

                                          + + + + + + + + + {foreach name="hotArchivesList" id="item" empty=" + + + + "} + + + + + {/foreach} + +
                                          标题浏览量
                                          暂无数据
                                          +

                                          {$item.title}

                                          +
                                          +
                                          {$item.views}
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          订单趋势

                                          +
                                          +
                                          + + {:__('Today')} + {:__('Yesterday')} + {:__('Last 7 Days')} + {:__('Last 30 Days')} + {:__('Last month')} + {:__('This month')} + +
                                          + + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          + +
                                          +
                                          +
                                          +
                                          +

                                          今日投稿排行

                                          + + + + + + + + + + {foreach name="todayContributeList" id="item" empty=" + + + + "} + + + + + + {/foreach} + +
                                          昵称数量占比
                                          暂无数据
                                          +

                                          {$item.user.nickname}

                                          +
                                          +
                                          {$item.nums}
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          本周投稿排行

                                          + + + + + + + + + + {foreach name="weekContributeList" id="item" empty=" + + + + "} + + + + + + {/foreach} + +
                                          昵称数量占比
                                          暂无数据
                                          +

                                          {$item.user.nickname}

                                          +
                                          +
                                          {$item.nums}
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +

                                          本月投稿排行

                                          + + + + + + + + + + {foreach name="monthContributeList" id="item" empty=" + + + + "} + + + + + + {/foreach} + +
                                          昵称数量占比
                                          暂无数据
                                          +

                                          {$item.user.nickname}

                                          +
                                          +
                                          {$item.nums}
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          + +
                                          +
                                          +
                                          +
                                          +

                                          管理员发文趋势

                                          +
                                          +
                                          + + {:__('Today')} + {:__('Yesterday')} + {:__('Last 7 Days')} + {:__('Last 30 Days')} + {:__('Last month')} + {:__('This month')} + +
                                          + + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          diff --git a/application/admin/view/cms/tag/add.html b/application/admin/view/cms/tag/add.html new file mode 100644 index 0000000..cd8794b --- /dev/null +++ b/application/admin/view/cms/tag/add.html @@ -0,0 +1,31 @@ +
                                          + +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + + + + +
                                          +
                                          + +
                                          diff --git a/application/admin/view/cms/tag/edit.html b/application/admin/view/cms/tag/edit.html new file mode 100644 index 0000000..b0259f2 --- /dev/null +++ b/application/admin/view/cms/tag/edit.html @@ -0,0 +1,49 @@ +
                                          + +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          + +
                                          + + + + +
                                          +
                                          + +
                                          diff --git a/application/admin/view/cms/tag/index.html b/application/admin/view/cms/tag/index.html new file mode 100644 index 0000000..b375c8b --- /dev/null +++ b/application/admin/view/cms/tag/index.html @@ -0,0 +1,21 @@ +
                                          + {:build_heading()} + +
                                          +
                                          +
                                          +
                                          +
                                          + {:build_toolbar('refresh,delete')} +
                                          + +
                                          +
                                          +
                                          + +
                                          +
                                          +
                                          diff --git a/application/admin/view/command/add.html b/application/admin/view/command/add.html new file mode 100644 index 0000000..01fc38a --- /dev/null +++ b/application/admin/view/command/add.html @@ -0,0 +1,430 @@ + +
                                          + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          + +
                                          +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          + +
                                          +
                                          +
                                          + 主表设置 +
                                          +
                                          + + {:build_select('table',$tableList,null,['class'=>'form-control selectpicker', 'data-live-search'=>'true']);} +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          + +
                                          + +
                                          + +
                                          + 关联表设置 + + +
                                          + +
                                          +
                                          + 字段识别设置 (与之匹配的字段都将生成相应组件) +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          + +
                                          + +
                                          + +
                                          + 生成命令行 + +
                                          + +
                                          + 返回结果 + +
                                          + +
                                          + + +
                                          + +
                                          +
                                          +
                                          +
                                          + +
                                          +
                                          +
                                          +
                                          + +
                                          + 基础设置 +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          +
                                          + +
                                          + 控制器设置 + +
                                          +
                                          + +
                                          +
                                          +
                                          + +
                                          + 生成命令行 + +
                                          + +
                                          + 返回结果 + +
                                          + +
                                          + + +
                                          + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          + +
                                          +
                                          +
                                          + + +
                                          +
                                          +
                                          +
                                          + 文档设置 +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          + + +
                                          +
                                          +
                                          + +
                                          + 生成命令行 + +
                                          + +
                                          + 返回结果 + +
                                          + +
                                          + + +
                                          + +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          +
                                          + diff --git a/application/admin/view/command/detail.html b/application/admin/view/command/detail.html new file mode 100644 index 0000000..24bf12d --- /dev/null +++ b/application/admin/view/command/detail.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                          {:__('Title')}{:__('Content')}
                                          {:__('Type')}{$row.type}({$row.type_text})
                                          {:__('Params')}{$row.params}
                                          {:__('Command')}{$row.command}
                                          {:__('Content')} + +
                                          {:__('Executetime')}{$row.executetime|datetime}
                                          {:__('Status')}{$row.status_text}
                                          + \ No newline at end of file diff --git a/application/admin/view/command/index.html b/application/admin/view/command/index.html new file mode 100644 index 0000000..3b4f823 --- /dev/null +++ b/application/admin/view/command/index.html @@ -0,0 +1,25 @@ +
                                          + {:build_heading()} + +
                                          +
                                          +
                                          + +
                                          + +
                                          +
                                          +
                                          diff --git a/application/admin/view/common/control.html b/application/admin/view/common/control.html new file mode 100644 index 0000000..69bc449 --- /dev/null +++ b/application/admin/view/common/control.html @@ -0,0 +1,77 @@ + + + + diff --git a/application/admin/view/common/header.html b/application/admin/view/common/header.html new file mode 100644 index 0000000..5efa6b0 --- /dev/null +++ b/application/admin/view/common/header.html @@ -0,0 +1,128 @@ + + + + + diff --git a/application/admin/view/common/menu.html b/application/admin/view/common/menu.html new file mode 100644 index 0000000..1e42c01 --- /dev/null +++ b/application/admin/view/common/menu.html @@ -0,0 +1,39 @@ + + diff --git a/application/admin/view/common/meta.html b/application/admin/view/common/meta.html new file mode 100644 index 0000000..ea32c2b --- /dev/null +++ b/application/admin/view/common/meta.html @@ -0,0 +1,26 @@ + +{$title|default=''} + + + + + + + + + +{if $Think.config.fastadmin.adminskin} + +{/if} + + + + + diff --git a/application/admin/view/common/script.html b/application/admin/view/common/script.html new file mode 100644 index 0000000..3d798dc --- /dev/null +++ b/application/admin/view/common/script.html @@ -0,0 +1 @@ +{$VUE_ELEMENTPLUS} \ No newline at end of file diff --git a/application/admin/view/countryflag/add.html b/application/admin/view/countryflag/add.html new file mode 100644 index 0000000..2cf0926 --- /dev/null +++ b/application/admin/view/countryflag/add.html @@ -0,0 +1,59 @@ +
                                          + +
                                          + +
                                          + + +
                                          +
                                          + + + + + + + + + + + + + + + + +
                                          + +
                                          +
                                          + +
                                          + + +
                                          + +
                                          +
                                            +
                                            +
                                            + + +
                                            + + + \ No newline at end of file diff --git a/application/admin/view/countryflag/edit.html b/application/admin/view/countryflag/edit.html new file mode 100644 index 0000000..70f31ca --- /dev/null +++ b/application/admin/view/countryflag/edit.html @@ -0,0 +1,59 @@ +
                                            + +
                                            + +
                                            + + +
                                            +
                                            + + + + + + + + + + + + + + + +
                                            + +
                                            +
                                            + +
                                            + + +
                                            + +
                                            +
                                              +
                                              +
                                              + + +
                                              + + + \ No newline at end of file diff --git a/application/admin/view/countryflag/index.html b/application/admin/view/countryflag/index.html new file mode 100644 index 0000000..49d8ab5 --- /dev/null +++ b/application/admin/view/countryflag/index.html @@ -0,0 +1,35 @@ +
                                              + {:build_heading()} + + +
                                              diff --git a/application/admin/view/dashboard/index.html b/application/admin/view/dashboard/index.html new file mode 100644 index 0000000..0c04396 --- /dev/null +++ b/application/admin/view/dashboard/index.html @@ -0,0 +1,403 @@ + +
                                              +
                                              + {:build_heading(null, false)} + +
                                              +
                                              +
                                              +
                                              + +
                                              +
                                              +
                                              + +
                                              + {$totaluser} + {:__('Total user')} +
                                              +
                                              +
                                              +
                                              +
                                              + +
                                              + {$totaladdon} + {:__('Total addon')} +
                                              +
                                              +
                                              +
                                              +
                                              + +
                                              + {$attachmentnums} + {:__('Total attachment')} +
                                              +
                                              +
                                              +
                                              +
                                              + +
                                              + {$totaladmin} + {:__('Total admin')} +
                                              +
                                              +
                                              +
                                              + +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              {$todayusersignup}
                                              +
                                              {:__('Today user signup')}
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              {$todayuserlogin}
                                              +
                                              {:__('Today user login')}
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              {$threednu}
                                              +
                                              {:__('Three dnu')}
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              {$sevendnu}
                                              +
                                              {:__('Seven dnu')}
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              {$sevendau}
                                              +
                                              {:__('Seven dau')}
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              {$thirtydau}
                                              +
                                              {:__('Thirty dau')}
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              + {:__('Real time')} +
                                              {:__('Working addon count')}
                                              +
                                              +
                                              +
                                              +
                                              +

                                              {$totalworkingaddon}

                                              +
                                              + {:__('Working addon count tips')} +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              + {:__('Real time')} +
                                              {:__('Database count')}
                                              +
                                              +
                                              +
                                              +
                                              +

                                              {$dbtablenums}

                                              +
                                              + {:__('Database table nums')} +
                                              +
                                              +
                                              +

                                              {$dbsize|format_bytes=###,'',0}

                                              +
                                              + {:__('Database size')} +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              + +
                                              +
                                              +
                                              +
                                              + {:__('Real time')} +
                                              {:__('Attachment count')}
                                              +
                                              +
                                              + +
                                              +
                                              +

                                              {$attachmentnums}

                                              +
                                              + {:__('Attachment nums')} +
                                              +
                                              +
                                              +

                                              {$attachmentsize|format_bytes=###,'',0}

                                              +
                                              + {:__('Attachment size')} +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              + {:__('Real time')} +
                                              {:__('Picture count')}
                                              +
                                              +
                                              + +
                                              +
                                              +

                                              {$picturenums}

                                              +
                                              + {:__('Picture nums')} +
                                              +
                                              +
                                              +

                                              {$picturesize|format_bytes=###,'',0}

                                              +
                                              + {:__('Picture size')} +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              + {:__('Custom zone')} +
                                              +
                                              +
                                              +
                                              +
                                              +
                                              diff --git a/application/admin/view/docs/add.html b/application/admin/view/docs/add.html new file mode 100644 index 0000000..449505c --- /dev/null +++ b/application/admin/view/docs/add.html @@ -0,0 +1,79 @@ +
                                              +
                                              + +
                                              +
                                              + + + + 自定义分类 + +
                                              +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              +
                                              + + +
                                              +
                                              +
                                              + +
                                              diff --git a/application/admin/view/docs/edit.html b/application/admin/view/docs/edit.html new file mode 100644 index 0000000..6cb6706 --- /dev/null +++ b/application/admin/view/docs/edit.html @@ -0,0 +1,87 @@ +
                                              + +
                                              + +
                                              +
                                              + + + + 自定义分类 + +
                                              +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              + +
                                              +
                                              +
                                              + +
                                              +
                                              + + +
                                              +
                                              +
                                              + +
                                              diff --git a/application/admin/view/docs/index.html b/application/admin/view/docs/index.html new file mode 100644 index 0000000..888aa6f --- /dev/null +++ b/application/admin/view/docs/index.html @@ -0,0 +1,32 @@ +
                                              +
                                              + {:build_heading(null,FALSE)} + + +
                                              + +
                                              +
                                              +
                                              +
                                              +
                                              + {:build_toolbar('refresh,add,edit,del')} + {:__('Refresh docs')} + {:__('Export HTML')} +
                                              + +
                                              +
                                              +
                                              + +
                                              +
                                              +
                                              diff --git a/application/admin/view/fastim/config/index.html b/application/admin/view/fastim/config/index.html new file mode 100644 index 0000000..bed4bec --- /dev/null +++ b/application/admin/view/fastim/config/index.html @@ -0,0 +1,123 @@ +
                                              + 温馨提示
                                              + 1.插件默认不包含富文本编辑器插件,请在插件市场按需要安装
                                              + 2.部分设置项与用户的Im设置重合,实际以用户的个人设置为准,此处的设置将作为默认值。
                                              + 3.消息推送:https下Web端可使用`Notification API`,无需配置;APP端使用`uniPush`配置请参考文档
                                              +
                                              +
                                              + {:token()} +
                                              +
                                              + +
                                              + +
                                              +
                                              + {foreach $configList as $index=>$vo} +
                                              +
                                              + + + + + + + + + {foreach $vo.list as $item} + + + + + {/foreach} + + + + + + + + +
                                              变量标题变量值
                                              {$item.title} +
                                              +
                                              + {switch $item.type} + {case string} + + {/case} + {case text} + + {/case} + {case editor} + + {/case} + {case number} + + {/case} + {case checkbox} +
                                              + {foreach name="item.content" item="vo"} + + {/foreach} +
                                              + {/case} + {case radio} +
                                              + {foreach name="item.content" item="vo"} + + {/foreach} +
                                              + {/case} + {case value="select" break="0"}{/case} + {case value="selects"} + + {/case} + {case value="image" break="0"}{/case} + {case value="images"} +
                                              + + + + +
                                                +
                                                + {/case} + {case value="file" break="0"}{/case} + {case value="files"} +
                                                + + + + +
                                                + {/case} + {case switch} + + + + + {/case} + {/switch} +
                                                +
                                                +
                                                +
                                                + + +
                                                +
                                                +
                                                + {/foreach} +
                                                +
                                                +
                                                +
                                                \ No newline at end of file diff --git a/application/admin/view/fastim/csr_group/add.html b/application/admin/view/fastim/csr_group/add.html new file mode 100644 index 0000000..c4f54eb --- /dev/null +++ b/application/admin/view/fastim/csr_group/add.html @@ -0,0 +1,40 @@ +
                                                + +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                + +
                                                + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                + +
                                                +
                                                + +
                                                diff --git a/application/admin/view/fastim/csr_group/edit.html b/application/admin/view/fastim/csr_group/edit.html new file mode 100644 index 0000000..ab971ae --- /dev/null +++ b/application/admin/view/fastim/csr_group/edit.html @@ -0,0 +1,40 @@ +
                                                + +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                + +
                                                + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                + +
                                                +
                                                + +
                                                diff --git a/application/admin/view/fastim/csr_group/index.html b/application/admin/view/fastim/csr_group/index.html new file mode 100644 index 0000000..2ce7274 --- /dev/null +++ b/application/admin/view/fastim/csr_group/index.html @@ -0,0 +1,45 @@ +
                                                + +
                                                + {:build_heading(null,FALSE)} + +
                                                + + + +
                                                diff --git a/application/admin/view/fastim/csr_group/recyclebin.html b/application/admin/view/fastim/csr_group/recyclebin.html new file mode 100644 index 0000000..2ff77fe --- /dev/null +++ b/application/admin/view/fastim/csr_group/recyclebin.html @@ -0,0 +1,25 @@ +
                                                + {:build_heading()} + +
                                                +
                                                +
                                                + +
                                                + +
                                                +
                                                +
                                                diff --git a/application/admin/view/fastim/fast_reply/add.html b/application/admin/view/fastim/fast_reply/add.html new file mode 100644 index 0000000..d5dce67 --- /dev/null +++ b/application/admin/view/fastim/fast_reply/add.html @@ -0,0 +1,41 @@ +
                                                + +
                                                + +
                                                + + 如果不设置所属客服,则供所有客服使用 +
                                                +
                                                +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                + +
                                                + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                + +
                                                +
                                                + +
                                                diff --git a/application/admin/view/fastim/fast_reply/edit.html b/application/admin/view/fastim/fast_reply/edit.html new file mode 100644 index 0000000..464a2a8 --- /dev/null +++ b/application/admin/view/fastim/fast_reply/edit.html @@ -0,0 +1,41 @@ +
                                                + +
                                                + +
                                                + + 如果不设置所属客服,则供所有客服使用 +
                                                +
                                                +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                + +
                                                + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                + +
                                                +
                                                + +
                                                diff --git a/application/admin/view/fastim/fast_reply/index.html b/application/admin/view/fastim/fast_reply/index.html new file mode 100644 index 0000000..323459f --- /dev/null +++ b/application/admin/view/fastim/fast_reply/index.html @@ -0,0 +1,44 @@ +
                                                + +
                                                + {:build_heading(null,FALSE)} + +
                                                + + + +
                                                diff --git a/application/admin/view/fastim/fast_reply/recyclebin.html b/application/admin/view/fastim/fast_reply/recyclebin.html new file mode 100644 index 0000000..2256281 --- /dev/null +++ b/application/admin/view/fastim/fast_reply/recyclebin.html @@ -0,0 +1,25 @@ +
                                                + {:build_heading()} + +
                                                +
                                                +
                                                + +
                                                + +
                                                +
                                                +
                                                diff --git a/application/admin/view/fastim/groupchat/edit.html b/application/admin/view/fastim/groupchat/edit.html new file mode 100644 index 0000000..ad19730 --- /dev/null +++ b/application/admin/view/fastim/groupchat/edit.html @@ -0,0 +1,96 @@ +
                                                + +
                                                + +
                                                + +
                                                +
                                                +
                                                + +
                                                +
                                                + +
                                                + + +
                                                + +
                                                +
                                                  +
                                                  +
                                                  +
                                                  + +
                                                  + +
                                                  +
                                                  +
                                                  + +
                                                  + +
                                                  +
                                                  +
                                                  + +
                                                  + +
                                                  +
                                                  +
                                                  + +
                                                  + + + +
                                                  +
                                                  +
                                                  + +
                                                  + + + +
                                                  +
                                                  +
                                                  + +
                                                  + + + +
                                                  +
                                                  +
                                                  + +
                                                  + + + +
                                                  +
                                                  + +
                                                  diff --git a/application/admin/view/fastim/groupchat/index.html b/application/admin/view/fastim/groupchat/index.html new file mode 100644 index 0000000..485856c --- /dev/null +++ b/application/admin/view/fastim/groupchat/index.html @@ -0,0 +1,25 @@ +
                                                  + {:build_heading()} + +
                                                  +
                                                  +
                                                  +
                                                  +
                                                  + + + {:__('Edit')} + + +
                                                  + +
                                                  +
                                                  +
                                                  + +
                                                  +
                                                  +
                                                  diff --git a/application/admin/view/fastim/groupchat/record.html b/application/admin/view/fastim/groupchat/record.html new file mode 100644 index 0000000..50b5ef3 --- /dev/null +++ b/application/admin/view/fastim/groupchat/record.html @@ -0,0 +1,28 @@ + +
                                                    \ No newline at end of file diff --git a/application/admin/view/fastim/groupchat/recyclebin.html b/application/admin/view/fastim/groupchat/recyclebin.html new file mode 100644 index 0000000..9d09c97 --- /dev/null +++ b/application/admin/view/fastim/groupchat/recyclebin.html @@ -0,0 +1,25 @@ +
                                                    + {:build_heading()} + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    diff --git a/application/admin/view/fastim/kbs/add.html b/application/admin/view/fastim/kbs/add.html new file mode 100644 index 0000000..2489eed --- /dev/null +++ b/application/admin/view/fastim/kbs/add.html @@ -0,0 +1,71 @@ +
                                                    + +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + + {:__('Will be used to match user statements')} +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    + +
                                                    diff --git a/application/admin/view/fastim/kbs/edit.html b/application/admin/view/fastim/kbs/edit.html new file mode 100644 index 0000000..d78c833 --- /dev/null +++ b/application/admin/view/fastim/kbs/edit.html @@ -0,0 +1,71 @@ +
                                                    + +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + + {:__('Will be used to match user statements')} +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    + +
                                                    diff --git a/application/admin/view/fastim/kbs/index.html b/application/admin/view/fastim/kbs/index.html new file mode 100644 index 0000000..15e85d9 --- /dev/null +++ b/application/admin/view/fastim/kbs/index.html @@ -0,0 +1,45 @@ +
                                                    + +
                                                    + {:build_heading(null,FALSE)} + +
                                                    + + + +
                                                    diff --git a/application/admin/view/fastim/kbs/recyclebin.html b/application/admin/view/fastim/kbs/recyclebin.html new file mode 100644 index 0000000..da44e70 --- /dev/null +++ b/application/admin/view/fastim/kbs/recyclebin.html @@ -0,0 +1,25 @@ +
                                                    + {:build_heading()} + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    diff --git a/application/admin/view/fastim/records/edit.html b/application/admin/view/fastim/records/edit.html new file mode 100644 index 0000000..c4a8d04 --- /dev/null +++ b/application/admin/view/fastim/records/edit.html @@ -0,0 +1,161 @@ + +
                                                    + +
                                                    + +
                                                    + + + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    +
                                                    + {switch $row.type} + {case default} + {$row.format_message} + {/case} + {case image} + + {/case} + {case audio} + + {/case} + {case video} + + {/case} + {case file} + +
                                                    + {$row.format_message.suffix}文件 +
                                                    +

                                                    {$row.format_message.suffix}文件

                                                    +

                                                    {$row.format_message.size}

                                                    +
                                                    +
                                                    + +
                                                    +
                                                    +
                                                    + {/case} + {case link} + {$row.format_message.link_name ? $row.format_message.link_name:$row.format_message.link_url} +

                                                    网址为:{$row.format_message.link_url} 请注意您的账户和财产安全!

                                                    + {/case} + {case system} +

                                                    {$row.format_message.message}

                                                    + {$row.format_message.display_user == 'all' && $row.group_id > 0 ? '接受人为所有群成员':''} + {/case} + {case group_apply} +

                                                    验证消息:{$row.format_message.note}

                                                    + {/case} + {case group_notice} + {$row.format_message.message} + {/case} + {case group_invitation} + {$row.format_message.message} + {/case} + {case friend_apply} +

                                                    验证消息:{$row.format_message.note}

                                                    + {/case} + {case kbs_list} + {$row.format_message.title} + {/case} + {case group_chat_notice} + {$row.format_message.content} + {/case} + {case voice} + + {/case} + {/switch} +
                                                    +
                                                    +
                                                    + +
                                                    diff --git a/application/admin/view/fastim/records/index.html b/application/admin/view/fastim/records/index.html new file mode 100644 index 0000000..51b1e21 --- /dev/null +++ b/application/admin/view/fastim/records/index.html @@ -0,0 +1,24 @@ +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    diff --git a/application/admin/view/fastim/report/edit.html b/application/admin/view/fastim/report/edit.html new file mode 100644 index 0000000..ccc1f98 --- /dev/null +++ b/application/admin/view/fastim/report/edit.html @@ -0,0 +1,78 @@ +
                                                    + +
                                                    + +
                                                    + + + +
                                                    +
                                                    + +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    + +
                                                    +
                                                    +
                                                    + +
                                                    +
                                                    + +
                                                    + + +
                                                    + +
                                                    +
                                                      +
                                                      +
                                                      +
                                                      + +
                                                      + +
                                                      + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                      + +
                                                      +
                                                      + +
                                                      diff --git a/application/admin/view/fastim/report/index.html b/application/admin/view/fastim/report/index.html new file mode 100644 index 0000000..f0545f8 --- /dev/null +++ b/application/admin/view/fastim/report/index.html @@ -0,0 +1,44 @@ +
                                                      + +
                                                      + {:build_heading(null,FALSE)} + +
                                                      + + + +
                                                      diff --git a/application/admin/view/fastim/session/add.html b/application/admin/view/fastim/session/add.html new file mode 100644 index 0000000..2497f76 --- /dev/null +++ b/application/admin/view/fastim/session/add.html @@ -0,0 +1,46 @@ +
                                                      + +
                                                      + +
                                                      + + + +
                                                      +
                                                      +
                                                      + +
                                                      + +
                                                      +
                                                      +
                                                      + +
                                                      + +
                                                      +
                                                      +
                                                      + +
                                                      + +
                                                      +
                                                      +
                                                      + +
                                                      + +
                                                      +
                                                      + +
                                                      diff --git a/application/admin/view/fastim/session/edit.html b/application/admin/view/fastim/session/edit.html new file mode 100644 index 0000000..738c552 --- /dev/null +++ b/application/admin/view/fastim/session/edit.html @@ -0,0 +1,46 @@ +
                                                      + +
                                                      + +
                                                      + + + +
                                                      +
                                                      +
                                                      + +
                                                      + +
                                                      +
                                                      +
                                                      + +
                                                      + +
                                                      +
                                                      +
                                                      + +
                                                      + +
                                                      +
                                                      +
                                                      + +
                                                      + +
                                                      +
                                                      + +
                                                      diff --git a/application/admin/view/fastim/session/index.html b/application/admin/view/fastim/session/index.html new file mode 100644 index 0000000..4531313 --- /dev/null +++ b/application/admin/view/fastim/session/index.html @@ -0,0 +1,22 @@ +
                                                      + {:build_heading()} + +
                                                      +
                                                      +
                                                      +
                                                      +
                                                      + + +
                                                      + +
                                                      +
                                                      +
                                                      + +
                                                      +
                                                      +
                                                      diff --git a/application/admin/view/fastim/session/record.html b/application/admin/view/fastim/session/record.html new file mode 100644 index 0000000..7c87dd4 --- /dev/null +++ b/application/admin/view/fastim/session/record.html @@ -0,0 +1,47 @@ + +
                                                        +
                                                        +
                                                        +
                                                        +
                                                        +
                                                        +
                                                        \ No newline at end of file diff --git a/application/admin/view/fastim/user/add.html b/application/admin/view/fastim/user/add.html new file mode 100644 index 0000000..a9c1bfa --- /dev/null +++ b/application/admin/view/fastim/user/add.html @@ -0,0 +1,124 @@ +
                                                        + +
                                                        + +
                                                        + + + +
                                                        +
                                                        +
                                                        + +
                                                        + +
                                                        +
                                                        +
                                                        + +
                                                        + +
                                                        +
                                                        +
                                                        + +
                                                        +
                                                        + +
                                                        + + +
                                                        + +
                                                        +
                                                          +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          +
                                                          + +
                                                          + + + +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          + +
                                                          diff --git a/application/admin/view/fastim/user/edit.html b/application/admin/view/fastim/user/edit.html new file mode 100644 index 0000000..8b94c3f --- /dev/null +++ b/application/admin/view/fastim/user/edit.html @@ -0,0 +1,141 @@ +
                                                          + +
                                                          + +
                                                          + + + +
                                                          +
                                                          +
                                                          + +
                                                          + + 仅用户类型为`客服`时,需要选择客服组 +
                                                          +
                                                          +
                                                          + +
                                                          + + {:__('Welcome Msg Tip')} +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          + +
                                                          + +
                                                          +
                                                          + +
                                                          + + +
                                                          + +
                                                          +
                                                            +
                                                            +
                                                            +
                                                            + +
                                                            + +
                                                            +
                                                            +
                                                            + +
                                                            + +
                                                            +
                                                            +
                                                            + +
                                                            + +
                                                            +
                                                            +
                                                            + +
                                                            + +
                                                            +
                                                            +
                                                            + +
                                                            + + + +
                                                            +
                                                            +
                                                            + +
                                                            + +
                                                            +
                                                            +
                                                            + +
                                                            + +
                                                            +
                                                            +
                                                            + +
                                                            + +
                                                            +
                                                            +
                                                            + +
                                                            + + + +
                                                            +
                                                            +
                                                            + +
                                                            + +
                                                            +
                                                            +
                                                            + +
                                                            + +
                                                            +
                                                            + +
                                                            diff --git a/application/admin/view/fastim/user/index.html b/application/admin/view/fastim/user/index.html new file mode 100644 index 0000000..e7127c3 --- /dev/null +++ b/application/admin/view/fastim/user/index.html @@ -0,0 +1,35 @@ +
                                                            + +
                                                            + {:build_heading(null,FALSE)} + +
                                                            + + +
                                                            +
                                                            +
                                                            + +
                                                            + +
                                                            +
                                                            +
                                                            diff --git a/application/admin/view/general/attachment/add.html b/application/admin/view/general/attachment/add.html new file mode 100644 index 0000000..6c63273 --- /dev/null +++ b/application/admin/view/general/attachment/add.html @@ -0,0 +1,62 @@ +
                                                            + {if $config.upload.cdnurl} +
                                                            + +
                                                            + +
                                                              +
                                                              +
                                                              + +
                                                              + +
                                                              +
                                                              + +
                                                              + + {if $config.upload.chunking} + + {/if} +
                                                              +
                                                              + {/if} + +
                                                              + +
                                                              + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                + +
                                                                + + {if $config.upload.chunking} + + {/if} +
                                                                +
                                                                + + +
                                                                diff --git a/application/admin/view/general/attachment/edit.html b/application/admin/view/general/attachment/edit.html new file mode 100644 index 0000000..9ae1b4b --- /dev/null +++ b/application/admin/view/general/attachment/edit.html @@ -0,0 +1,89 @@ + + +
                                                                + +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                + +
                                                                +
                                                                + +
                                                                diff --git a/application/admin/view/general/attachment/index.html b/application/admin/view/general/attachment/index.html new file mode 100644 index 0000000..c06c998 --- /dev/null +++ b/application/admin/view/general/attachment/index.html @@ -0,0 +1,53 @@ +
                                                                + +
                                                                + {:build_heading(null,FALSE)} + +
                                                                + +
                                                                +
                                                                +
                                                                +
                                                                +
                                                                + {:build_toolbar('refresh,add,edit,del')} + {:__('Classify')} +
                                                                + +
                                                                +
                                                                +
                                                                + +
                                                                +
                                                                +
                                                                + + diff --git a/application/admin/view/general/attachment/select.html b/application/admin/view/general/attachment/select.html new file mode 100644 index 0000000..fdc14f2 --- /dev/null +++ b/application/admin/view/general/attachment/select.html @@ -0,0 +1,47 @@ + +
                                                                +
                                                                + {:build_heading(null,FALSE)} + +
                                                                + +
                                                                +
                                                                +
                                                                +
                                                                +
                                                                + {:build_toolbar('refresh')} + + {if request()->get('multiple') == 'true'} + {:__('Choose')} + {/if} +
                                                                + + +
                                                                +
                                                                +
                                                                + +
                                                                +
                                                                +
                                                                diff --git a/application/admin/view/general/config/index.html b/application/admin/view/general/config/index.html new file mode 100644 index 0000000..c8a39ab --- /dev/null +++ b/application/admin/view/general/config/index.html @@ -0,0 +1,356 @@ + +
                                                                +
                                                                + {:build_heading(null, false)} + +
                                                                + +
                                                                +
                                                                + + {foreach $siteList as $index=>$vo} +
                                                                +
                                                                +
                                                                + {:token()} + + + + + + {if $Think.config.app_debug} + + + {/if} + + + + {foreach $vo.list as $item} + + + + {if $Think.config.app_debug} + + + {/if} + + {/foreach} + + + + + + {if $Think.config.app_debug} + + + {/if} + + +
                                                                {:__('Title')}{:__('Value')}{:__('Name')}
                                                                {$item.title} +
                                                                +
                                                                + {switch $item.type} + {case string} + + {/case} + {case password} + + {/case} + {case text} + + {/case} + {case editor} + + {/case} + {case array} +
                                                                +
                                                                + {:isset($item["setting"]["key"])&&$item["setting"]["key"]?$item["setting"]["key"]:__('Array key')} + {:isset($item["setting"]["value"])&&$item["setting"]["value"]?$item["setting"]["value"]:__('Array value')} +
                                                                +
                                                                {:__('Append')}
                                                                + +
                                                                + {/case} + {case date} + + {/case} + {case time} + + {/case} + {case datetime} + + {/case} + {case datetimerange} + + {/case} + {case number} + + {/case} + {case checkbox} +
                                                                + {foreach name="item.content" item="vo"} + + {/foreach} +
                                                                + {/case} + {case radio} +
                                                                + {foreach name="item.content" item="vo"} + + {/foreach} +
                                                                + {/case} + {case value="select" break="0"}{/case} + {case value="selects"} + + {/case} + {case value="image" break="0"}{/case} + {case value="images"} +
                                                                + + + + +
                                                                  +
                                                                  + {/case} + {case value="file" break="0"}{/case} + {case value="files"} +
                                                                  + + + + +
                                                                  + {/case} + {case switch} + + + + + {/case} + {case bool} + + + {/case} + {case city} +
                                                                  + +
                                                                  + {/case} + {case value="selectpage" break="0"}{/case} + {case value="selectpages"} + + {/case} + {case custom} + {$item.extend_html} + {/case} + {/switch} +
                                                                  +
                                                                  +
                                                                  + +
                                                                  {php}echo "{\$site.". $item['name'] . "}";{/php}{if $item['id']>18}{/if}
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + {/foreach} +
                                                                  +
                                                                  + {:token()} +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  + + + + + + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + + + + + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + {if !$Think.config.app_debug} + + {else/} + + + {/if} +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  diff --git a/application/admin/view/general/profile/index.html b/application/admin/view/general/profile/index.html new file mode 100644 index 0000000..ed5ff9d --- /dev/null +++ b/application/admin/view/general/profile/index.html @@ -0,0 +1,118 @@ + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + {:__('Profile')} +
                                                                  +
                                                                  + +
                                                                  + {:token()} + +
                                                                  + +
                                                                  + +
                                                                  {:__('Click to edit')}
                                                                  + +
                                                                  + +

                                                                  {$admin.nickname|htmlentities}

                                                                  + +
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + {:build_toolbar('refresh')} +
                                                                  + + +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  diff --git a/application/admin/view/index/index.html b/application/admin/view/index/index.html new file mode 100644 index 0000000..0fd259a --- /dev/null +++ b/application/admin/view/index/index.html @@ -0,0 +1,56 @@ + + + + + {include file="common/meta" /} + + + +
                                                                  + + + + + + + + +
                                                                  + {if $fixedmenu} +
                                                                  + +
                                                                  + {/if} + {if $referermenu} +
                                                                  + +
                                                                  + {/if} +
                                                                  + + +
                                                                  + + Copyright © 2017-{:date("Y")} {$site.name}. All rights reserved. +
                                                                  + + +
                                                                  + {include file="common/control" /} +
                                                                  + + + {include file="common/script" /} + + diff --git a/application/admin/view/index/login.html b/application/admin/view/index/login.html new file mode 100644 index 0000000..68d4a5f --- /dev/null +++ b/application/admin/view/index/login.html @@ -0,0 +1,143 @@ + + + + {include file="common/meta" /} + + + + {if $background} + + {/if} + + + +
                                                                  + +
                                                                  +{include file="common/script" /} + + diff --git a/application/admin/view/layout/default.html b/application/admin/view/layout/default.html new file mode 100644 index 0000000..4eb4091 --- /dev/null +++ b/application/admin/view/layout/default.html @@ -0,0 +1,45 @@ + + + + {include file="common/meta" /} + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +

                                                                  + {:__('Dashboard')} + {:__('Control panel')} +

                                                                  +
                                                                  + {if !IS_DIALOG && !$Think.config.fastadmin.multiplenav && $Think.config.fastadmin.breadcrumb} + +
                                                                  + + +
                                                                  + + {/if} +
                                                                  + {__CONTENT__} +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + {include file="common/script" /} + + diff --git a/application/admin/view/renzheng/add.html b/application/admin/view/renzheng/add.html new file mode 100644 index 0000000..b15ed2d --- /dev/null +++ b/application/admin/view/renzheng/add.html @@ -0,0 +1,28 @@ +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  diff --git a/application/admin/view/renzheng/edit.html b/application/admin/view/renzheng/edit.html new file mode 100644 index 0000000..8b9f889 --- /dev/null +++ b/application/admin/view/renzheng/edit.html @@ -0,0 +1,35 @@ +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + + + + + + +
                                                                  + +
                                                                  + + {:build_radios('row[status]', ['0'=>'关闭', '1'=>'正常'], $row['status'])} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  diff --git a/application/admin/view/renzheng/index.html b/application/admin/view/renzheng/index.html new file mode 100644 index 0000000..b895424 --- /dev/null +++ b/application/admin/view/renzheng/index.html @@ -0,0 +1,45 @@ +
                                                                  + + + + + + + + + + + + + +
                                                                  diff --git a/application/admin/view/renzheng/php/add.html b/application/admin/view/renzheng/php/add.html new file mode 100644 index 0000000..b15ed2d --- /dev/null +++ b/application/admin/view/renzheng/php/add.html @@ -0,0 +1,28 @@ +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  diff --git a/application/admin/view/renzheng/php/edit.html b/application/admin/view/renzheng/php/edit.html new file mode 100644 index 0000000..f9408b1 --- /dev/null +++ b/application/admin/view/renzheng/php/edit.html @@ -0,0 +1,28 @@ +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  diff --git a/application/admin/view/renzheng/php/index.html b/application/admin/view/renzheng/php/index.html new file mode 100644 index 0000000..1b3b276 --- /dev/null +++ b/application/admin/view/renzheng/php/index.html @@ -0,0 +1,45 @@ +
                                                                  + +
                                                                  + {:build_heading(null,FALSE)} + +
                                                                  + + + +
                                                                  diff --git a/application/admin/view/shopro/activity/activity/activity.html b/application/admin/view/shopro/activity/activity/activity.html new file mode 100644 index 0000000..668657b --- /dev/null +++ b/application/admin/view/shopro/activity/activity/activity.html @@ -0,0 +1,87 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  营销活动
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/activity/activity/add.html b/application/admin/view/shopro/activity/activity/add.html new file mode 100644 index 0000000..24214cd --- /dev/null +++ b/application/admin/view/shopro/activity/activity/add.html @@ -0,0 +1,674 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  +
                                                                  + + + + + + + + + + + + +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/activity/activity/index.html b/application/admin/view/shopro/activity/activity/index.html new file mode 100644 index 0000000..6db896e --- /dev/null +++ b/application/admin/view/shopro/activity/activity/index.html @@ -0,0 +1,106 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  {{state.title}}
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/activity/activity/add')} + 添加 + {/if} + {if $auth->check('shopro/activity/activity/recyclebin')} + 回收站 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/activity/activity/recyclebin.html b/application/admin/view/shopro/activity/activity/recyclebin.html new file mode 100644 index 0000000..ce93efa --- /dev/null +++ b/application/admin/view/shopro/activity/activity/recyclebin.html @@ -0,0 +1,20 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/activity/activity/select.html b/application/admin/view/shopro/activity/activity/select.html new file mode 100644 index 0000000..826c102 --- /dev/null +++ b/application/admin/view/shopro/activity/activity/select.html @@ -0,0 +1,33 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/activity/activity/skus.html b/application/admin/view/shopro/activity/activity/skus.html new file mode 100644 index 0000000..da5c077 --- /dev/null +++ b/application/admin/view/shopro/activity/activity/skus.html @@ -0,0 +1,171 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + {{ ss.name }} + 库存价格销量活动库存 + 活动价格 + + 团长价格 + 操作
                                                                  + {{ st }} + {{ sp.stock }}{{ sp.price }}{{ sp.sales }} + + + + + + + + + +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/activity/groupon/detail.html b/application/admin/view/shopro/activity/groupon/detail.html new file mode 100644 index 0000000..a024b2d --- /dev/null +++ b/application/admin/view/shopro/activity/groupon/detail.html @@ -0,0 +1,96 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {if $auth->check('shopro/activity/groupon/addUser')} + + 添加虚拟人数 + {/if} + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/activity/groupon/index.html b/application/admin/view/shopro/activity/groupon/index.html new file mode 100644 index 0000000..aa65e6a --- /dev/null +++ b/application/admin/view/shopro/activity/groupon/index.html @@ -0,0 +1,105 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  拼团列表
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/mplive/goods/add.html b/application/admin/view/shopro/app/mplive/goods/add.html new file mode 100644 index 0000000..9fd6434 --- /dev/null +++ b/application/admin/view/shopro/app/mplive/goods/add.html @@ -0,0 +1,118 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + +
                                                                  在小程序直播控制台添加的商品,不支持通过此系统操作。
                                                                  + + + 我的小程序 + 其他小程序 + + + + + 添加商品 + +
                                                                  + +
                                                                  +
                                                                  + + +
                                                                  图片尺寸最大300像素*300像素
                                                                  +
                                                                  + + + + + + 一口价 + 价格区间 + 显示折扣价 + + + +
                                                                  {{ + form.model.price_type === 2 ? '最小价格' : '市场价' + }}
                                                                  + + + +
                                                                  {{ + form.model.price_type === 2 ? '最大价格' : '现价' + }}
                                                                  + + + +
                                                                  + + + + + +
                                                                  + +
                                                                  + 请确保小程序页面路径可被访问。例如:pages/goods/index?query=value +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/mplive/index/index.html b/application/admin/view/shopro/app/mplive/index/index.html new file mode 100644 index 0000000..0f28553 --- /dev/null +++ b/application/admin/view/shopro/app/mplive/index/index.html @@ -0,0 +1,252 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + +
                                                                  +
                                                                  +
                                                                  {{ dispatchType === 'live' ? '直播间管理' : '商品库管理' }}
                                                                  + + +
                                                                  +
                                                                  + + + + + + {if $auth->check('shopro/app/mplive/room/sync')} + 同步直播间 + {/if} + {if $auth->check('shopro/app/mplive/room/add')} + 创建直播间 + {/if} +
                                                                  +
                                                                  + + + {if $auth->check('shopro/app/mplive/goods/add')} + 添加商品 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/mplive/room/add.html b/application/admin/view/shopro/app/mplive/room/add.html new file mode 100644 index 0000000..e5f6796 --- /dev/null +++ b/application/admin/view/shopro/app/mplive/room/add.html @@ -0,0 +1,134 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + +
                                                                  + + 手机直播 + 推流设备直播 + +
                                                                  通过“小程序直播”小程序开播
                                                                  +
                                                                  通过第三方推流设备发起直播,请自行定义画面宽高比
                                                                  +
                                                                  +
                                                                  + + + + + +
                                                                  直播间背景图,图片建议像素1080*1920,大小不超过2M
                                                                  +
                                                                  + + +
                                                                  直播间分享图,图片建议像素800*640,大小不超过1M
                                                                  +
                                                                  + + +
                                                                  + 购物直播频道封面图,图片建议像素800*800,大小不超过100KB +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  + 开播时间需要在当前时间的30分钟后 并且 开始时间不能在 6 个月后
                                                                  + 开播时间和结束时间间隔不得短于30分钟,不得超过72小时
                                                                  + 开播时间段仅供参考,不是实际直播间可以开播的时间。
                                                                  + 直播间在开始时间前也可以开播,并且到结束时间后不会强制结束。
                                                                  + 若到结束时间仍未开播,则直播间无法再开播。 +
                                                                  +
                                                                  + + + + +
                                                                  + +
                                                                  + 每个直播间需要绑定一个用作核实主播身份,不会展示给观众。
                                                                  + 主播微信号,如果未实名认证,需要先前往“小程序直播”小程序进行实名验证。 +
                                                                  + + + + + +
                                                                  +
                                                                  + + + + +
                                                                  + +
                                                                  + 开启后本场直播将有可能被官方推荐。
                                                                  + 此项设置在直播间创建完成后可以在控制台内修改。 +
                                                                  +
                                                                  +
                                                                  + + + + 开启 + 关闭 + + + + + 开启 + 关闭 + + + + + 开启 + 关闭 + + + + + 开启 + 关闭 + + + + + 开启 + 关闭 + + +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/mplive/room/playback.html b/application/admin/view/shopro/app/mplive/room/playback.html new file mode 100644 index 0000000..751d49e --- /dev/null +++ b/application/admin/view/shopro/app/mplive/room/playback.html @@ -0,0 +1,68 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + + + + + + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/mplive/room/push_url.html b/application/admin/view/shopro/app/mplive/room/push_url.html new file mode 100644 index 0000000..e3bae0d --- /dev/null +++ b/application/admin/view/shopro/app/mplive/room/push_url.html @@ -0,0 +1,61 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + +
                                                                  推流地址
                                                                  +
                                                                  +
                                                                  在第三方推流应用中,以下地址进行推流
                                                                  + 复制链接 +
                                                                  +
                                                                  {{ state.pushUrl }}
                                                                  +
                                                                  此地址为当前直播间唯一推流地址,不要泄露给第三方。
                                                                  +
                                                                  备注
                                                                  +
                                                                  服务器地址:{{ state.serverAddress }}
                                                                  +
                                                                  串流密钥:{{ state.key }}
                                                                  +
                                                                  +
                                                                  推流直播操作详见
                                                                  + 指引 +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/mplive/room/qrcode.html b/application/admin/view/shopro/app/mplive/room/qrcode.html new file mode 100644 index 0000000..f0f230b --- /dev/null +++ b/application/admin/view/shopro/app/mplive/room/qrcode.html @@ -0,0 +1,64 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  直播间小程序码
                                                                  +
                                                                  小程序码不带参数
                                                                  + 保存图片 +
                                                                  + +
                                                                  +
                                                                  +
                                                                  直播间页面路径
                                                                  +
                                                                  {{ state.path }}
                                                                  + 复制链接 +
                                                                  链接是直播间原始页面路径,如需加入参数,详见使用方法
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/mplive/room/select.html b/application/admin/view/shopro/app/mplive/room/select.html new file mode 100644 index 0000000..786c262 --- /dev/null +++ b/application/admin/view/shopro/app/mplive/room/select.html @@ -0,0 +1,79 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 确 定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/score_shop/add.html b/application/admin/view/shopro/app/score_shop/add.html new file mode 100644 index 0000000..89651fe --- /dev/null +++ b/application/admin/view/shopro/app/score_shop/add.html @@ -0,0 +1,102 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + +
                                                                  商品名称:{{ state.title }}
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + +
                                                                  + {{ ss.name }} + 是否参与商品价格可兑换数量兑换积分兑换价格
                                                                  + {{ st }} + + + {{ sp.price }} + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/score_shop/index.html b/application/admin/view/shopro/app/score_shop/index.html new file mode 100644 index 0000000..c3849ee --- /dev/null +++ b/application/admin/view/shopro/app/score_shop/index.html @@ -0,0 +1,158 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  积分商城
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/app/score_shop/add')} + 添加 + {/if} + {if $auth->check('shopro/app/score_shop/recyclebin')} + 回收站 + {/if} +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/score_shop/recyclebin.html b/application/admin/view/shopro/app/score_shop/recyclebin.html new file mode 100644 index 0000000..dc6b4b2 --- /dev/null +++ b/application/admin/view/shopro/app/score_shop/recyclebin.html @@ -0,0 +1,54 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/app/score_shop/restore')} + 还原 + + {/if} + {if $auth->check('shopro/app/score_shop/destroy')} + 销毁 + + {/if} + {if $auth->check('shopro/app/score_shop/destroy')} + 清空回收站 + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/app/score_shop/select.html b/application/admin/view/shopro/app/score_shop/select.html new file mode 100644 index 0000000..0e9c718 --- /dev/null +++ b/application/admin/view/shopro/app/score_shop/select.html @@ -0,0 +1,68 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + + + + + + + + + 确 定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/category/add.html b/application/admin/view/shopro/category/add.html new file mode 100644 index 0000000..dd93fec --- /dev/null +++ b/application/admin/view/shopro/category/add.html @@ -0,0 +1,275 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + 一级分类 + 二级分类 + 三级分类 + + + +
                                                                  +
                                                                  + + + + +
                                                                  +
                                                                  +
                                                                  样式{{ item.name }}
                                                                  + + + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + +
                                                                  +
                                                                  分类数据
                                                                  + + 插入一级分类 +
                                                                  +
                                                                  +
                                                                  +
                                                                  ID
                                                                  +
                                                                  分类名称
                                                                  +
                                                                  分类图片
                                                                  + +
                                                                  描述
                                                                  +
                                                                  会员折扣
                                                                  +
                                                                  + + +
                                                                  + 权重以倒序排列,默认值为0,相同权重则以ID优先 +
                                                                  +
                                                                  +
                                                                  +
                                                                  操作
                                                                  +
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/category/index.html b/application/admin/view/shopro/category/index.html new file mode 100644 index 0000000..880ae47 --- /dev/null +++ b/application/admin/view/shopro/category/index.html @@ -0,0 +1,75 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  商品分类
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/category/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/category/select.html b/application/admin/view/shopro/category/select.html new file mode 100644 index 0000000..78b5269 --- /dev/null +++ b/application/admin/view/shopro/category/select.html @@ -0,0 +1,21 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/chat/common_word/add.html b/application/admin/view/shopro/chat/common_word/add.html new file mode 100644 index 0000000..59d5c72 --- /dev/null +++ b/application/admin/view/shopro/chat/common_word/add.html @@ -0,0 +1,42 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + +
                                                                  + +
                                                                  +
                                                                  + + + + + + + 正常 + 隐藏 + + +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/chat/common_word/index.html b/application/admin/view/shopro/chat/common_word/index.html new file mode 100644 index 0000000..ca62106 --- /dev/null +++ b/application/admin/view/shopro/chat/common_word/index.html @@ -0,0 +1,97 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  常用语
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/chat/common_word/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/chat/common_word/delete')} + 删除 + + {/if} + {if $auth->check('shopro/chat/common_word/edit')} + 正常 + + {/if} + {if $auth->check('shopro/chat/common_word/edit')} + 隐藏 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/chat/customer_service/add.html b/application/admin/view/shopro/chat/customer_service/add.html new file mode 100644 index 0000000..38b5aa8 --- /dev/null +++ b/application/admin/view/shopro/chat/customer_service/add.html @@ -0,0 +1,42 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/chat/customer_service/index.html b/application/admin/view/shopro/chat/customer_service/index.html new file mode 100644 index 0000000..f407a5a --- /dev/null +++ b/application/admin/view/shopro/chat/customer_service/index.html @@ -0,0 +1,108 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  客服管理
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/chat/customer_service/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/chat/customer_service/delete')} + 删除 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/chat/question/add.html b/application/admin/view/shopro/chat/question/add.html new file mode 100644 index 0000000..723a76b --- /dev/null +++ b/application/admin/view/shopro/chat/question/add.html @@ -0,0 +1,42 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + +
                                                                  + +
                                                                  +
                                                                  + + + + + + + 正常 + 隐藏 + + +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/chat/question/index.html b/application/admin/view/shopro/chat/question/index.html new file mode 100644 index 0000000..74bcbc8 --- /dev/null +++ b/application/admin/view/shopro/chat/question/index.html @@ -0,0 +1,97 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  常见问题
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/chat/question/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/chat/question/delete')} + 删除 + + {/if} + {if $auth->check('shopro/chat/question/edit')} + 正常 + + {/if} + {if $auth->check('shopro/chat/question/edit')} + 隐藏 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/chat/record/index.html b/application/admin/view/shopro/chat/record/index.html new file mode 100644 index 0000000..609ac6e --- /dev/null +++ b/application/admin/view/shopro/chat/record/index.html @@ -0,0 +1,197 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  +
                                                                  {{ state.nickname }}
                                                                  + + +
                                                                  {{ state.room_name }}
                                                                  + + + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/chat/user/index.html b/application/admin/view/shopro/chat/user/index.html new file mode 100644 index 0000000..cbf25c6 --- /dev/null +++ b/application/admin/view/shopro/chat/user/index.html @@ -0,0 +1,109 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  会话管理
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/chat/user/delete')} + 删除 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/agent/detail.html b/application/admin/view/shopro/commission/agent/detail.html new file mode 100644 index 0000000..ddb346a --- /dev/null +++ b/application/admin/view/shopro/commission/agent/detail.html @@ -0,0 +1,601 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + +
                                                                  +
                                                                  + 分销商信息 + + + + + 刷新 + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + {{ + state.data.user + ? state.data.user.nickname + : state.data.user_id + }} +
                                                                  + + + + + {{ state.data.status_text }} + + + + + + + + + + + {if $auth->check('shopro/commission/agent/edit')} + 更换 + {/if} + + +
                                                                  + {{ state.data.level_status_info.name }} +
                                                                  +
                                                                  (等级{{ state.data.level_status || '-' }})
                                                                  + {if $auth->check('shopro/commission/agent/edit')} + 同意 + 拒绝 + {/if} +
                                                                  + + + 更换 + + + +
                                                                  +
                                                                  一级(自购)
                                                                  +
                                                                  + {{ + state.data.level_info + ? state.data.level_info.commission_rules.commission_1 + : '0.00' + }}% +
                                                                  +
                                                                  +
                                                                  +
                                                                  二级
                                                                  +
                                                                  + {{ + state.data.level_info + ? state.data.level_info.commission_rules.commission_2 + : '0.00' + }}% +
                                                                  +
                                                                  +
                                                                  +
                                                                  三级
                                                                  +
                                                                  + {{ + state.data.level_info + ? state.data.level_info.commission_rules.commission_3 + : '0.00' + }}% +
                                                                  +
                                                                  +
                                                                  + + {if $auth->check('shopro/commission/agent/edit')} + + + {/if} + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + 申请信息 +
                                                                  + {if $auth->check('shopro/commission/agent/edit')} + 编辑 +
                                                                  + + 取消 + + 保存 + +
                                                                  + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + + + +
                                                                  + 团队统计 + {if $auth->check('shopro/commission/agent/team')} + 查看团队 + {/if} +
                                                                  + + +
                                                                  团队人数:
                                                                  +
                                                                  + {{ state.data.child_user_count_all }} + +
                                                                  +
                                                                  + +
                                                                  一级团队人数:
                                                                  +
                                                                  + {{ state.data.child_user_count_1 }} + +
                                                                  +
                                                                  + +
                                                                  二级团队人数:
                                                                  +
                                                                  + {{ state.data.child_user_count_2 }} + +
                                                                  +
                                                                  + +
                                                                  + 团队分销商人数: + +
                                                                  + 等级{{ key }}:{{ value }}人 +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + {{ state.data.child_agent_count_all }} + +
                                                                  +
                                                                  + +
                                                                  + 一级分销商人数: + +
                                                                  + 等级{{ key }}:{{ value }}人 +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + {{ state.data.child_agent_count_1 }} + +
                                                                  +
                                                                  + +
                                                                  二级分销商人数:
                                                                  +
                                                                  + {{ state.data.child_agent_count_2 }} + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + 业绩统计 +
                                                                  + + +
                                                                  团队分销总金额:
                                                                  +
                                                                  + {{ state.data.child_order_money_all }} + +
                                                                  +
                                                                  + +
                                                                  一级分销总金额:
                                                                  +
                                                                  + {{ state.data.child_order_money_1 }} + +
                                                                  +
                                                                  + +
                                                                  二级分销总金额:
                                                                  +
                                                                  + {{ state.data.child_order_money_2 }} + +
                                                                  +
                                                                  + +
                                                                  + 自购分销总金额: + + + +
                                                                  +
                                                                  + {{ state.data.child_order_money_0 }} + +
                                                                  +
                                                                  + +
                                                                  团队分销订单:
                                                                  +
                                                                  + {{ state.data.child_order_count_all }} + +
                                                                  +
                                                                  + +
                                                                  一级分销订单:
                                                                  +
                                                                  + {{ state.data.child_order_count_1 }} + +
                                                                  +
                                                                  + +
                                                                  二级分销订单:
                                                                  +
                                                                  + {{ state.data.child_order_count_2 }} + +
                                                                  +
                                                                  + +
                                                                  + 自购分销订单: + + + +
                                                                  +
                                                                  + {{ state.data.child_order_count_0 }} + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + 资产信息 +
                                                                  + + +
                                                                  累计佣金:
                                                                  +
                                                                  + {{ state.data.total_income }} + +
                                                                  +
                                                                  + +
                                                                  待入账佣金:
                                                                  +
                                                                  + {{ state.data.pending_reward }} + +
                                                                  +
                                                                  + +
                                                                  消费金额:
                                                                  +
                                                                  + {{ state.data.user?.total_consume || 0 }} + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/agent/index.html b/application/admin/view/shopro/commission/agent/index.html new file mode 100644 index 0000000..d870a7b --- /dev/null +++ b/application/admin/view/shopro/commission/agent/index.html @@ -0,0 +1,204 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  分销商
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/agent/select.html b/application/admin/view/shopro/commission/agent/select.html new file mode 100644 index 0000000..84f3e71 --- /dev/null +++ b/application/admin/view/shopro/commission/agent/select.html @@ -0,0 +1,81 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + +
                                                                  +
                                                                  + 当前推荐人: + +
                                                                  + + + + +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + 设为平台直推 + + + + {if $auth->check('shopro/commission/agent/changeParentUser')} + 确定 + {/if} + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/agent/team.html b/application/admin/view/shopro/commission/agent/team.html new file mode 100644 index 0000000..f43c474 --- /dev/null +++ b/application/admin/view/shopro/commission/agent/team.html @@ -0,0 +1,159 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/goods/edit.html b/application/admin/view/shopro/commission/goods/edit.html new file mode 100644 index 0000000..356db21 --- /dev/null +++ b/application/admin/view/shopro/commission/goods/edit.html @@ -0,0 +1,353 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + +
                                                                  商品信息
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + {{ goods.title }} +
                                                                  +
                                                                  + {{ goods.is_sku ? '多规格' : '' }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  佣金设置
                                                                  + + + + 不参与 + 参与 + + + + + 不计入 + + + 计入 + + + + + + + + + + 默认规则 + 独立规则 + 批量规则 + + + + + + {{ state.commission_config_temp.status ? '自定义' : '默认' }} + + + + + 一级 + 二级 + 三级 + + + +
                                                                  + + 关闭 + 开启 + +
                                                                  分销自购开启后,分销商自己购买时,下单可以给自己返佣
                                                                  +
                                                                  +
                                                                  + +
                                                                  + + 商品价 + 实际支付价 + +
                                                                  + 商品价: 商品实际售价/规格价,实际支付价:实际支付的费用(不含运费) +
                                                                  +
                                                                  +
                                                                  + + + 支付后结算 + 确认收货结算 + 订单完成结算 + 手动打款 + + +
                                                                  + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/goods/index.html b/application/admin/view/shopro/commission/goods/index.html new file mode 100644 index 0000000..0d080b2 --- /dev/null +++ b/application/admin/view/shopro/commission/goods/index.html @@ -0,0 +1,116 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  分销商品
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/commission/goods/edit')} + 设置佣金 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/level/add.html b/application/admin/view/shopro/commission/level/add.html new file mode 100644 index 0000000..8cf5029 --- /dev/null +++ b/application/admin/view/shopro/commission/level/add.html @@ -0,0 +1,187 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + +
                                                                  等级佣金比例
                                                                  + +
                                                                  + {{ item.name }} +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/level/index.html b/application/admin/view/shopro/commission/level/index.html new file mode 100644 index 0000000..b245efa --- /dev/null +++ b/application/admin/view/shopro/commission/level/index.html @@ -0,0 +1,60 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  分销商等级
                                                                  +
                                                                  +
                                                                  + + {if $auth->check('shopro/commission/level/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/level/select.html b/application/admin/view/shopro/commission/level/select.html new file mode 100644 index 0000000..5628bae --- /dev/null +++ b/application/admin/view/shopro/commission/level/select.html @@ -0,0 +1,29 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/log/index.html b/application/admin/view/shopro/commission/log/index.html new file mode 100644 index 0000000..47e530c --- /dev/null +++ b/application/admin/view/shopro/commission/log/index.html @@ -0,0 +1,56 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  分销动态
                                                                  +
                                                                  + 实时动态 + + + {{ really.reallyStatus == 1 ? '开启' : '关闭' }} + + + + +
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/order/index.html b/application/admin/view/shopro/commission/order/index.html new file mode 100644 index 0000000..16fe5d6 --- /dev/null +++ b/application/admin/view/shopro/commission/order/index.html @@ -0,0 +1,513 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  分销订单
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/commission/order/export')} + 订单导出 + + {/if} +
                                                                  +
                                                                  + + +
                                                                  {{ state.count.total }}
                                                                  +
                                                                  商品总订单数
                                                                  +
                                                                  + +
                                                                  + {{ state.count.total_amount?.toFixed(2) }} +
                                                                  +
                                                                  商品结算总金额
                                                                  +
                                                                  + +
                                                                  + {{ state.count.total_commission?.toFixed(2) }} +
                                                                  +
                                                                  分佣总金额
                                                                  +
                                                                  + +
                                                                  + {{ state.count.total_commission_cancel?.toFixed(2) }} +
                                                                  +
                                                                  已取消佣金
                                                                  +
                                                                  + +
                                                                  + {{ state.count.total_commission_back?.toFixed(2) }} +
                                                                  +
                                                                  已退回佣金
                                                                  +
                                                                  + +
                                                                  + {{ state.count.total_commission_pending?.toFixed(2) }} +
                                                                  +
                                                                  未结算佣金
                                                                  +
                                                                  + +
                                                                  + {{ state.count.total_commission_accounted?.toFixed(2) }} +
                                                                  +
                                                                  已结算佣金
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/commission/reward/index.html b/application/admin/view/shopro/commission/reward/index.html new file mode 100644 index 0000000..14fc29a --- /dev/null +++ b/application/admin/view/shopro/commission/reward/index.html @@ -0,0 +1,59 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  佣金明细
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/commission/reward/export')} + 导出 + + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/common/script.html b/application/admin/view/shopro/common/script.html new file mode 100644 index 0000000..6277534 --- /dev/null +++ b/application/admin/view/shopro/common/script.html @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application/admin/view/shopro/config/index.html b/application/admin/view/shopro/config/index.html new file mode 100644 index 0000000..2a6ebcc --- /dev/null +++ b/application/admin/view/shopro/config/index.html @@ -0,0 +1,1135 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  支付配置
                                                                  +
                                                                  + + {if $auth->check('shopro/pay_config/add')} + 添加 + {/if} + {if $auth->check('shopro/pay_config/recyclebin')} + 回收站 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + + 确定 + + + + +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + + + + 刷新 +
                                                                  +
                                                                  去升级 + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/config/platform.html b/application/admin/view/shopro/config/platform.html new file mode 100644 index 0000000..304ebe2 --- /dev/null +++ b/application/admin/view/shopro/config/platform.html @@ -0,0 +1,181 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + +
                                                                  状态
                                                                  + + + + {{form.model.status == 0 ? '关闭' : '开启'}} + +
                                                                  支付配置启用货到付款后,请自行安排合作快递完成收款和结算
                                                                  + + + 微信 + 支付宝 + 余额 + 货到付款 + + + +
                                                                  + + + + + 添加支付方式 + +
                                                                  +
                                                                  + +
                                                                  + + + + + 添加支付方式 + +
                                                                  +
                                                                  +
                                                                  + {{state.platform == 'H5' ? '微信H5' : state.label}}平台设置 +
                                                                  + 如使用微信支付,请在此输入已开通微信H5支付的Appid +
                                                                  +
                                                                  + +
                                                                  + + 查看配置引导 +
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  微信登录设置
                                                                  + +
                                                                  + + + + {{form.model.status == 0 ? '关闭' : '开启'}} +
                                                                  进入应用后立即自动发起授权登录
                                                                  +
                                                                  +
                                                                  + +
                                                                  + + + + {{form.model.status == 0 ? '关闭' : '开启'}} +
                                                                  授权登录后立即提醒绑定手机号
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  APP下载
                                                                  + + + + + + + + + + + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/coupon/add.html b/application/admin/view/shopro/coupon/add.html new file mode 100644 index 0000000..f2c2ae2 --- /dev/null +++ b/application/admin/view/shopro/coupon/add.html @@ -0,0 +1,210 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + 满减券 + 折扣券 + + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + + + + + + + + + + + + + + + + 相对天数 + 固定区间 + + +
                                                                  + + + + + + +
                                                                  + +
                                                                  +
                                                                  + + +
                                                                  + {{form.model.is_double_discount == 1?'开启':'关闭'}} +
                                                                  +
                                                                  +
                                                                  开启优惠叠加,优惠券将可以和活动一起使用
                                                                  +
                                                                  +
                                                                  + +
                                                                  + + 公开发放 + 后台发放 + 禁止使用 + +
                                                                  + 后台发放状态改为别的状态,将导致满赠活动无法赠送该优惠券 +
                                                                  +
                                                                  +
                                                                  + + + 全场通用 + 指定商品可用 + 指定商品不可用 + 指定分类可用 + + + + 选择商品 + 选择分类 + + + +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/coupon/index.html b/application/admin/view/shopro/coupon/index.html new file mode 100644 index 0000000..4b82e91 --- /dev/null +++ b/application/admin/view/shopro/coupon/index.html @@ -0,0 +1,206 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + +
                                                                  +
                                                                  +
                                                                  {{value.num}}
                                                                  +
                                                                  +
                                                                  +
                                                                  {{value.name}}
                                                                  + + {{value.tip}} + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  优惠券
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/coupon/add')} + 添加 + {/if} + {if $auth->check('shopro/coupon/recyclebin')} + 回收站 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/coupon/recyclebin.html b/application/admin/view/shopro/coupon/recyclebin.html new file mode 100644 index 0000000..c581874 --- /dev/null +++ b/application/admin/view/shopro/coupon/recyclebin.html @@ -0,0 +1,54 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/coupon/restore')} + 还原 + + {/if} + {if $auth->check('shopro/coupon/destroy')} + 销毁 + + {/if} + {if $auth->check('shopro/coupon/destroy')} + 清空回收站 + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/coupon/select.html b/application/admin/view/shopro/coupon/select.html new file mode 100644 index 0000000..de00d38 --- /dev/null +++ b/application/admin/view/shopro/coupon/select.html @@ -0,0 +1,79 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 确 定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/dashboard/index.html b/application/admin/view/shopro/dashboard/index.html new file mode 100644 index 0000000..fe78603 --- /dev/null +++ b/application/admin/view/shopro/dashboard/index.html @@ -0,0 +1,382 @@ +{include file="/shopro/common/script" /} + + + + + +
                                                                  + + + + {if $auth->check('shopro/dashboard/total')} + + + +
                                                                  +
                                                                  +
                                                                  {{state.total.user.title}} {{state.total.user.data.total}}
                                                                  +
                                                                  {{state.total.user.tip}} {{state.total.user.data.today}}
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  {{state.total.agent.title}} {{state.total.agent.data.total}}
                                                                  +
                                                                  {{state.total.agent.tip}} {{state.total.agent.data.today}}
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  {{state.total.share.title}} {{state.total.share.data.total}}
                                                                  +
                                                                  {{state.total.share.tip}} {{state.total.share.data.today}}
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + + +
                                                                  + {/if} + {if $auth->check('shopro/dashboard/chart')} + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + {{ value }} +
                                                                  +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  + +
                                                                  + 详情 + + + + +
                                                                  +
                                                                  +
                                                                  {{ value.num }}
                                                                  +
                                                                  + {{ value.text }} + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + {/if} + {if $auth->check('shopro/dashboard/ranking')} + + +
                                                                  +
                                                                  + 销量榜 +
                                                                  + + + + + + + + + + + +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + 热搜榜 +
                                                                  + + + + + + + + + + + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + {/if} +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/area/add.html b/application/admin/view/shopro/data/area/add.html new file mode 100644 index 0000000..6d1d3bf --- /dev/null +++ b/application/admin/view/shopro/data/area/add.html @@ -0,0 +1,29 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/area/index.html b/application/admin/view/shopro/data/area/index.html new file mode 100644 index 0000000..873e7a4 --- /dev/null +++ b/application/admin/view/shopro/data/area/index.html @@ -0,0 +1,108 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  省市区
                                                                  +
                                                                  + 数据来源: + + 2022年国家统计局区划代码 + + 更新时间2022-10-31 +
                                                                  +
                                                                  +
                                                                  + + {if $auth->check('shopro/data/area/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  名称
                                                                  +
                                                                  级别
                                                                  +
                                                                  操作
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/area/select.html b/application/admin/view/shopro/data/area/select.html new file mode 100644 index 0000000..8bf78a0 --- /dev/null +++ b/application/admin/view/shopro/data/area/select.html @@ -0,0 +1,19 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/express/add.html b/application/admin/view/shopro/data/express/add.html new file mode 100644 index 0000000..fe528c5 --- /dev/null +++ b/application/admin/view/shopro/data/express/add.html @@ -0,0 +1,24 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/express/index.html b/application/admin/view/shopro/data/express/index.html new file mode 100644 index 0000000..c02a376 --- /dev/null +++ b/application/admin/view/shopro/data/express/index.html @@ -0,0 +1,77 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  快递公司
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/data/express/add')} + 添加 + {/if} +
                                                                  +
                                                                  + + + +
                                                                  + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/data/express/delete')} + 删除 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/fake_user/add.html b/application/admin/view/shopro/data/fake_user/add.html new file mode 100644 index 0000000..30aef36 --- /dev/null +++ b/application/admin/view/shopro/data/fake_user/add.html @@ -0,0 +1,45 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ form.model.createtime }} + + + {{ form.model.updatetime }} + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/fake_user/index.html b/application/admin/view/shopro/data/fake_user/index.html new file mode 100644 index 0000000..7e91132 --- /dev/null +++ b/application/admin/view/shopro/data/fake_user/index.html @@ -0,0 +1,96 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  虚拟用户
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/data/fake_user/add')} + 添加 + {/if} + {if $auth->check('shopro/data/fake_user/random')} + 自动生成 + {/if} +
                                                                  +
                                                                  + + + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/data/fake_user/delete')} + 删除 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/fake_user/random.html b/application/admin/view/shopro/data/fake_user/random.html new file mode 100644 index 0000000..5665f6a --- /dev/null +++ b/application/admin/view/shopro/data/fake_user/random.html @@ -0,0 +1,18 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/fake_user/select.html b/application/admin/view/shopro/data/fake_user/select.html new file mode 100644 index 0000000..491a317 --- /dev/null +++ b/application/admin/view/shopro/data/fake_user/select.html @@ -0,0 +1,31 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/faq/add.html b/application/admin/view/shopro/data/faq/add.html new file mode 100644 index 0000000..225738f --- /dev/null +++ b/application/admin/view/shopro/data/faq/add.html @@ -0,0 +1,28 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + 正常 + 隐藏 + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/faq/index.html b/application/admin/view/shopro/data/faq/index.html new file mode 100644 index 0000000..1066b20 --- /dev/null +++ b/application/admin/view/shopro/data/faq/index.html @@ -0,0 +1,97 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  常见问题
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/data/faq/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/data/faq/delete')} + 删除 + + {/if} + {if $auth->check('shopro/data/faq/edit')} + 正常 + + {/if} + {if $auth->check('shopro/data/faq/edit')} + 隐藏 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/page/add.html b/application/admin/view/shopro/data/page/add.html new file mode 100644 index 0000000..35dd72e --- /dev/null +++ b/application/admin/view/shopro/data/page/add.html @@ -0,0 +1,24 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/page/index.html b/application/admin/view/shopro/data/page/index.html new file mode 100644 index 0000000..396d152 --- /dev/null +++ b/application/admin/view/shopro/data/page/index.html @@ -0,0 +1,86 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  页面链接
                                                                  +
                                                                  +
                                                                  + + {if $auth->check('shopro/data/page/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/data/page/delete')} + 删除 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/page/select.html b/application/admin/view/shopro/data/page/select.html new file mode 100644 index 0000000..728c66f --- /dev/null +++ b/application/admin/view/shopro/data/page/select.html @@ -0,0 +1,123 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + +
                                                                  +
                                                                  {{ g.group }}
                                                                  +
                                                                  +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  {{ g.group }}
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/richtext/add.html b/application/admin/view/shopro/data/richtext/add.html new file mode 100644 index 0000000..77104b8 --- /dev/null +++ b/application/admin/view/shopro/data/richtext/add.html @@ -0,0 +1,23 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/richtext/index.html b/application/admin/view/shopro/data/richtext/index.html new file mode 100644 index 0000000..3f0af89 --- /dev/null +++ b/application/admin/view/shopro/data/richtext/index.html @@ -0,0 +1,75 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  富文本
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/data/richtext/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/data/richtext/delete')} + 删除 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/data/richtext/select.html b/application/admin/view/shopro/data/richtext/select.html new file mode 100644 index 0000000..dadc08a --- /dev/null +++ b/application/admin/view/shopro/data/richtext/select.html @@ -0,0 +1,43 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  + 添加 +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/decorate/designer/index.html b/application/admin/view/shopro/decorate/designer/index.html new file mode 100644 index 0000000..c68597b --- /dev/null +++ b/application/admin/view/shopro/decorate/designer/index.html @@ -0,0 +1,118 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  设计师模板
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/decorate/page/index.html b/application/admin/view/shopro/decorate/page/index.html new file mode 100644 index 0000000..fdd5de2 --- /dev/null +++ b/application/admin/view/shopro/decorate/page/index.html @@ -0,0 +1,4290 @@ +{include file="/shopro/common/script" /} + + + + + + + + + + + + + + + +
                                                                  + + +
                                                                  + +
                                                                  +
                                                                  + + + + + + + + + + + +
                                                                  +
                                                                  + + + + + + +
                                                                  +
                                                                  + + +
                                                                  + + + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  + + + + + + +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  + +
                                                                  + + + + + +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + +
                                                                  + +
                                                                  标题栏
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + + +
                                                                  + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/decorate/page/preview.html b/application/admin/view/shopro/decorate/page/preview.html new file mode 100644 index 0000000..d5e59bd --- /dev/null +++ b/application/admin/view/shopro/decorate/page/preview.html @@ -0,0 +1,152 @@ +{include file="/shopro/common/script" /} + + + + + +
                                                                  + + + + + +
                                                                  +
                                                                  此为预览效果,实际效果请扫码查看
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  {{ state.detail?.name }}
                                                                  + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/decorate/template/add.html b/application/admin/view/shopro/decorate/template/add.html new file mode 100644 index 0000000..ad545fc --- /dev/null +++ b/application/admin/view/shopro/decorate/template/add.html @@ -0,0 +1,26 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + {{item.label}} + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/decorate/template/index.html b/application/admin/view/shopro/decorate/template/index.html new file mode 100644 index 0000000..813b7e9 --- /dev/null +++ b/application/admin/view/shopro/decorate/template/index.html @@ -0,0 +1,167 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  模板管理
                                                                  +
                                                                  +
                                                                  + + {if $auth->check('shopro/decorate/template/add')} + 添加 + {/if} + {if $auth->check('shopro/decorate/template/recyclebin')} + 回收站 + {/if} +
                                                                  +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/decorate/template/recyclebin.html b/application/admin/view/shopro/decorate/template/recyclebin.html new file mode 100644 index 0000000..26f18ae --- /dev/null +++ b/application/admin/view/shopro/decorate/template/recyclebin.html @@ -0,0 +1,54 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/decorate/template/restore')} + 还原 + + {/if} + {if $auth->check('shopro/decorate/template/destroy')} + 销毁 + + {/if} + {if $auth->check('shopro/decorate/template/destroy')} + 清空回收站 + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/decorate/template/select.html b/application/admin/view/shopro/decorate/template/select.html new file mode 100644 index 0000000..3fbdd56 --- /dev/null +++ b/application/admin/view/shopro/decorate/template/select.html @@ -0,0 +1,24 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/dispatch/dispatch/add.html b/application/admin/view/shopro/dispatch/dispatch/add.html new file mode 100644 index 0000000..b920efa --- /dev/null +++ b/application/admin/view/shopro/dispatch/dispatch/add.html @@ -0,0 +1,178 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/dispatch/dispatch/index.html b/application/admin/view/shopro/dispatch/dispatch/index.html new file mode 100644 index 0000000..6e83e3d --- /dev/null +++ b/application/admin/view/shopro/dispatch/dispatch/index.html @@ -0,0 +1,180 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  配送设置
                                                                  +
                                                                  +
                                                                  + + {if $auth->check('shopro/dispatch/dispatch/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  #{{ item.id }}
                                                                  +
                                                                  {{ item.name }}
                                                                  +
                                                                  +
                                                                  +
                                                                  最后编辑时间:{{ item.updatetime }}
                                                                  + {if $auth->check('shopro/dispatch/dispatch/edit')} + 编辑 + {/if} + {if $auth->check('shopro/dispatch/dispatch/add')} + 复制 + {/if} + + + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + +
                                                                  + + + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/feedback/detail.html b/application/admin/view/shopro/feedback/detail.html new file mode 100644 index 0000000..ce0e0d4 --- /dev/null +++ b/application/admin/view/shopro/feedback/detail.html @@ -0,0 +1,46 @@ +{include file="/shopro/common/script" /} + + \ No newline at end of file diff --git a/application/admin/view/shopro/feedback/index.html b/application/admin/view/shopro/feedback/index.html new file mode 100644 index 0000000..33a9097 --- /dev/null +++ b/application/admin/view/shopro/feedback/index.html @@ -0,0 +1,123 @@ +{include file="/shopro/common/script" /} + + \ No newline at end of file diff --git a/application/admin/view/shopro/goods/comment/add.html b/application/admin/view/shopro/goods/comment/add.html new file mode 100644 index 0000000..6559c49 --- /dev/null +++ b/application/admin/view/shopro/goods/comment/add.html @@ -0,0 +1,248 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + + + + + 选择虚拟用户 + +
                                                                  +
                                                                  +
                                                                  {{ state.fakeUserData?.id }}
                                                                  +
                                                                  {{ state.fakeUserData?.nickname }}
                                                                  +
                                                                  + + 移除 + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 选择商品 + +
                                                                  +
                                                                  +
                                                                  {{ form.model.goods_id }}
                                                                  +
                                                                  + +
                                                                  +
                                                                  + {{ state.goodsData?.title }} +
                                                                  +
                                                                  + ¥{{ state.goodsData?.price.join('~') || 0 }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  + 移除 +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + 显示 + 隐藏 + + +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  + + + + +
                                                                  评价信息
                                                                  +
                                                                  +
                                                                  用户昵称:
                                                                  +
                                                                  {{form.model.user?.nickname || form.model.user_id}}
                                                                  +
                                                                  +
                                                                  +
                                                                  评价星级:
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  显示状态:
                                                                  +
                                                                  + + 正常 + 隐藏 + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  商品信息
                                                                  +
                                                                  + +
                                                                  +
                                                                  + {{ form.model.order_item.goods_title }} +
                                                                  +
                                                                  +
                                                                  单价:
                                                                  +
                                                                  {{ form.model.order_item.goods_price }}×{{ + form.model.order_item.goods_num }}
                                                                  +
                                                                  +
                                                                  +
                                                                  规格:
                                                                  +
                                                                  {{ form.model.order_item.goods_sku_text }},
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  {{form.model.goods.title}}
                                                                  +
                                                                  +
                                                                  单价:
                                                                  +
                                                                  + {{form.model.goods.price.join(',')}} +
                                                                  +
                                                                  +
                                                                  +
                                                                  规格:
                                                                  +
                                                                  {{form.model.goods.sku_text }}
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  评价记录
                                                                  + {if $auth->check('shopro/goods/comment/reply')} + 点击回复 + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  {{ form.model.admin.nickname }}
                                                                  +
                                                                  {{ form.model.reply_time }}
                                                                  +
                                                                  {{ form.model.reply_content }}
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  {{ form.model.user.nickname }}
                                                                  +
                                                                  {{ form.model.createtime }}
                                                                  +
                                                                  {{ form.model.content }}
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/comment/index.html b/application/admin/view/shopro/goods/comment/index.html new file mode 100644 index 0000000..c9bcc2f --- /dev/null +++ b/application/admin/view/shopro/goods/comment/index.html @@ -0,0 +1,178 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  评价管理
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/goods/comment/add')} + 添加虚拟评价 + {/if} + {if $auth->check('shopro/goods/comment/recyclebin')} + 回收站 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/goods/comment/delete')} + 删除 + + {/if} + {if $auth->check('shopro/goods/comment/edit')} + 正常 + + {/if} + {if $auth->check('shopro/goods/comment/edit')} + 隐藏 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/comment/recyclebin.html b/application/admin/view/shopro/goods/comment/recyclebin.html new file mode 100644 index 0000000..54c9537 --- /dev/null +++ b/application/admin/view/shopro/goods/comment/recyclebin.html @@ -0,0 +1,54 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/goods/comment/restore')} + 还原 + + {/if} + {if $auth->check('shopro/goods/comment/destroy')} + 销毁 + + {/if} + {if $auth->check('shopro/goods/comment/destroy')} + 清空回收站 + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/comment/reply.html b/application/admin/view/shopro/goods/comment/reply.html new file mode 100644 index 0000000..3e2ee46 --- /dev/null +++ b/application/admin/view/shopro/goods/comment/reply.html @@ -0,0 +1,18 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/goods/add.html b/application/admin/view/shopro/goods/goods/add.html new file mode 100644 index 0000000..f469775 --- /dev/null +++ b/application/admin/view/shopro/goods/goods/add.html @@ -0,0 +1,928 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + + +
                                                                  + + + + + + + + +
                                                                  作用于商城列表、分享图片;建议尺寸:750*750 px
                                                                  +
                                                                  + + +
                                                                  + 作用于商品详情顶部轮播显示,
                                                                  轮播图可以拖拽调整顺序 +
                                                                  +
                                                                  + +
                                                                  + + + + + + + + + {if $auth->check('shopro/category/add')} + 添加商品分类 + {/if} +
                                                                  +
                                                                  + + + + + + + 不限购 + 每日 + 累计 + + + + + + + + + + 上架 + 隐藏 + 下架 + + + +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + + 单规格 + 多规格 + +
                                                                  + 如商品参与了拼团、秒杀、积分等活动,切换规格,可能导致活动规格不可用 +
                                                                  +
                                                                  +
                                                                  + + + + + +
                                                                  +
                                                                  精确显示
                                                                  + + + + +
                                                                  +
                                                                  + +
                                                                  +
                                                                  粗略显示
                                                                  + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + + +
                                                                  可以提高商品的销量排行榜,鼓励用户下单
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  + + +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + + + + {if $auth->check('shopro/goods/service/add')} + + 添加服务保障 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  参数名称
                                                                  +
                                                                  内容
                                                                  +
                                                                  操作
                                                                  +
                                                                  + + + + 添加 + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/goods/add_stock.html b/application/admin/view/shopro/goods/goods/add_stock.html new file mode 100644 index 0000000..2e454ad --- /dev/null +++ b/application/admin/view/shopro/goods/goods/add_stock.html @@ -0,0 +1,153 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + +
                                                                  +
                                                                  {{ state.stock }}
                                                                  + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  规格值图片价格(元)划线价格成本价 +
                                                                  +
                                                                  库存(件)
                                                                  + + + +
                                                                  + 取消 + 确定 +
                                                                  +
                                                                  +
                                                                  +
                                                                  库存预警(件)重量(kg)规格编码商品状态
                                                                  {{ item.goods_sku_text.join('/') }} + + {{ item.price }}{{ item.original_price }}{{ item.cost_price }} +
                                                                  +
                                                                  {{ item.stock }}
                                                                  + + +
                                                                  +
                                                                  {{ item.stock_warning }}{{ item.weight }}{{ item.sn }} + {{ item.status == 'up' ? `${item.status_text}中` : `已${item.status_text}` }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/goods/index.html b/application/admin/view/shopro/goods/goods/index.html new file mode 100644 index 0000000..c2361a2 --- /dev/null +++ b/application/admin/view/shopro/goods/goods/index.html @@ -0,0 +1,364 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  商品库
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/goods/goods/add')} + 添加 + {/if} + {if $auth->check('shopro/goods/goods/recyclebin')} + 回收站 + {/if} +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/goods/goods/delete')} + 删除 + + {/if} + {if $auth->check('shopro/goods/goods/edit')} + 上架 + + {/if} + {if $auth->check('shopro/goods/goods/edit')} + 下架 + + {/if} + {if $auth->check('shopro/goods/goods/edit')} + 隐藏 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/goods/recyclebin.html b/application/admin/view/shopro/goods/goods/recyclebin.html new file mode 100644 index 0000000..000a501 --- /dev/null +++ b/application/admin/view/shopro/goods/goods/recyclebin.html @@ -0,0 +1,54 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/goods/goods/restore')} + 还原 + + {/if} + {if $auth->check('shopro/goods/goods/destroy')} + 销毁 + + {/if} + {if $auth->check('shopro/goods/goods/destroy')} + 清空回收站 + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/goods/select.html b/application/admin/view/shopro/goods/goods/select.html new file mode 100644 index 0000000..ca3d1d5 --- /dev/null +++ b/application/admin/view/shopro/goods/goods/select.html @@ -0,0 +1,168 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + + +
                                                                  + 商品价格 + + + + + +
                                                                  + +
                                                                  + + + + + + + + + + + + + + + + 确 定 + +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/service/add.html b/application/admin/view/shopro/goods/service/add.html new file mode 100644 index 0000000..45e32d6 --- /dev/null +++ b/application/admin/view/shopro/goods/service/add.html @@ -0,0 +1,24 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/service/index.html b/application/admin/view/shopro/goods/service/index.html new file mode 100644 index 0000000..844c11c --- /dev/null +++ b/application/admin/view/shopro/goods/service/index.html @@ -0,0 +1,83 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  服务保障
                                                                  +
                                                                  +
                                                                  + + {if $auth->check('shopro/goods/service/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/goods/service/delete')} + 删除 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/stock_log/index.html b/application/admin/view/shopro/goods/stock_log/index.html new file mode 100644 index 0000000..35027fc --- /dev/null +++ b/application/admin/view/shopro/goods/stock_log/index.html @@ -0,0 +1,51 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  补货记录
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/stock_warning/add_stock.html b/application/admin/view/shopro/goods/stock_warning/add_stock.html new file mode 100644 index 0000000..2d16aec --- /dev/null +++ b/application/admin/view/shopro/goods/stock_warning/add_stock.html @@ -0,0 +1,23 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + +
                                                                  +
                                                                  {{ state.stock }}
                                                                  + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/stock_warning/index.html b/application/admin/view/shopro/goods/stock_warning/index.html new file mode 100644 index 0000000..dc3e640 --- /dev/null +++ b/application/admin/view/shopro/goods/stock_warning/index.html @@ -0,0 +1,60 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + +
                                                                  +
                                                                  +
                                                                  库存预警
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/goods/stock_warning/recyclebin')} + 历史记录 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/goods/stock_warning/recyclebin.html b/application/admin/view/shopro/goods/stock_warning/recyclebin.html new file mode 100644 index 0000000..28302ec --- /dev/null +++ b/application/admin/view/shopro/goods/stock_warning/recyclebin.html @@ -0,0 +1,20 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/notification/config/edit.html b/application/admin/view/shopro/notification/config/edit.html new file mode 100644 index 0000000..c885b79 --- /dev/null +++ b/application/admin/view/shopro/notification/config/edit.html @@ -0,0 +1,154 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + 默认配置 + 自定义配置 + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/notification/config/index.html b/application/admin/view/shopro/notification/config/index.html new file mode 100644 index 0000000..acbe9f5 --- /dev/null +++ b/application/admin/view/shopro/notification/config/index.html @@ -0,0 +1,286 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  消息配置
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + + + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/aftersale/add_log.html b/application/admin/view/shopro/order/aftersale/add_log.html new file mode 100644 index 0000000..4c315f5 --- /dev/null +++ b/application/admin/view/shopro/order/aftersale/add_log.html @@ -0,0 +1,22 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/aftersale/detail.html b/application/admin/view/shopro/order/aftersale/detail.html new file mode 100644 index 0000000..fb3d44c --- /dev/null +++ b/application/admin/view/shopro/order/aftersale/detail.html @@ -0,0 +1,417 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + +
                                                                  +
                                                                  温馨提示
                                                                  +
                                                                  1、如果同意申请,请发送正确的退货地址给买家
                                                                  +
                                                                  2、如果拒绝申请,请发送给买家拒绝理由
                                                                  +
                                                                  + + +
                                                                  + {{ state.data.aftersale_status_text }} +
                                                                  +
                                                                  + {{ state.data.aftersale_status_desc }} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + + + + + + + + + + + + + + +
                                                                  +
                                                                  +
                                                                  售后详情
                                                                  + + +
                                                                  交易信息
                                                                  +
                                                                  +
                                                                  用户信息:
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  订单编号:
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  物流状态:
                                                                  +
                                                                  {{ state.data.dispatch_status_text }}
                                                                  +
                                                                  +
                                                                  +
                                                                  订单实付:
                                                                  +
                                                                  ¥{{ state.data?.order?.pay_fee }}
                                                                  +
                                                                  +
                                                                  +
                                                                  订单运费:
                                                                  +
                                                                  ¥{{ state.data?.order?.dispatch_amount }}
                                                                  +
                                                                  +
                                                                  +
                                                                  订单优惠:
                                                                  +
                                                                  ¥{{ state.data?.order?.total_discount_fee }}
                                                                  +
                                                                  +
                                                                  +
                                                                  商品运费:
                                                                  +
                                                                  ¥{{ state.data.dispatch_fee }}
                                                                  +
                                                                  +
                                                                  +
                                                                  商品优惠:
                                                                  +
                                                                  ¥{{ state.data.discount_fee }}
                                                                  +
                                                                  +
                                                                  +
                                                                  建议退款:
                                                                  +
                                                                  ¥{{ state.data.suggest_refund_fee }}
                                                                  +
                                                                  +
                                                                  + +
                                                                  售后信息
                                                                  +
                                                                  +
                                                                  售后单号:
                                                                  +
                                                                  + {{ state.data.aftersale_sn }} + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  申请时间:
                                                                  +
                                                                  {{ state.data.createtime }}
                                                                  +
                                                                  +
                                                                  +
                                                                  联系电话:
                                                                  +
                                                                  {{ state.data.mobile || '-' }}
                                                                  +
                                                                  +
                                                                  +
                                                                  售后类型:
                                                                  +
                                                                  + {{ state.data.type_text }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  实际退款:
                                                                  +
                                                                  + ¥{{ state.data.refund_fee }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  申请原因:
                                                                  +
                                                                  {{ state.data.reason }}
                                                                  +
                                                                  +
                                                                  +
                                                                  相关描述:
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  商品信息
                                                                  +
                                                                  + +
                                                                  +
                                                                  + {{state.data.goods_title}}
                                                                  +
                                                                  + ¥{{state.data.goods_price}} x{{state.data.goods_num}} +
                                                                  +
                                                                  + {{state.data.goods_sku_text}} +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  协商记录
                                                                  + {if $auth->check('shopro/order/aftersale/addLog')} + 回复买家 + {/if} +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  {{ log.oper.name }}
                                                                  +
                                                                  {{ log.createtime }}
                                                                  +
                                                                  {{ log.log_type_text }}
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/aftersale/index.html b/application/admin/view/shopro/order/aftersale/index.html new file mode 100644 index 0000000..0ec1df2 --- /dev/null +++ b/application/admin/view/shopro/order/aftersale/index.html @@ -0,0 +1,199 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + +
                                                                  +
                                                                  +
                                                                  售后管理
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/aftersale/refund.html b/application/admin/view/shopro/order/aftersale/refund.html new file mode 100644 index 0000000..85850d9 --- /dev/null +++ b/application/admin/view/shopro/order/aftersale/refund.html @@ -0,0 +1,28 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + +
                                                                  + + 原路退回 + 退回余额 + +
                                                                  +
                                                                  + + +
                                                                  退款时请与买家协商好,退款之后不可撤回
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/aftersale/refuse.html b/application/admin/view/shopro/order/aftersale/refuse.html new file mode 100644 index 0000000..6ddf91d --- /dev/null +++ b/application/admin/view/shopro/order/aftersale/refuse.html @@ -0,0 +1,18 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/invoice/confirm.html b/application/admin/view/shopro/order/invoice/confirm.html new file mode 100644 index 0000000..4a32b19 --- /dev/null +++ b/application/admin/view/shopro/order/invoice/confirm.html @@ -0,0 +1,36 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/invoice/index.html b/application/admin/view/shopro/order/invoice/index.html new file mode 100644 index 0000000..0d56483 --- /dev/null +++ b/application/admin/view/shopro/order/invoice/index.html @@ -0,0 +1,126 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + +
                                                                  +
                                                                  +
                                                                  订单发票
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/order/action.html b/application/admin/view/shopro/order/order/action.html new file mode 100644 index 0000000..4cd4b22 --- /dev/null +++ b/application/admin/view/shopro/order/order/action.html @@ -0,0 +1,17 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/order/batch_dispatch.html b/application/admin/view/shopro/order/order/batch_dispatch.html new file mode 100644 index 0000000..9448eab --- /dev/null +++ b/application/admin/view/shopro/order/order/batch_dispatch.html @@ -0,0 +1,91 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + +
                                                                  +
                                                                  方法一:如使用导入订单,需完善发货表单物流信息后再上传
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + {{ express.form.model.file.name }} +
                                                                  + + + +
                                                                  +
                                                                  + + + {{ + dc.name + }} ({{ dc.code }}) + + + +
                                                                  发货
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + 方法二:如使用批量发货,需确认 +  商城配置-第三方服务  + 中快递鸟配置完成 +
                                                                  +
                                                                  + 批量发货 + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/order/change_fee.html b/application/admin/view/shopro/order/order/change_fee.html new file mode 100644 index 0000000..038b589 --- /dev/null +++ b/application/admin/view/shopro/order/order/change_fee.html @@ -0,0 +1,30 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + ¥{{ state.pay_fee }} + + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/order/detail.html b/application/admin/view/shopro/order/order/detail.html new file mode 100644 index 0000000..7cf76fc --- /dev/null +++ b/application/admin/view/shopro/order/order/detail.html @@ -0,0 +1,1011 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + +
                                                                  +
                                                                  温馨提示
                                                                  +
                                                                  1、如果无法发货,请及时与买家联系并说明情况后主动退款;
                                                                  +
                                                                  2、买家申请售后,须征得买家同意后发货,否则买家有权拒收货物;
                                                                  +
                                                                  3、订单全部退款将会退回商品库存,并且减少实际销量,订单商品上的主动退款不会退回库存和销量 +
                                                                  +
                                                                  +
                                                                  + + +
                                                                  + + + + {{ state.detail.status_text }} +
                                                                  +
                                                                  + {{ state.detail.status_desc }} +
                                                                  +
                                                                  + {if $auth->check('shopro/order/order/dispatch')} + + 立即发货 + {/if} + {if $auth->check('shopro/order/order/fullRefund')} + 全部退款 + + {/if} +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  商品总价:
                                                                  +
                                                                  ¥{{ state.detail.goods_amount }}
                                                                  +
                                                                  +
                                                                  +
                                                                  运费价格:
                                                                  +
                                                                  ¥{{ state.detail.dispatch_amount }}
                                                                  +
                                                                  +
                                                                  +
                                                                  活动优惠:
                                                                  +
                                                                  +
                                                                  +
                                                                  满减
                                                                  +
                                                                  + -¥{{ state.detail.ext.promo_discounts.full_reduce }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  满折
                                                                  +
                                                                  + -¥{{ state.detail.ext.promo_discounts.full_discount }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  满赠
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  满包邮
                                                                  +
                                                                  + -¥{{ state.detail.ext.promo_discounts.free_shipping }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  优惠券
                                                                  +
                                                                  + -¥{{ state.detail.coupon_discount_fee }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + {{['paid', 'completed'].includes(state.detail.status)?'实付金额':'应付金额'}}: +
                                                                  +
                                                                  + ¥{{ state.detail.pay_fee }} + + {{ state.detail.original_pay_fee }} + + {if $auth->check('shopro/order/order/changeFee')} + 改价 + + {/if} +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  买家留言:
                                                                  +
                                                                  + {{ state.detail.remark || '暂无留言' }} +
                                                                  +
                                                                  +
                                                                  + + + + +
                                                                  交易信息
                                                                  +
                                                                  +
                                                                  订单编号:
                                                                  +
                                                                  + {{ state.detail.order_sn }} + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  订单来源:
                                                                  +
                                                                  + {{ state.detail.platform_text }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  付款时间:
                                                                  +
                                                                  {{ state.detail.paid_time }}
                                                                  +
                                                                  +
                                                                  + +
                                                                  买家信息
                                                                  +
                                                                  +
                                                                  用户昵称:
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + 收货信息 + + +
                                                                  +
                                                                  +
                                                                  收货昵称:
                                                                  +
                                                                  + + {{ state.detail.address.consignee }} + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  联系方式:
                                                                  +
                                                                  + + {{ state.detail.address.mobile }} + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  收货地址:
                                                                  +
                                                                  +
                                                                  + {{ state.detail.address.province_name }}  + {{ state.detail.address.city_name }}  + {{ state.detail.address.district_name }} +
                                                                  + + +
                                                                  + {{ state.detail.address.address }} +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + 发票信息 + {{ + state.detail.invoice.status_text }} + {if $auth->check('shopro/order/invoice/confirm')} + 确认开具 + {/if} +
                                                                  +
                                                                  +
                                                                  发票类型:
                                                                  +
                                                                  + {{ state.detail.invoice.type_text }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  抬头名称:
                                                                  +
                                                                  + {{ state.detail.invoice.name }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  税号:
                                                                  +
                                                                  + {{ state.detail.invoice.tax_no }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  手机号:
                                                                  +
                                                                  + {{ state.detail.invoice.mobile }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  金额:
                                                                  +
                                                                  ¥{{ state.detail.invoice.amount }}
                                                                  +
                                                                  +
                                                                  +
                                                                  实际开票金额:
                                                                  +
                                                                  ¥{{ state.detail.invoice.invoice_amount }}
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + +
                                                                  + 物流信息 + + {if $auth->check('shopro/order/order/dispatch')} + + + + {/if} +
                                                                  +
                                                                  +
                                                                  快递公司:
                                                                  +
                                                                  +
                                                                  + {{ item.express_name }} +
                                                                  + + {{ + dc.name + }} ({{ dc.code }}) + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  快递单号:
                                                                  +
                                                                  +
                                                                  + {{ item.express_no }} +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + {{ goods.goods_title }} +
                                                                  +
                                                                  +
                                                                  + {{ goods.goods_sku_text }} +
                                                                  +
                                                                  x {{ goods.goods_num }}
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + + {if $auth->check('shopro/order/order/updateExpress')} + + + {/if} +
                                                                  + + +
                                                                  + {{ log.content }} +
                                                                  +
                                                                  {{ log.change_date }}
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/order/dispatch.html b/application/admin/view/shopro/order/order/dispatch.html new file mode 100644 index 0000000..7328d6a --- /dev/null +++ b/application/admin/view/shopro/order/order/dispatch.html @@ -0,0 +1,201 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + +
                                                                  + + + + + + + + + + + + + +
                                                                  +
                                                                  配送信息
                                                                  +
                                                                  +
                                                                  +
                                                                  收货信息:
                                                                  +
                                                                  + {{ state.detail.address.consignee }}  + {{ state.detail.address.mobile }} +
                                                                  +
                                                                  +
                                                                  +
                                                                  收货地址:
                                                                  +
                                                                  +
                                                                  + {{ state.detail.address.province_name }}  + {{ state.detail.address.city_name }}  + {{ state.detail.address.district_name }} +
                                                                  +
                                                                  {{ state.detail.address.address }}
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  物流信息
                                                                  +
                                                                  + + + 物流快递 + 一键发货 + + + + + + {{ + dc.name + }} ({{ dc.code }}) + + + + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + 固定内容 + 自定义内容 + + + + + + +
                                                                  +
                                                                  +
                                                                  参数名称
                                                                  +
                                                                  内容
                                                                  +
                                                                  操作
                                                                  +
                                                                  + + + + 添加 + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/order/dispatch_list.html b/application/admin/view/shopro/order/order/dispatch_list.html new file mode 100644 index 0000000..8ae9b97 --- /dev/null +++ b/application/admin/view/shopro/order/order/dispatch_list.html @@ -0,0 +1,98 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + 重新发货 + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/order/full_refund.html b/application/admin/view/shopro/order/order/full_refund.html new file mode 100644 index 0000000..6769225 --- /dev/null +++ b/application/admin/view/shopro/order/order/full_refund.html @@ -0,0 +1,26 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + +
                                                                  + + 原路退回 + 退回余额 + +
                                                                  + 将全额退款,并且退回库存减少销量 +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/order/order/index.html b/application/admin/view/shopro/order/order/index.html new file mode 100644 index 0000000..558782c --- /dev/null +++ b/application/admin/view/shopro/order/order/index.html @@ -0,0 +1,951 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + +
                                                                  +
                                                                  +
                                                                  订单管理
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/order/order/export')} + 订单导出 + + {/if} {if $auth->check('shopro/order/order/exportDelivery')} + 导出发货单 + {/if} + 酒店导出 +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}} 项 +
                                                                  +
                                                                  + {if $auth->check('shopro/order/order/batchDispatch')} + 批量发货 + + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  diff --git a/application/admin/view/shopro/order/order/refund.html b/application/admin/view/shopro/order/order/refund.html new file mode 100644 index 0000000..e6dc1f5 --- /dev/null +++ b/application/admin/view/shopro/order/order/refund.html @@ -0,0 +1,28 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + +
                                                                  + + 原路退回 + 退回余额 + +
                                                                  +
                                                                  + + +
                                                                  退款时请与买家协商好,退款之后不可撤回
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/pay_config/add.html b/application/admin/view/shopro/pay_config/add.html new file mode 100644 index 0000000..3a241e0 --- /dev/null +++ b/application/admin/view/shopro/pay_config/add.html @@ -0,0 +1,180 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + 微信支付V3版 + 支付宝支付 + + + + + 普通商户 + 服务商 + + + + + + + 显示 + 禁用 + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/pay_config/recyclebin.html b/application/admin/view/shopro/pay_config/recyclebin.html new file mode 100644 index 0000000..87c9999 --- /dev/null +++ b/application/admin/view/shopro/pay_config/recyclebin.html @@ -0,0 +1,54 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + + + +
                                                                  +
                                                                  + 已选择 {{batchHandle.data.length}}
                                                                  +
                                                                  + {if $auth->check('shopro/pay_config/restore')} + 还原 + + {/if} + {if $auth->check('shopro/pay_config/destroy')} + 销毁 + + {/if} + {if $auth->check('shopro/pay_config/destroy')} + 清空回收站 + {/if} +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/paylog/add.html b/application/admin/view/shopro/paylog/add.html new file mode 100644 index 0000000..6f27b3a --- /dev/null +++ b/application/admin/view/shopro/paylog/add.html @@ -0,0 +1,46 @@ +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  diff --git a/application/admin/view/shopro/paylog/edit.html b/application/admin/view/shopro/paylog/edit.html new file mode 100644 index 0000000..dd47d80 --- /dev/null +++ b/application/admin/view/shopro/paylog/edit.html @@ -0,0 +1,46 @@ +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  diff --git a/application/admin/view/shopro/paylog/index.html b/application/admin/view/shopro/paylog/index.html new file mode 100644 index 0000000..1cc856f --- /dev/null +++ b/application/admin/view/shopro/paylog/index.html @@ -0,0 +1,35 @@ +
                                                                  + {:build_heading()} + + +
                                                                  diff --git a/application/admin/view/shopro/trade/order/detail.html b/application/admin/view/shopro/trade/order/detail.html new file mode 100644 index 0000000..37b319b --- /dev/null +++ b/application/admin/view/shopro/trade/order/detail.html @@ -0,0 +1,56 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + {{ state.detail.order_amount }}元 + + + {{ state.detail.pay_fee }}元 + + + + {{ state.detail.status_text }} + + + +
                                                                  + +
                                                                  {{state.detail.pay_type_text}}
                                                                  +
                                                                  +
                                                                  -
                                                                  +
                                                                  + + {{ state.detail.order_sn }} + + + {{ state.detail.platform_text }} + + + {{ state.detail.createtime }} + + + {{ state.detail.paid_time || '-' }} + + + {{ state.detail.remark || '-' }} + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/trade/order/index.html b/application/admin/view/shopro/trade/order/index.html new file mode 100644 index 0000000..af8e2f1 --- /dev/null +++ b/application/admin/view/shopro/trade/order/index.html @@ -0,0 +1,97 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + +
                                                                  +
                                                                  +
                                                                  充值订单
                                                                  + + +
                                                                  +
                                                                  + + + {if $auth->check('shopro/trade/order/export')} + 订单导出 + + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/user/coupon/index.html b/application/admin/view/shopro/user/coupon/index.html new file mode 100644 index 0000000..8dd8752 --- /dev/null +++ b/application/admin/view/shopro/user/coupon/index.html @@ -0,0 +1,56 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  领取记录
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/user/user/detail.html b/application/admin/view/shopro/user/user/detail.html new file mode 100644 index 0000000..f0ee7a0 --- /dev/null +++ b/application/admin/view/shopro/user/user/detail.html @@ -0,0 +1,378 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + +
                                                                  基本信息
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + {{ state.detail.nickname }} +
                                                                  +
                                                                  #{{ state.detail.id }}
                                                                  +
                                                                  +
                                                                  + {if $auth->check('shopro/user/user/edit')} + 更换头像 + {/if} +
                                                                  +
                                                                  + + +
                                                                  昵称
                                                                  +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  性别
                                                                  +
                                                                  + + + + +
                                                                  +
                                                                  + +
                                                                  用户名
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  + +
                                                                  密码
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  + +
                                                                  手机号
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  + +
                                                                  电子邮箱
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  + +
                                                                  状态
                                                                  +
                                                                  + + 正常 + 禁用 + +
                                                                  +
                                                                  + +
                                                                  +
                                                                  + 重置 + {if $auth->check('shopro/user/user/edit')} + 保存 + {/if} +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + +
                                                                  账户信息
                                                                  +
                                                                  +
                                                                  + + +
                                                                  推荐人
                                                                  +
                                                                  + + {if $is_pro} + + 更换 + {/if} +
                                                                  +
                                                                  + +
                                                                  佣金
                                                                  +
                                                                  {{ state.detail.commission }}
                                                                  +
                                                                  + +
                                                                  余额
                                                                  +
                                                                  + {{ state.detail.money }} + {if $auth->check('shopro/user/user/recharge')} + 充值 + {/if} +
                                                                  +
                                                                  + +
                                                                  上次登陆时间
                                                                  +
                                                                  {{ state.detail.logintime }}
                                                                  +
                                                                  + +
                                                                  积分
                                                                  +
                                                                  + {{ state.detail.score }} + {if $auth->check('shopro/user/user/recharge')} + 充值 + {/if} +
                                                                  +
                                                                  + +
                                                                  登录IP
                                                                  +
                                                                  {{ state.detail.loginip }}
                                                                  +
                                                                  + +
                                                                  总计消费
                                                                  +
                                                                  + {{ state.detail.total_consume }} +
                                                                  +
                                                                  + +
                                                                  注册时间
                                                                  +
                                                                  {{ state.detail.createtime }}
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  第三方账号
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + + + {{ platform[item.provider][item.platform] }} + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  登录次数:
                                                                  +
                                                                  {{ item.login_num }}
                                                                  +
                                                                  +
                                                                  +
                                                                  Openid:
                                                                  +
                                                                  {{ item.openid }}
                                                                  +
                                                                  +
                                                                  +
                                                                  Unionid:
                                                                  +
                                                                  {{ item.unionid }}
                                                                  +
                                                                  +
                                                                  +
                                                                  更新时间:
                                                                  +
                                                                  {{ item.updatetime }}
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  用户动态
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/user/user/index.html b/application/admin/view/shopro/user/user/index.html new file mode 100644 index 0000000..0eabfac --- /dev/null +++ b/application/admin/view/shopro/user/user/index.html @@ -0,0 +1,56 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  用户管理
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/user/user/recharge.html b/application/admin/view/shopro/user/user/recharge.html new file mode 100644 index 0000000..a73ef60 --- /dev/null +++ b/application/admin/view/shopro/user/user/recharge.html @@ -0,0 +1,19 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/vip/add.html b/application/admin/view/shopro/vip/add.html new file mode 100644 index 0000000..6074cbf --- /dev/null +++ b/application/admin/view/shopro/vip/add.html @@ -0,0 +1,52 @@ +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  diff --git a/application/admin/view/shopro/vip/edit.html b/application/admin/view/shopro/vip/edit.html new file mode 100644 index 0000000..e5a195f --- /dev/null +++ b/application/admin/view/shopro/vip/edit.html @@ -0,0 +1,52 @@ +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  diff --git a/application/admin/view/shopro/vip/index.html b/application/admin/view/shopro/vip/index.html new file mode 100644 index 0000000..d0dcf6e --- /dev/null +++ b/application/admin/view/shopro/vip/index.html @@ -0,0 +1,45 @@ +
                                                                  + +
                                                                  + {:build_heading(null,FALSE)} + +
                                                                  + + + +
                                                                  diff --git a/application/admin/view/shopro/wechat/config/index.html b/application/admin/view/shopro/wechat/config/index.html new file mode 100644 index 0000000..812fd0c --- /dev/null +++ b/application/admin/view/shopro/wechat/config/index.html @@ -0,0 +1,80 @@ +{include file="/shopro/common/script" /} + + +
                                                                  + + + + + + + + + + + + + + + + 订阅号 + 认证订阅号 + 服务号 + 认证服务号 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/wechat/material/add.html b/application/admin/view/shopro/wechat/material/add.html new file mode 100644 index 0000000..f8442b8 --- /dev/null +++ b/application/admin/view/shopro/wechat/material/add.html @@ -0,0 +1,57 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/wechat/material/index.html b/application/admin/view/shopro/wechat/material/index.html new file mode 100644 index 0000000..8de6c66 --- /dev/null +++ b/application/admin/view/shopro/wechat/material/index.html @@ -0,0 +1,304 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + + + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + {if $auth->check('shopro/wechat/material/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + + +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/wechat/menu/add.html b/application/admin/view/shopro/wechat/menu/add.html new file mode 100644 index 0000000..99631df --- /dev/null +++ b/application/admin/view/shopro/wechat/menu/add.html @@ -0,0 +1,293 @@ +{include file="/shopro/common/script" /} + + + + \ No newline at end of file diff --git a/application/admin/view/shopro/wechat/menu/index.html b/application/admin/view/shopro/wechat/menu/index.html new file mode 100644 index 0000000..30b13ab --- /dev/null +++ b/application/admin/view/shopro/wechat/menu/index.html @@ -0,0 +1,122 @@ +{include file="/shopro/common/script" /} + + + + \ No newline at end of file diff --git a/application/admin/view/shopro/wechat/reply/add.html b/application/admin/view/shopro/wechat/reply/add.html new file mode 100644 index 0000000..8294321 --- /dev/null +++ b/application/admin/view/shopro/wechat/reply/add.html @@ -0,0 +1,91 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + +
                                                                  + + + +
                                                                  + {{ tag }} + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + + 图文消息 + 图片 + 视频 + 语音 + 文本 + 链接 + + + + + +
                                                                  + +
                                                                  + {{ item.title }} +
                                                                  +
                                                                  + {{ item.media_id }} +
                                                                  +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  + + + 启用 + 禁用 + + +
                                                                  +
                                                                  +
                                                                  + + 确定 + +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/wechat/reply/index.html b/application/admin/view/shopro/wechat/reply/index.html new file mode 100644 index 0000000..262ecad --- /dev/null +++ b/application/admin/view/shopro/wechat/reply/index.html @@ -0,0 +1,106 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + + +
                                                                  +
                                                                  +
                                                                  自动回复
                                                                  +
                                                                  +
                                                                  + + {if $auth->check('shopro/wechat/reply/add')} + 添加 + {/if} +
                                                                  +
                                                                  +
                                                                  + + + +
                                                                  +
                                                                  {{ item.type_text }}
                                                                  +
                                                                  {{ item.content }}
                                                                  + +
                                                                  + {{keyword}} +
                                                                  +
                                                                  +
                                                                  + + {if $auth->check('shopro/wechat/reply/edit')} + + {/if} + +
                                                                  + {if $auth->check('shopro/wechat/reply/edit')} + 编辑 + {/if} + + + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/withdraw/handle.html b/application/admin/view/shopro/withdraw/handle.html new file mode 100644 index 0000000..b1cfa07 --- /dev/null +++ b/application/admin/view/shopro/withdraw/handle.html @@ -0,0 +1,17 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + 确定 + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/withdraw/index.html b/application/admin/view/shopro/withdraw/index.html new file mode 100644 index 0000000..1676b7f --- /dev/null +++ b/application/admin/view/shopro/withdraw/index.html @@ -0,0 +1,146 @@ +{include file="/shopro/common/script" /} + + + +
                                                                  + + + + + + +
                                                                  +
                                                                  +
                                                                  提现管理
                                                                  + + +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                  + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/shopro/withdraw/log.html b/application/admin/view/shopro/withdraw/log.html new file mode 100644 index 0000000..7214f40 --- /dev/null +++ b/application/admin/view/shopro/withdraw/log.html @@ -0,0 +1,17 @@ +{include file="/shopro/common/script" /} + +
                                                                  + + + + + + + + + + + +
                                                                  \ No newline at end of file diff --git a/application/admin/view/third/index.html b/application/admin/view/third/index.html new file mode 100644 index 0000000..6fc9d97 --- /dev/null +++ b/application/admin/view/third/index.html @@ -0,0 +1,23 @@ +
                                                                  + {:build_heading()} + +
                                                                  +
                                                                  +
                                                                  +
                                                                  + + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  diff --git a/application/admin/view/user/club/add.html b/application/admin/view/user/club/add.html new file mode 100644 index 0000000..4c35549 --- /dev/null +++ b/application/admin/view/user/club/add.html @@ -0,0 +1,190 @@ +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  +
                                                                  +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  diff --git a/application/admin/view/user/club/edit.html b/application/admin/view/user/club/edit.html new file mode 100644 index 0000000..99667c7 --- /dev/null +++ b/application/admin/view/user/club/edit.html @@ -0,0 +1,418 @@ +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  + +
                                                                  +
                                                                  + +
                                                                  + + +
                                                                  + +
                                                                  +
                                                                    +
                                                                    +
                                                                    + + + +
                                                                    + +
                                                                    +
                                                                    + +
                                                                    + + +
                                                                    + +
                                                                    +
                                                                      +
                                                                      +
                                                                      + +
                                                                      + +
                                                                      + + + +
                                                                      +
                                                                      +
                                                                      + +
                                                                      + +
                                                                      +
                                                                      + + +
                                                                      + +
                                                                      + {:build_radios('row[is_asfc]', ['1'=>__('是'), '0'=>__('否')], $row['is_asfc'])} +
                                                                      +
                                                                      + + +
                                                                      + +
                                                                      + +
                                                                      +
                                                                      +
                                                                      + +
                                                                      +
                                                                      + +
                                                                      + + +
                                                                      + +
                                                                      +
                                                                        +
                                                                        +
                                                                        +
                                                                        + +
                                                                        +
                                                                        + +
                                                                        + + +
                                                                        + +
                                                                        + +
                                                                        +
                                                                        +
                                                                        + +
                                                                        +
                                                                        + +
                                                                        + + +
                                                                        + +
                                                                        +
                                                                          +
                                                                          +
                                                                          + +
                                                                          + +
                                                                          +
                                                                          + +
                                                                          + + +
                                                                          + +
                                                                          +
                                                                            +
                                                                            +
                                                                            +
                                                                            + +
                                                                            +
                                                                            + +
                                                                            + + +
                                                                            + +
                                                                            +
                                                                              +
                                                                              +
                                                                              + +
                                                                              + +
                                                                              +
                                                                              + +
                                                                              + + +
                                                                              + +
                                                                              +
                                                                                +
                                                                                +
                                                                                + +
                                                                                + +
                                                                                +
                                                                                + +
                                                                                + + +
                                                                                + +
                                                                                +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  + {:build_radios('row[is_media]', ['1'=>__('是'), '0'=>__('否')], $row['is_asfc'])} +
                                                                                  +
                                                                                  + + {if $row.is_media == 1 } +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  + {/if} + +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  + {notempty name="row.coach2" } +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  + {/notempty} + {notempty name="row.coach3" } +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  + + +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  + {/notempty} + {notempty name="row.coach4" } +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  +
                                                                                  + +
                                                                                  + +
                                                                                  +
                                                                                  + {/notempty} + +
                                                                                  + +
                                                                                  +
                                                                                  + +
                                                                                  + + +
                                                                                  + +
                                                                                  +
                                                                                    +
                                                                                    +
                                                                                    +
                                                                                    + +
                                                                                    +
                                                                                    + + + +
                                                                                    +
                                                                                      +
                                                                                      +
                                                                                      + + +
                                                                                      + +
                                                                                      +
                                                                                      + +
                                                                                      + + +
                                                                                      + +
                                                                                      +
                                                                                        +
                                                                                        +
                                                                                        +
                                                                                        + +
                                                                                        + + {:build_radios('status', ['0' => '正式俱乐部', '1' => '退回修改', '2' => '拒绝'] )} +
                                                                                        +
                                                                                        + + + + + + + +
                                                                                        diff --git a/application/admin/view/user/club/index.html b/application/admin/view/user/club/index.html new file mode 100644 index 0000000..709d873 --- /dev/null +++ b/application/admin/view/user/club/index.html @@ -0,0 +1,35 @@ +
                                                                                        + {:build_heading()} + + +
                                                                                        diff --git a/application/admin/view/user/club/show.html b/application/admin/view/user/club/show.html new file mode 100644 index 0000000..7128556 --- /dev/null +++ b/application/admin/view/user/club/show.html @@ -0,0 +1,319 @@ +
                                                                                        + + +
                                                                                        + +
                                                                                        + +
                                                                                        +
                                                                                        +
                                                                                        + +
                                                                                        + +
                                                                                        +
                                                                                        +
                                                                                        + +
                                                                                        + +
                                                                                        +
                                                                                        + +
                                                                                        + +
                                                                                        + +
                                                                                        +
                                                                                        + +
                                                                                        + +
                                                                                        + +
                                                                                        +
                                                                                        + +
                                                                                        + +
                                                                                        +
                                                                                        + + +
                                                                                        + + +
                                                                                        + +
                                                                                        +
                                                                                          +
                                                                                          +
                                                                                          + + + +
                                                                                          + +
                                                                                          +
                                                                                          + + + +
                                                                                          + + +
                                                                                          + +
                                                                                          +
                                                                                            +
                                                                                            +
                                                                                            + +
                                                                                            + +
                                                                                            + + + +
                                                                                            +
                                                                                            +
                                                                                            + +
                                                                                            + +
                                                                                            +
                                                                                            +
                                                                                            + +
                                                                                            + {:build_radios('row[is_asfc]', ['1'=>__('是'), '0'=>__('否')], $row['is_asfc'])} +
                                                                                            +
                                                                                            + + {if $row.is_asfc == 1 } +
                                                                                            + +
                                                                                            + +
                                                                                            +
                                                                                            +
                                                                                            + +
                                                                                            +
                                                                                            + +
                                                                                            + + +
                                                                                            + +
                                                                                            +
                                                                                              +
                                                                                              +
                                                                                              + {/if} +
                                                                                              + +
                                                                                              +
                                                                                              + + +
                                                                                              + + +
                                                                                              + +
                                                                                              + +
                                                                                              +
                                                                                              +
                                                                                              + +
                                                                                              +
                                                                                              + + +
                                                                                              + + +
                                                                                              + +
                                                                                              +
                                                                                                +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                + {notempty name="row.coach2" } +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                + {/notempty} + {notempty name="row.coach3" } +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                + + +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                + {/notempty} + {notempty name="row.coach4" } +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                +
                                                                                                + +
                                                                                                + +
                                                                                                +
                                                                                                + {/notempty} + +
                                                                                                + +
                                                                                                +
                                                                                                + +
                                                                                                + + +
                                                                                                + +
                                                                                                +
                                                                                                  +
                                                                                                  +
                                                                                                  +
                                                                                                  + +
                                                                                                  +
                                                                                                  + + + +
                                                                                                  +
                                                                                                    +
                                                                                                    +
                                                                                                    + + +
                                                                                                    + +
                                                                                                    +
                                                                                                    + +
                                                                                                    + + +
                                                                                                    + +
                                                                                                    +
                                                                                                      +
                                                                                                      +
                                                                                                      + + + +
                                                                                                      diff --git a/application/admin/view/user/group/add.html b/application/admin/view/user/group/add.html new file mode 100644 index 0000000..b60e0f7 --- /dev/null +++ b/application/admin/view/user/group/add.html @@ -0,0 +1,41 @@ +
                                                                                                      + {:token()} + +
                                                                                                      + +
                                                                                                      + +
                                                                                                      +
                                                                                                      +
                                                                                                      + +
                                                                                                      + + + +
                                                                                                      +
                                                                                                      +
                                                                                                      +
                                                                                                      + +
                                                                                                      + +
                                                                                                      + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                                                                      + +
                                                                                                      +
                                                                                                      + +
                                                                                                      + diff --git a/application/admin/view/user/group/edit.html b/application/admin/view/user/group/edit.html new file mode 100644 index 0000000..075ce12 --- /dev/null +++ b/application/admin/view/user/group/edit.html @@ -0,0 +1,41 @@ +
                                                                                                      + {:token()} + +
                                                                                                      + +
                                                                                                      + +
                                                                                                      +
                                                                                                      +
                                                                                                      + +
                                                                                                      + + + +
                                                                                                      +
                                                                                                      +
                                                                                                      +
                                                                                                      + +
                                                                                                      + +
                                                                                                      + {foreach name="statusList" item="vo"} + + {/foreach} +
                                                                                                      + +
                                                                                                      +
                                                                                                      + +
                                                                                                      + diff --git a/application/admin/view/user/group/index.html b/application/admin/view/user/group/index.html new file mode 100644 index 0000000..57191ef --- /dev/null +++ b/application/admin/view/user/group/index.html @@ -0,0 +1,28 @@ +
                                                                                                      + {:build_heading()} + +
                                                                                                      +
                                                                                                      +
                                                                                                      +
                                                                                                      +
                                                                                                      + {:build_toolbar('refresh,add,edit,del')} + +
                                                                                                      + +
                                                                                                      +
                                                                                                      +
                                                                                                      + +
                                                                                                      +
                                                                                                      +
                                                                                                      diff --git a/application/admin/view/user/player/edit.html b/application/admin/view/user/player/edit.html new file mode 100644 index 0000000..b6e15f1 --- /dev/null +++ b/application/admin/view/user/player/edit.html @@ -0,0 +1,262 @@ +
                                                                                                      + {:token()} + +
                                                                                                      + +
                                                                                                      + +
                                                                                                      +
                                                                                                      +
                                                                                                      + +
                                                                                                      + +
                                                                                                      +
                                                                                                      + +
                                                                                                      + +
                                                                                                      + +
                                                                                                      +
                                                                                                      + +
                                                                                                      + +
                                                                                                      + {:build_radios('row[gender]', ['男'=>__('Male'), '女'=>__('Female')], $row['gender'])} +
                                                                                                      +
                                                                                                      + +
                                                                                                      + +
                                                                                                      + +
                                                                                                      +
                                                                                                      + +
                                                                                                      + +
                                                                                                      + + + +
                                                                                                      +
                                                                                                      + +
                                                                                                      + +
                                                                                                      + +
                                                                                                      +
                                                                                                      + +
                                                                                                      + +
                                                                                                      + +
                                                                                                      +
                                                                                                      + +
                                                                                                      + +
                                                                                                      + +
                                                                                                      +
                                                                                                      + + + + + + + + + +
                                                                                                      + +
                                                                                                      +
                                                                                                      + +
                                                                                                      + + +
                                                                                                      + +
                                                                                                      +
                                                                                                        +
                                                                                                        +
                                                                                                        + +
                                                                                                        + +
                                                                                                        +
                                                                                                        + +
                                                                                                        + + +
                                                                                                        + +
                                                                                                        +
                                                                                                          +
                                                                                                          +
                                                                                                          + +
                                                                                                          + +
                                                                                                          + +
                                                                                                          +
                                                                                                          + {if $row.age >= 18} +
                                                                                                          + +
                                                                                                          + +
                                                                                                          +
                                                                                                          + {if $row.card_type != ''} +
                                                                                                          + +
                                                                                                          + +
                                                                                                          +
                                                                                                          + +
                                                                                                          + +
                                                                                                          + +
                                                                                                          +
                                                                                                          + +
                                                                                                          + +
                                                                                                          +
                                                                                                          + +
                                                                                                          + + +
                                                                                                          + +
                                                                                                          +
                                                                                                            +
                                                                                                            +
                                                                                                            + {/if} + {else /} + +
                                                                                                            + +
                                                                                                            + +
                                                                                                            +
                                                                                                            + +
                                                                                                            + +
                                                                                                            + +
                                                                                                            +
                                                                                                            + +
                                                                                                            + +
                                                                                                            +
                                                                                                            + +
                                                                                                            + + +
                                                                                                            + +
                                                                                                            +
                                                                                                              +
                                                                                                              +
                                                                                                              + +
                                                                                                              + +
                                                                                                              + +
                                                                                                              +
                                                                                                              + +
                                                                                                              + +
                                                                                                              + +
                                                                                                              +
                                                                                                              + +
                                                                                                              + +
                                                                                                              + +
                                                                                                              +
                                                                                                              + +
                                                                                                              + +
                                                                                                              + +
                                                                                                              +
                                                                                                              + +
                                                                                                              + +
                                                                                                              +
                                                                                                              + +
                                                                                                              + + +
                                                                                                              + +
                                                                                                              +
                                                                                                                +
                                                                                                                +
                                                                                                                + {/if} +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + + + +
                                                                                                                +
                                                                                                                + +
                                                                                                                +
                                                                                                                + + + + + +
                                                                                                                + \ No newline at end of file diff --git a/application/admin/view/user/player/index.html b/application/admin/view/user/player/index.html new file mode 100644 index 0000000..eb84ca4 --- /dev/null +++ b/application/admin/view/user/player/index.html @@ -0,0 +1,28 @@ +
                                                                                                                + {:build_heading()} + +
                                                                                                                +
                                                                                                                +
                                                                                                                +
                                                                                                                +
                                                                                                                + {:build_toolbar('refresh,edit,del')} + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                diff --git a/application/admin/view/user/rule/add.html b/application/admin/view/user/rule/add.html new file mode 100644 index 0000000..63b29d1 --- /dev/null +++ b/application/admin/view/user/rule/add.html @@ -0,0 +1,53 @@ +
                                                                                                                + {:token()} +
                                                                                                                + +
                                                                                                                + {:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')])} +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                + {:build_select('row[pid]', $ruledata, null, ['class'=>'form-control', 'required'=>''])} +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')])} +
                                                                                                                +
                                                                                                                + +
                                                                                                                diff --git a/application/admin/view/user/rule/edit.html b/application/admin/view/user/rule/edit.html new file mode 100644 index 0000000..7b1978e --- /dev/null +++ b/application/admin/view/user/rule/edit.html @@ -0,0 +1,52 @@ +
                                                                                                                + {:token()} +
                                                                                                                + +
                                                                                                                + {:build_radios('row[ismenu]', ['1'=>__('Yes'), '0'=>__('No')], $row['ismenu'])} +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + {:build_select('row[pid]', $ruledata, $row['pid'], ['class'=>'form-control', 'required'=>''])} +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])} +
                                                                                                                +
                                                                                                                + +
                                                                                                                diff --git a/application/admin/view/user/rule/index.html b/application/admin/view/user/rule/index.html new file mode 100644 index 0000000..1ecb4ed --- /dev/null +++ b/application/admin/view/user/rule/index.html @@ -0,0 +1,28 @@ +
                                                                                                                + {:build_heading()} + +
                                                                                                                +
                                                                                                                +
                                                                                                                +
                                                                                                                +
                                                                                                                + {:build_toolbar('refresh,add,edit,del')} + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                diff --git a/application/admin/view/user/user/edit.html b/application/admin/view/user/user/edit.html new file mode 100644 index 0000000..8659a25 --- /dev/null +++ b/application/admin/view/user/user/edit.html @@ -0,0 +1,151 @@ +
                                                                                                                + {:token()} + +
                                                                                                                + +
                                                                                                                + {$groupList} +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                + +
                                                                                                                +
                                                                                                                +
                                                                                                                + +
                                                                                                                +
                                                                                                                + +
                                                                                                                + + +
                                                                                                                + +
                                                                                                                +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {:build_radios('row[gender]', ['1'=>__('Male'), '0'=>__('Female')], $row['gender'])} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {:build_radios('row[status]', ['normal'=>__('Normal'), 'hidden'=>__('Hidden')], $row['status'])} +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  diff --git a/application/admin/view/user/user/index.html b/application/admin/view/user/user/index.html new file mode 100644 index 0000000..91e1763 --- /dev/null +++ b/application/admin/view/user/user/index.html @@ -0,0 +1,28 @@ +
                                                                                                                  + {:build_heading()} + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {:build_toolbar('refresh,edit,del')} + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  diff --git a/application/api/common.php b/application/api/common.php new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/application/api/common.php @@ -0,0 +1 @@ + '\\app\\api\\library\\ExceptionHandle', +]; diff --git a/application/api/controller/Club.php b/application/api/controller/Club.php new file mode 100644 index 0000000..7346d2f --- /dev/null +++ b/application/api/controller/Club.php @@ -0,0 +1,151 @@ +"block","name"=>"indexfocus","row"=>"5","orderby"=>"weigh"]); + $data['hotNews'] = \addons\cms\model\Archives::getArchivesList(["id"=>"item","limit"=>"5","channel"=>"43","type"=>"sons"]); + $data['industry'] = \addons\cms\model\Archives::getArchivesList(["id"=>"item","limit"=>"4","channel"=>"44","type"=>"sons"]); + $data['fly'] = \addons\cms\model\Archives::getArchivesList(["id"=>"item","limit"=>"6","channel"=>"45","type"=>"sons"]); + $data['atlas'] = \addons\cms\model\Archives::getArchivesList(["id"=>"item","limit"=>"4","channel"=>"46","type"=>"sons"]); + $data['video'] = \addons\cms\model\Archives::getArchivesList(["id"=>"item","channel"=>"47","addon"=>"video","limit"=>"0,5"]); + + foreach ($data as $keys => $vals) { + if (count($vals) > 1) { + foreach ($vals as $key => $val) { + $data[$key][$key] = $val->toArray(); + } + } + + } + + return json($data); + + } + + //地区筛选 + public function area_club(){ + $club = new ClubModel(); + $province = $this->request->param("province"); + if($province) $where['province'] = $province; + $city = $this->request->param("city"); + if($city) $where['city'] = $city; + $district = $this->request->param("district"); + if($district) $where['district'] = $district; + // if(empty($club_id)) $this->error('缺少参数'); + // var_dump($where);exit; + $club_res = $club->field('id,name,logo,flag,name_short') + ->where($where)->where('status in (9)') + ->orderRaw('convert(name_short using gbk) ASC') + ->select(); + return json($club_res); + } + + public function all_club(){ + $club = new ClubModel(); + $club_res = $club->field('id,name,logo,flag,name_short')->where('status in (9)') + ->orderRaw('convert(name_short using gbk) ASC') + ->select(); + return json($club_res); + } + + public function club_detail(){ + $club = new ClubModel(); + $invate = new ClubInvate(); + $player = new Players(); + $Archives = new Archives(); + $MatchRanking = new MatchRanking(); + $League = new League(); + $club_id = $this->request->param("club_id"); + $LeagueClubIntegral = new LeagueClubIntegral(); + if(empty($club_id)) $this->error('缺少参数'); + $Archives_res = $Archives->where('channel_id',73)->select(); + unset($Archives_res[1]);//国家集训队 + unset($Archives_res[4]);//测试站 + // var_dump($Archives_res);exit; + $station_integral = [];$j = 0; + $lastElement = end($Archives_res); + foreach ($Archives_res as $value){ + + $club_integral_res = $LeagueClubIntegral->where('club_id',$club_id)->where('match_id',$value['id'])->find(); + + if(!empty($club_integral_res)){ + //复用积分排名 + $res = $League->club_integral($value['id'],$club_integral_res['name_short']); + // var_dump($res['number']);exit; + $player_count = $MatchRanking->where('match_id',$value['id'])->where('name_short',$club_integral_res['name_short'])->where('course',101)->where('other_round',1)->count(); + $station_integral[] = array('match_id'=>$value['id'],'name'=> $value['title'],'sum_grade'=>$club_integral_res['sum_grade'],'number'=>$res['integral_sort'],'player_number'=>$player_count,'nostart'=>0); + // var_dump($player_count);exit; + }else{ + $station_integral[] = array('match_id'=>$value['id'],'name'=> $value['title'],'sum_grade'=>0,'number'=>0,'player_number'=>0,'nostart'=>0); + } + //判断最后一场比赛是否未开始 + if($value['id'] == $lastElement['id']){ + $res = db("cms_addonproducts")->where('id',$value['id'])->find(); + if(strtotime($res['stime']) > time()){ + // var_dump(strtotime($res['stime']));exit; + $station_integral[$j]['nostart'] = 1; + } + // var_dump($res['stime']);exit; + } + $j++; + // var_dump($club_integral_res);exit; + } + // var_dump($station_integral);exit; + $club_res = $club->field('id,user_id,name,name_short,province,city,district,logo,flag,competition_slogan,leader')->where('id',$club_id)->find(); + $all_invate = $invate->where('club_id',$club_res['id'])->where('status',6)->where('deletetime',null)->select(); + $all_player = []; $i = 0; + foreach ($all_invate as $v){ + $player_info = $player->field('id,real_name,player_pic')->where('id',$v['player_id'])->find(); + $all_player[$i] = $player_info; + $i++; + } + $club_res['all_player'] = $all_player; + $club_res['station_integral'] = $station_integral; + return json($club_res); + } + + public function randclub(){ + $club = new ClubModel(); + $club_res = $club->field('id,user_id,name,name_short,province,city,district,logo,flag,competition_slogan,leader')->where('status',9)->orderRaw("rand()")->limit(6)->select(); + $invate = new ClubInvate(); + $player = new Players(); + + foreach ($club_res as &$val){ + $all_invate = $invate->where('club_id',$val['id'])->where('status',6)->select(); + $all_player = []; $i = 0; + foreach ($all_invate as $v){ + $player_info = $player->field('id,real_name,player_pic')->where('id',$v['player_id'])->find(); + $all_player[$i] = $player_info; + $i++; + } + $val['all_player'] = $all_player; + } + + return json($club_res); + } + +} diff --git a/application/api/controller/Common.php b/application/api/controller/Common.php new file mode 100644 index 0000000..d3151be --- /dev/null +++ b/application/api/controller/Common.php @@ -0,0 +1,134 @@ +request->request('version')) { + $lng = $this->request->request('lng'); + $lat = $this->request->request('lat'); + + //配置信息 + $upload = Config::get('upload'); + //如果非服务端中转模式需要修改为中转 + if ($upload['storage'] != 'local' && isset($upload['uploadmode']) && $upload['uploadmode'] != 'server') { + //临时修改上传模式为服务端中转 + set_addon_config($upload['storage'], ["uploadmode" => "server"], false); + + $upload = \app\common\model\Config::upload(); + // 上传信息配置后 + Hook::listen("upload_config_init", $upload); + + $upload = Config::set('upload', array_merge(Config::get('upload'), $upload)); + } + + $upload['cdnurl'] = $upload['cdnurl'] ? $upload['cdnurl'] : cdnurl('', true); + $upload['uploadurl'] = preg_match("/^((?:[a-z]+:)?\/\/)(.*)/i", $upload['uploadurl']) ? $upload['uploadurl'] : url($upload['storage'] == 'local' ? '/api/common/upload' : $upload['uploadurl'], '', false, true); + + $content = [ + 'citydata' => Area::getCityFromLngLat($lng, $lat), + 'versiondata' => Version::check($version), + 'uploaddata' => $upload, + 'coverdata' => Config::get("cover"), + ]; + $this->success('', $content); + } else { + $this->error(__('Invalid parameters')); + } + } + + /** + * 上传文件 + * @ApiMethod (POST) + * @param File $file 文件流 + */ + public function upload() + { + // var_dump(123);exit; + Config::set('default_return_type', 'json'); + //必须设定cdnurl为空,否则cdnurl函数计算错误 + Config::set('upload.cdnurl', ''); + $chunkid = $this->request->post("chunkid"); + // var_dump($chunkid);exit; + if ($chunkid) { + if (!Config::get('upload.chunking')) { + $this->error(__('Chunk file disabled')); + } + $action = $this->request->post("action"); + $chunkindex = $this->request->post("chunkindex/d"); + $chunkcount = $this->request->post("chunkcount/d"); + $filename = $this->request->post("filename"); + $method = $this->request->method(true); + if ($action == 'merge') { + $attachment = null; + //合并分片文件 + try { + $upload = new Upload(); + $attachment = $upload->merge($chunkid, $chunkcount, $filename); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + $this->success(__('Uploaded successful'), ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]); + } elseif ($method == 'clean') { + //删除冗余的分片文件 + try { + $upload = new Upload(); + $upload->clean($chunkid); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + $this->success(); + } else { + //上传分片文件 + //默认普通上传文件 + $file = $this->request->file('file'); + try { + $upload = new Upload($file); + $upload->chunk($chunkid, $chunkindex, $chunkcount); + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + $this->success(); + } + } else { + $attachment = null; + //默认普通上传文件 + $file = $this->request->file('file'); + try { + $upload = new Upload($file); + $attachment = $upload->upload(); + // var_dump($attachment);exit; + } catch (UploadException $e) { + $this->error($e->getMessage()); + } + + $this->success(__('Uploaded successful'), ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]); + } + + } +} diff --git a/application/api/controller/Demo.php b/application/api/controller/Demo.php new file mode 100644 index 0000000..1e783cb --- /dev/null +++ b/application/api/controller/Demo.php @@ -0,0 +1,108 @@ +post(); + $this->success('返回成功', $this->request->param()); + + } + + /** + * 无需登录的接口 + * + */ + public function test1() + { + $this->success('返回成功', ['action' => 'test1']); + } + + /** + * 需要登录的接口 + * + */ + public function test2() + { + $this->success('返回成功', ['action' => 'test2']); + } + + /** + * 需要登录且需要验证有相应组的权限 + * + */ + public function test3() + { + $this->success('返回成功', ['action' => 'test3']); + } + + public function sendSMS() + { + $alisms = new \addons\alisms\library\Alisms(); + + $config = get_addon_config('alisms'); + + $template = "SMS_463741027"; + + $sign = $config['sign']; + $param = [ + 'name' => "Soar", + 'content' => "未通过审核,请根据审核内容修改后重新提交。" + ]; + + + $alisms->mobile(17633935176) + ->template($template) + ->sign($sign) + ->param($param) + ->send(); + } + + public function delfile() + { + $ossClient = new OssClient("LTAI4Fq72VJX1kU4LuqtqD5Z", "fVNcV32xywj0nwaxygq2PpS0aobhKY", "oss-cn-shanghai.aliyuncs.com"); + + $deleteObject = $ossClient->deleteObject("ydool2017", "%E6%AF%94%E7%BF%BC%E9%A3%9E%E8%A1%8C%E7%BD%91//uploads/20231205/9eae1e5dab151ed9246c116717c6c009.png"); + print_r($deleteObject);exit; + } + +} diff --git a/application/api/controller/Ems.php b/application/api/controller/Ems.php new file mode 100644 index 0000000..7d39a12 --- /dev/null +++ b/application/api/controller/Ems.php @@ -0,0 +1,96 @@ +request->post("email"); + $event = $this->request->post("event"); + $event = $event ? $event : 'register'; + + $last = Emslib::get($email, $event); + if ($last && time() - $last['createtime'] < 60) { + $this->error(__('发送频繁')); + } + if ($event) { + $userinfo = User::getByEmail($email); + if ($event == 'register' && $userinfo) { + //已被注册 + $this->error(__('已被注册')); + } elseif (in_array($event, ['changeemail']) && $userinfo) { + //被占用 + $this->error(__('已被占用')); + } elseif (in_array($event, ['changepwd', 'resetpwd']) && !$userinfo) { + //未注册 + $this->error(__('未注册')); + } + } + $ret = Emslib::send($email, null, $event); + if ($ret) { + $this->success(__('发送成功')); + } else { + $this->error(__('发送失败')); + } + } + + /** + * 检测验证码 + * + * @ApiMethod (POST) + * @param string $email 邮箱 + * @param string $event 事件名称 + * @param string $captcha 验证码 + */ + public function check() + { + $email = $this->request->post("email"); + $event = $this->request->post("event"); + $event = $event ? $event : 'register'; + $captcha = $this->request->post("captcha"); + + if ($event) { + $userinfo = User::getByEmail($email); + if ($event == 'register' && $userinfo) { + //已被注册 + $this->error(__('已被注册')); + } elseif (in_array($event, ['changeemail']) && $userinfo) { + //被占用 + $this->error(__('已被占用')); + } elseif (in_array($event, ['changepwd', 'resetpwd']) && !$userinfo) { + //未注册 + $this->error(__('未注册')); + } + } + $ret = Emslib::check($email, $captcha, $event); + if ($ret) { + $this->success(__('成功')); + } else { + $this->error(__('验证码不正确')); + } + } +} diff --git a/application/api/controller/Fastimadd.php b/application/api/controller/Fastimadd.php new file mode 100644 index 0000000..8d5d0ba --- /dev/null +++ b/application/api/controller/Fastimadd.php @@ -0,0 +1,125 @@ +request->param("user_id"); + $userim_res = $use_im->where('user_id',$user_id)->find(); + $user_res = $user->where('id',$user_id)->find(); + if(!empty($userim_res)){ + $this->success('success',$userim_res['id']); + } + if(!empty($user_res)){ + if(!empty($user_res['mobile'])){ + $this->simulation($user_res['mobile']); + $userim_res = $use_im->where('user_id',$user_id)->find(); + $this->success('success',$userim_res['id']); + } + if(!empty($user_res['email'])){ + $this->simulation($user_res['email']); + $userim_res = $use_im->where('user_id',$user_id)->find(); + $this->success('success',$userim_res['id']); + } + + } + } + + public function openim(){ + $use_im = new Userim(); + $uid1 = $this->request->param("uid1"); + $uid2 = $this->request->param("uid2"); + $userim_res1 = $use_im->where('id',$uid1)->find(); + $userim_res2 = $use_im->where('id',$uid2)->find(); + if(empty($userim_res1) || empty($userim_res2)) $this->error('chat fail'); + // $message = new Message(); + $common = new Common(); + $res = $common->imSession('single',$uid1,$uid2); + $this->success('success',$res); + } + + public function get_imtoken(){ + $use_im = new Userim(); + $user = new UserModel(); + $user_id = $this->request->param("user_id"); + + $user_res = $user->where('id',$user_id)->find(); + // var_dump($user_id);exit; + if(!empty($user_res)){ + if(!empty($user_res['mobile'])){ + $res = $this->simulation($user_res['mobile']); + // var_dump($res);exit; + // var_dump(json_decode($res,true));exit; + // return json(json_decode($res,true)); + // $userim_res = $use_im->where('user_id',$user_id)->find(); + // $this->success('success',$userim_res['id']); + }elseif(!empty($user_res['email'])){ + $this->simulation($user_res['email']); + // return json($res); + // $userim_res = $use_im->where('user_id',$user_id)->find(); + // $this->success('success',$userim_res['id']); + } + }else{ + $this->error('empty user'); + } + } + + public function find_alluser(){ + $user = new User(); + $res = $user->where('id','<',0)->select(); + // var_dump($res);exit; + $sum = 0; + foreach ($res as $val){ + if(!empty($val['mobile'])){ + $this->simulation($val['mobile']); + }elseif(!empty($val['email'])){ + $this->simulation($val['email']); + }else{ + $arr[] = $val; + $sum++; + // var_dump($val); + } + // var_dump($val['username']);exit; + + } + var_dump($sum); + exit; + } + + public function simulation($account){ + $url = "https://www.fpvone.cn/addons/fastim/api.user/logins";//你要请求的地址 + $post_data = array( + "captcha" => "6556", + "captcha_id" => "d55d405fdc6a871ad0dcdc4fad5d582d", + "account" => $account, + "password" => "lunar5", + ); + $ch = curl_init();//初始化cURL + + curl_setopt($ch,CURLOPT_URL,$url);//抓取指定网页 + curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);//要求结果为字符串并输出到屏幕上 + curl_setopt($ch,CURLOPT_POST,1);//Post请求方式 + curl_setopt($ch,CURLOPT_POSTFIELDS,$post_data);//Post变量 + + $output = curl_exec($ch);//执行并获得HTML内容 + curl_close($ch);//释放cURL句柄 + + print_r($output); + } +} diff --git a/application/api/controller/Government.php b/application/api/controller/Government.php new file mode 100644 index 0000000..393bd8e --- /dev/null +++ b/application/api/controller/Government.php @@ -0,0 +1,250 @@ + $endTimestamp){ + return false; + } + // var_dump($newTimestamp);exit; + $check_sign = md5($time.'ydool'); + if($check_sign != $sign){ + return false; + } + return true; + } + /** + * 修改密码 + */ + public function changepwd() + { + if ($this->request->isPost()) { + $type = $this->request->post("type"); + $mobile = $this->request->post("mobile"); + + $newpassword = $this->request->post("newpassword"); + $sign = $this->request->post("sign"); + $time = $this->request->post("time"); + $res = $this->checkSign($time,$sign); + if(!$res){ + return $this->easy_json(400,'fail','error param1'); + } + // $dialCode = $this->request->post("dialCode"); + + // if (!$newpassword || !$captcha) { + // $this->error(__('Invalid parameters')); + // } + //验证Token + if (!Validate::make()->check(['newpassword' => $newpassword], ['newpassword' => 'require|regex:\S{6,30}'])) { + return $this->easy_json(400,'fail','Password must be 6 to 30 characters'); + // $this->error(__('Password must be 6 to 30 characters')); + } + + $user = \app\common\model\User::getByMobile($mobile); + if (!$user) { + return $this->easy_json(400,'fail','User not found'); + // $this->error(__('User not found')); + } + //模拟一次登录 + $this->auth->direct($user->id); + $ret = $this->auth->changepwd($newpassword, '', true); + if ($ret) { + return $this->easy_json(200,'success','Reset password successful'); + // $this->success(__('Reset password successful')); + } else { + return $this->easy_json(400,'fail',$this->auth->getError()); + // $this->error($this->auth->getError()); + } + } + + } + /** + * 注册会员 + */ + public function register() + { + if ($this->request->isPost()) { + $username = $this->request->post('username'); + $password = $this->request->post('password'); + $email = $this->request->post('email'); + $mobile = $this->request->post('mobile', ''); + $sign = $this->request->post("sign"); + $time = $this->request->post("time"); + $res = $this->checkSign($time,$sign); + // $captcha = $this->request->post('captcha'); + // var_dump($captcha);exit; + if(!$res){ + return $this->easy_json(400,'fail','error param1'); + } + if(empty($username) || empty($password) || strlen($username)>50 || strlen($username) < 3 || strlen($password)>30 || strlen($password)<6 || strlen($mobile) != 11){ + // var_dump(12);exit; + return $this->easy_json(400,'fail','error param'); + } + + + $rule = [ + 'account' => 'require|length:3,50', + 'password' => 'require|length:6,30', + ]; + + $msg = [ + 'account.require' => 'Account can not be empty', + 'account.length' => 'Account must be 3 to 50 characters', + 'password.require' => 'Password can not be empty', + 'password.length' => 'Password must be 6 to 30 characters', + ]; + $data = [ + 'account' => $username, + 'password' => $password, + ]; + $validate = new Validate($rule, $msg); + $result = $validate->check($data); + if (!$result) { + $message = 'fail'; + // $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); + return $this->easy_json(400,$message,$validate->getError()); + // return false; + } + if ($this->auth->register($username, $password, $email, $mobile)) { + $user = User::get(['username' => $username]); + return $this->easy_json(200,'success',array('id' => base64_encode($user['id']))); + } else { + $message = 'fail'; + if($this->auth->getError() == 'Mobile already exist'){ + $message = '手机号已存在'; + }elseif($this->auth->getError() == 'Username already exist'){ + $message = '用户名已存在'; + } + return $this->easy_json(400,$message,$this->auth->getError()); + } + } + + } + + /** + * 会员登录 + */ + public function login() + { + // $this->layout = 'default'; + // $this->view->engine->layout('layout/' . $this->layout); + // header('Access-Control-Allow-Origin: *'); + // header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies + // header('Access-Control-Expose-Headers: *'); //服务器 headers 白名单,可以让客户端进行访问 + // header('Access-Control-Allow-Headers: *'); + // var_dump(base64_encode(1049));exit; + // var_dump(base64_decode('MTIz'));exit; + $url = $this->request->request('url', '', 'trim'); + // var_dump(45);exit; + if ($this->request->isPost()) { + $username = $this->request->post('username'); + $password = $this->request->post('password'); + $sign = $this->request->post("sign"); + $time = $this->request->post("time"); + $res = $this->checkSign($time,$sign); + // var_dump($account);exit; + if(!$res){ + return $this->easy_json(400,'fail','error param1'); + } + if(empty($username) || empty($password) || strlen($username)>50 || strlen($username) < 3 || strlen($password)>30 || strlen($password)<6){ + // var_dump(12);exit; + return $this->easy_json(400,'fail','error param'); + } + + $rule = [ + 'account' => 'require|length:3,50', + 'password' => 'require|length:6,30', + ]; + + $msg = [ + 'account.require' => 'Account can not be empty', + 'account.length' => 'Account must be 3 to 50 characters', + 'password.require' => 'Password can not be empty', + 'password.length' => 'Password must be 6 to 30 characters', + ]; + $data = [ + 'account' => $username, + 'password' => $password, + ]; + $validate = new Validate($rule, $msg); + $result = $validate->check($data); + if (!$result) { + $message = 'fail'; + // $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); + return $this->easy_json(400,$message,$validate->getError()); + // return false; + } + // return $this->auth->login($username, $password); + if ($this->auth->login($username, $password)) { + $field = Validate::is($username, 'email') ? 'email' : (Validate::regex($username, '/^1\d{10}$/') ? 'mobile' : 'username'); + $user = User::get([$field => $username]); + return $this->easy_json(200,'success',array('id' => base64_encode($user['id']),'nickname'=>$user['nickname'])); + + // $this->success(__('Logged in successful'), $url ? $url : url('user/index')); + } else { + return $this->easy_json(400,'fail',$this->auth->getError()); + } + + } + + } + + private function easy_json($code,$msg,$data) + { + $data = [ + 'code' => $code, + 'msg' => $msg, + 'data' => $data, + ]; + return json($data); + + } + + private function encrypt($data, $key, $cipher = 'aes-256-cbc') { + $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher)); + $encrypted = openssl_encrypt($data, $cipher, $key, 0, $iv); + return base64_encode($iv . $encrypted); + } + + private function decrypt($data, $key, $cipher = 'aes-256-cbc') { + $data = base64_decode($data); + $iv = substr($data, 0, openssl_cipher_iv_length($cipher)); + $data = substr($data, openssl_cipher_iv_length($cipher)); + $decrypted = openssl_decrypt($data, $cipher, $key, 0, $iv); + return $decrypted; + } + + + public function delfile() + { + $ossClient = new OssClient("LTAI4Fq72VJX1kU4LuqtqD5Z", "fVNcV32xywj0nwaxygq2PpS0aobhKY", "oss-cn-shanghai.aliyuncs.com"); + + $deleteObject = $ossClient->deleteObject("ydool2017", "%E6%AF%94%E7%BF%BC%E9%A3%9E%E8%A1%8C%E7%BD%91//uploads/20231205/9eae1e5dab151ed9246c116717c6c009.png"); + print_r($deleteObject);exit; + } + +} diff --git a/application/api/controller/Index.php b/application/api/controller/Index.php new file mode 100644 index 0000000..1a84e65 --- /dev/null +++ b/application/api/controller/Index.php @@ -0,0 +1,40 @@ +"block","name"=>"indexfocus","row"=>"5","orderby"=>"weigh"]); + $data['hotNews'] = \addons\cms\model\Archives::getArchivesList(["id"=>"item","limit"=>"5","channel"=>"43","type"=>"sons"]); + $data['industry'] = \addons\cms\model\Archives::getArchivesList(["id"=>"item","limit"=>"4","channel"=>"44","type"=>"sons"]); + $data['fly'] = \addons\cms\model\Archives::getArchivesList(["id"=>"item","limit"=>"6","channel"=>"45","type"=>"sons"]); + $data['atlas'] = \addons\cms\model\Archives::getArchivesList(["id"=>"item","limit"=>"4","channel"=>"46","type"=>"sons"]); + $data['video'] = \addons\cms\model\Archives::getArchivesList(["id"=>"item","channel"=>"47","addon"=>"video","limit"=>"0,5"]); + + foreach ($data as $keys => $vals) { + if (count($vals) > 1) { + foreach ($vals as $key => $val) { + $data[$key][$key] = $val->toArray(); + } + } + + } + + return json($data); + + } +} diff --git a/application/api/controller/Leaguepapply.php b/application/api/controller/Leaguepapply.php new file mode 100644 index 0000000..ed963d1 --- /dev/null +++ b/application/api/controller/Leaguepapply.php @@ -0,0 +1,24 @@ +request->param("match_id"); + // 获取赛事详细信息 + + $archives = new Archives(); + + @$toArray = $archives->alias("archives") + ->join("cms_addonproduct" . ' addon', 'addon.id=archives.id', 'LEFT') + ->where("archives.id", "eq", $match_id) + ->field("archives.title, archives.flag, archives.status, archives.publishtime, archives.id, + archives.channel_id, archives.style, addon.content, archives.image, addon.phoneimg, addon.zbf, addon.cbf, + addon.bsd, addon.csrs, addon.xsaddress, addon.stime, addon.starttime, addon.endtime, addon.etime, addon.jszn, addon.poster, addon.isopen") + ->find() + ->toArray(); + + if (@empty($toArray)) { + $this->error("赛事信息不存在"); + } + // 获取已报名人数 + $matchContestant = new MatchContestant(); + $toArray['entry_player'] = $matchContestant->where("match_id", $match_id) + ->whereIn("status", [1, 2]) + ->count("*"); + + $httpPost = $this->httpPost("118.89.120.104:14859/api/v1/processHtml", http_build_query(['mach_id' => $match_id])); + + $html = json_decode($httpPost, true); + if (empty($html)) { + $this->error("服务端错误!"); + } + $toArray['content'] = $html['data']['html'] ? $html['data']['html'] : $toArray['content']; + $toArray['schedule'] = $html['data']['schedule'] ? $html['data']['schedule'] : " "; + $toArray['regulation'] = $html['data']['regulation'] ? $html['data']['regulation'] : " "; + $toArray['commitment'] = $html['data']['commitment'] ? $html['data']['commitment'] : " "; + $toArray['rule'] = $html['data']['rule'] ? $html['data']['rule'] : " "; + + $this->result("获取成功", $toArray, 1); + } + + public function entry_match() + { + $token = $this->auth->getToken(); + + $token_info = \app\common\library\Token::get($token); + $expiretime = date("Y-m-d H:i:s", $token_info['expiretime']); + + $where['member_id'] = $token_info['user_id']; + + $plyaerinfo = \app\common\model\Players::get($where); + + if (empty($plyaerinfo) || $plyaerinfo->player_status != 9) { + $this->error("你还不是正式的飞手"); + } + + // 获取参与的赛事 + $matchContestant = new MatchContestant(); + $entry_list = $matchContestant->where("player_id", $plyaerinfo->id) + ->select(); + + if (empty($entry_list)) { + $this->error("您还未参加过赛事"); + } + + $key = 0; + foreach ($entry_list as $key => $val) { + $archives = Archives::get($val->match_id); + if (empty($archives)) { + unset($entry_list[$key]); + continue; + } + + $res_data[$key]['match_title'] = $archives->title; + $res_data[$key]['status'] = $val->status; + $res_data[$key]['mark'] = $val->mark; + $res_data[$key]['player_id'] = $plyaerinfo->id; + $res_data[$key]['match_id'] = $val->match_id; + $key++; + } + + if (empty($res_data)) { + $this->error("您还未参加过赛事"); + } + + $this->result("获取成功", $res_data, 200); + + } + + public function getindexmatch() + { + $res['match_id'] = "413"; + + $this->result("获取成功", $res); + } + + + function curlPost($url, $post_data = array(), $timeout = 5, $header = "", $data_type = "") { + $header = empty($header) ? '' : $header; + + //支持json数据数据提交 + if($data_type == 'json'){ + $post_string = json_encode($post_data); + }elseif($data_type == 'array') { + $post_string = $post_data; + }elseif(is_array($post_data)){ + $post_string = http_build_query($post_data, '', '&'); + } + $ch = curl_init(); // 启动一个CURL会话 + curl_setopt($ch, CURLOPT_URL, $url); // 要访问的地址 + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查 // https请求 不验证证书和hosts + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在 + curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器 + //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转 + //curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer + curl_setopt($ch, CURLOPT_POST, true); // 发送一个常规的Post请求 + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string); // Post提交的数据包 + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); // 设置超时限制防止死循环 + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + //curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 获取的信息以文件流的形式返回 + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); //模拟的header头 + $result = curl_exec($ch); + + // 打印请求的header信息 + //$a = curl_getinfo($ch); + //var_dump($a); + + curl_close($ch); + return $result; + } + + // 模拟提交数据函数 + public function httpPost($url,$data){ + $curl = curl_init(); // 启动一个CURL会话 + curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址 + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查 + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在 + curl_setopt($curl, CURLOPT_POST, true); // 发送一个常规的Post请求 + curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包 + curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环 + curl_setopt($curl, CURLOPT_HEADER, false); // 显示返回的Header区域内容 + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // 获取的信息以文件流的形式返回 + $result = curl_exec($curl); // 执行操作 + if (curl_errno($curl)) { + return 'Error POST'.curl_error($curl); + } + curl_close($curl); // 关键CURL会话 + return $result; // 返回数据 + } + + + + +} \ No newline at end of file diff --git a/application/api/controller/MatchServer.php b/application/api/controller/MatchServer.php new file mode 100644 index 0000000..af0b85e --- /dev/null +++ b/application/api/controller/MatchServer.php @@ -0,0 +1,131 @@ +public function __construct() +{ +$this->matchModel = new Archives(); +$this->matchContestantModel = new MatchContestant(); +$this->addonproductModel = db("cms_addonproduct"); +} + +function errors(string $message) +{ +$res['code'] = 102; +$res['data'] = []; +$res['message'] = $message; + +return json($res); +} + +function succeed(array $data) { +$res['code'] = 100; +$res['data'] = $data; +$res['message'] = '获取成功'; + +return json($res); +} + +/** +* 获取赛事信息开启缓存 +* @Author:Soar +* @Time:2023/10/8 15:33 +* @return \think\response\Json +*/ +public function get_match() +{ +$match = $this->matchModel::get($this->match_id, [], true); +$addon = $this->addonproductModel->where('id', $this->match_id)->find(); +if ($addon) { +$match->setData($addon); +} + +try { +$match = $match->toArray(); +} catch (\Exception $exception) { +$message = $exception->getMessage(); + +return $this->errors($message); + +} + +return $this->succeed($match); +} + +/** +* 获取赛事信息禁用缓存 +* @Author:Soar +* @Time:2023/10/8 15:33 +* @return \think\response\Json +*/ +public function get_line_match() +{ +$match = $this->matchModel::get($this->match_id, [], false); +$addon = $this->addonproductModel->where('id', $this->match_id)->find(); +if ($addon) { +$match->setData($addon); +} + +try { +$match = $match->toArray(); +} catch (\Exception $exception) { +$message = $exception->getMessage(); + +return $this->errors($message); + +} + +return $this->succeed($match); +} + +public function get_match_players(int $players_id = null, array $where = []) +{ +$collection = $this->matchContestantModel->all($where, [], true); + +return $collection; +} + +/** +* 飞手报名参赛 +* @Author:Soar +* @Time:2023/10/8 16:48 +* @param int $players_id +* @param int $match_id +*/ +public function add_players_to_match(int $players_id = null, int $match_id = null) +{ +$players_ids = 358; +$match_ids = 181; + +$match_info_players = $this->matchContestantModel->where('player_id', $players_ids) +->where('match_id', $match_ids) +->where('status', 'in', [1,2]) +->select(); + +if (!empty($match_info_players)) { +return $this->errors("报名信息已存在!请勿重复报名!"); +} + +print_r($match_info_players);exit; +} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application/api/controller/News.php b/application/api/controller/News.php new file mode 100644 index 0000000..0dc2c81 --- /dev/null +++ b/application/api/controller/News.php @@ -0,0 +1,35 @@ +request->param("news_id"); + $archives = new Archives(); + + @$news = $archives->alias("archives") + ->join("peewee_cms_addonnews" . ' addon', 'addon.id=archives.id', 'LEFT') + ->where("archives.id", "eq", $news_id) + ->find() + ->toArray(); + + if (@empty($news)) { + $this->error("新闻信息不存在"); + } + + $this->result("获取成功", $news, 1); + } +} \ No newline at end of file diff --git a/application/api/controller/Players.php b/application/api/controller/Players.php new file mode 100644 index 0000000..3a72989 --- /dev/null +++ b/application/api/controller/Players.php @@ -0,0 +1,193 @@ +post("real_name"); + $add_data['gender'] = $request->post("gender"); + $add_data['birthday'] = $request->post("birthday"); + $add_data['age'] = $request->post("age"); + $add_data['country'] = $request->post("country"); + $add_data['province'] = $request->post("province"); + $add_data['city'] = $request->post("city"); + $add_data['district'] = $request->post("district"); + $add_data['address'] = $request->post("address"); + $add_data['player_pic'] = $request->post("player_pic"); + $add_data['experience'] = $request->post("experience"); + $add_data['guarder_name'] = $request->post("guarder_name"); + $add_data['guarder_phone'] = $request->post("guarder_phone"); + $add_data['guarder_card_number'] = $request->post("guarder_card_number"); + $add_data['guarder_card_type'] = $request->post("guarder_card_type"); + $add_data['guarder_card_front_view'] = $request->post("guarder_card_front_view"); + $add_data['guarder_card_back_view'] = $request->post("guarder_card_back_view"); + $add_data['card_type'] = $request->post("card_type"); + $add_data['card_number'] = $request->post("card_number"); + $add_data['card_front_view'] = $request->post("card_front_view"); + $add_data['card_back_view'] = $request->post("card_back_view"); + $add_data['card_view_same'] = $request->post("card_view_same"); + $add_data['guarder_card_view_same'] = $request->post("guarder_card_view_same"); + $add_data['asfc_user'] = $request->post("asfc_user"); + $add_data['membership_id'] = $request->post("membership_id"); + $add_data['flight_number'] = $request->post("flight_number"); + $add_data['club_members'] = $request->post("club_members"); + $add_data['club_name'] = $request->post("club_name"); + $add_data['duties'] = $request->post("duties"); + } + + public function getplayer() + { + + $token = $this->auth->getToken(); + $token_info = \app\common\library\Token::get($token); + $expiretime = date("Y-m-d H:i:s", $token_info['expiretime']); + + $where['member_id'] = $token_info['user_id']; + + $plyaerinfo = \app\common\model\Players::get($where); + + if (empty($plyaerinfo)) { + + $result['data'] = []; + $result['message'] = "请先申请认证飞手!"; + $result['code'] = 500; + + return json($result); + } + $plyaer['real_name'] = $plyaerinfo->real_name; + $plyaer['phone'] = $plyaerinfo->phone; + $plyaer['age'] = $plyaerinfo->age; + $plyaer['gender'] = $plyaerinfo->gender; + $plyaer['birthday'] = $plyaerinfo->birthday; + $plyaer['country'] = $plyaerinfo->country; + $plyaer['player_status'] = $plyaerinfo->player_status; + $plyaer['player_pic'] = $plyaerinfo->player_pic; + $plyaer['card_number'] = $plyaerinfo->card_number; + $plyaer['check_mark'] = ""; + if ($plyaerinfo->player_status == \app\common\model\Players::PLAYERS_MODIFY + || $plyaerinfo->player_status == \app\common\model\Players::PLAYERS_NO_PASS + ) { + $playerCheck = new PlayerCheck(); + $check_info = $playerCheck->where("member_id", $token_info['user_id']) + ->order("id", "DESC") + ->find(); + + $plyaer['check_mark'] = $check_info->mark; + } + + $result['data'] = $plyaer; + $result['message'] = "获取成功"; + $result['code'] = 200; + + return json($result); + + } + + public function get_player_detail() + { + + $token = $this->auth->getToken(); + $token_info = \app\common\library\Token::get($token); + $expiretime = date("Y-m-d H:i:s", $token_info['expiretime']); + + $where['member_id'] = $token_info['user_id']; + + $plyaerinfo = \app\common\model\Players::get($where); + + return $plyaerinfo; + } + + public function playerInfo(Request $request){ + $player_id = $request->param('player_id'); + $member_number = $request->param('member_number'); + if(!empty($player_id)){ + $player_res = model('players')->getPlayerId($player_id); + }elseif(!empty($member_number)){ + $user_info = $user->where('member_number',$member_number)->find(); + $player_res = $PlayersModel->field('id,real_name,age,province,player_pic,member_id')->where('member_id',$user_info['id'])->find(); + } + $data['player'] = $player_res; + $msg = ['code'=>200,'data'=>$data]; + return json($msg); + } + + public function getDraPlayer(ClubModel $clubModel,User $user,Request $request,ClubInvate $clubInvate,LeagueIntegral $leagueIntegral){ + $Archives = new Archives(); + $League = new League(); + $PlayersModel = new PlayersModel(); + $player_id = $request->param('player_id'); + $member_number = $request->param('member_number'); + if(!empty($player_id)){ + $club_res = $clubInvate->where('player_id',$player_id)->where('status',6)->where('deletetime',null)->find(); + $club_info = $clubModel->field('name,name_short,logo')->where('id',$club_res['club_id'])->find(); + $player_res = model('players')->getPlayerId($player_id); + $user_info = $user->where('id',$player_res['member_id'])->find(); + $player_res['member_number'] = $user_info['member_number']; + }elseif(!empty($member_number)){ + + $user_info = $user->where('member_number',$member_number)->find(); + $player_res = $PlayersModel->field('id,real_name,age,province,player_pic,member_id')->where('member_id',$user_info['id'])->find(); + $player_res['member_number'] = $member_number; + $club_res = $clubInvate->where('player_id',$player_res['id'])->where('status',6)->where('deletetime',null)->find(); + $club_info = $clubModel->field('name,name_short,logo')->where('id',$club_res['club_id'])->find(); + } + $Archives_res = $Archives->where('channel_id',73)->select(); + unset($Archives_res[1]);//国家集训队 + unset($Archives_res[4]);//测试站 + $station_integral = [];$i = 0; + $lastElement = end($Archives_res); + foreach ($Archives_res as $value){ + + $club_integral_res = $leagueIntegral->where('player_id',$user_info['member_number'])->where('match_id',$value['id'])->find(); + + if(!empty($club_integral_res)){ + //复用分站积分排名 + // $res = $League->player_integral($value['id'],$user_info['member_number']); + // var_dump($res);exit; + $station_integral[] = array('match_id'=>$value['id'],'name'=> $value['title'],'sum_grade'=>$club_integral_res['grade'],'number'=>$club_integral_res['number'],'nostart'=>0); + }else{ + $station_integral[] = array('match_id'=>$value['id'],'name'=> $value['title'],'sum_grade'=>0,'number'=>0,'nostart'=>0); + } + //判断最后一场比赛是否未开始 + if($value['id'] == $lastElement['id']){ + $res = db("cms_addonproducts")->where('id',$value['id'])->find(); + if(strtotime($res['stime']) > time()){ + $station_integral[$i]['nostart'] = 1; + } + // var_dump($res['stime']);exit; + } + // var_dump($club_integral_res);exit; + $i++; + } + + $data['player'] = $player_res; + $data['club'] = $club_info; + $data['integral'] = $station_integral; + $msg = ['code'=>200,'data'=>$data]; + return json($msg); + // var_dump($integral_res);exit; + } + +} \ No newline at end of file diff --git a/application/api/controller/Ranking.php b/application/api/controller/Ranking.php new file mode 100644 index 0000000..87ee5f6 --- /dev/null +++ b/application/api/controller/Ranking.php @@ -0,0 +1,52 @@ +request->param("match_id"); + $player_id = $this->request->param("player_id"); + $http_get = $this->btnGet($this->url . "?match_id={$match_id}&player_id={$player_id}"); + + return $http_get; + } + + public function btnGet($url) + { + # 初始化一个curl会话 + $ch = curl_init(); + + # 判断是否是https + if (stripos($url, "https://") !== false) { + # 禁用后cURL将终止从服务端进行验证 + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + # 使用的SSL版本(2 或 3) + curl_setopt($ch, CURLOPT_SSLVERSION, 1); + } + # 设置请求地址 + curl_setopt($ch, CURLOPT_URL, $url); + # 在启用CURLOPT_RETURNTRANSFER的时候,返回原生的(Raw)输出 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + + # 执行这个请求 + $output = curl_exec($ch); + # 关闭这个请求 + curl_close($ch); + return $output; + } + +} \ No newline at end of file diff --git a/application/api/controller/Share.php b/application/api/controller/Share.php new file mode 100644 index 0000000..1c36f12 --- /dev/null +++ b/application/api/controller/Share.php @@ -0,0 +1,77 @@ +param("url"); + // $url = 'http://www.baidu.com'; + $access_tokenurl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wxae874d12ca3f56b8&secret=0984255faae9d7c0c8a9926aa396d22b'; + $tokenBase = $this->http_get($access_tokenurl); + $ticketurl = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='.$tokenBase['access_token'].'&type=jsapi'; + $ticketbase = $this->http_get($ticketurl); + //生成随机字符串 + $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + $str = ""; + for ($i = 0; $i < 16; $i++) { + $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); + } + + $wxdata['noncestr'] = $str; + $wxdata['jsapi_ticket'] = $ticketbase['ticket']; + $wxdata['timestamp'] = time(); + $wxdata['url'] = $url; + + $string = "jsapi_ticket=".$wxdata['jsapi_ticket']."&noncestr=".$wxdata['noncestr']."×tamp=".$wxdata['timestamp']."&url=".$wxdata['url']; + + $signature = sha1($string); + + $signPackage = array( + // "appId" => 'wxae874d12ca3f56b8', + "nonceStr" => $wxdata['noncestr'], + "timestamp" => $wxdata['timestamp'], + "url" => $wxdata['url'], + "signature" => $signature, + // "rawString" => $string + ); + + return json($signPackage); + } + + public function http_get($url, $header = []) + { + if (empty($header)) { + $header = [ + "Content-type:application/json;", + "Accept:application/json" + ]; + } + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_HTTPHEADER, $header); + $response = curl_exec($curl); + curl_close($curl); + $response = json_decode($response, true); + + return $response; + } +} diff --git a/application/api/controller/Sms.php b/application/api/controller/Sms.php new file mode 100644 index 0000000..eee76ff --- /dev/null +++ b/application/api/controller/Sms.php @@ -0,0 +1,123 @@ +request->post("dialCode"); + $mobile = $this->request->post("mobile"); + $event = $this->request->post("event"); + $event = $event ? $event : 'register'; + // $dialCode = '82'; + //中国大陆手机号 + $check_mobile = $mobile; + if($dialCode == "+86"){ + if (!$mobile || !\think\Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('手机号不正确')); + } + }elseif(!empty($dialCode) && $dialCode != "+86"){ + $check_mobile = $dialCode.$mobile; + $event = 'inter_'.$event; + } + + + $last = Smslib::get($check_mobile, $event); + if ($last && time() - $last['createtime'] < 60) { + $this->error(__('发送频繁')); + } + $ipSendTotal = \app\common\model\Sms::where(['ip' => $this->request->ip()])->whereTime('createtime', '-1 hours')->count(); + if ($ipSendTotal >= 5) { + $this->error(__('发送频繁')); + } + if ($event) { + $userinfo = User::getByMobile($mobile); + // var_dump($userinfo);exit; + if (in_array($event, ['register','inter_register']) && $userinfo) { + //已被注册 + $this->error(__('手机号已被注册')); + } elseif (in_array($event, ['changemobile','inter_changemobile']) && $userinfo) { + //被占用 + $this->error(__('已被占用')); + } elseif (in_array($event, ['changepwd', 'resetpwd','inter_resetpwd']) && !$userinfo) { + //未注册 + $this->error(__('未注册')); + } + } + if (!Hook::get('sms_send')) { + $this->error(__('请在后台插件管理安装短信验证插件')); + } + + if($dialCode == "+86"){ + $ret = Smslib::send($check_mobile, null, $event); + }elseif(!empty($dialCode) && $dialCode != "+86"){ + $ret = Submail::send($check_mobile, null, $event); + } + + + if ($ret) { + $this->success(__('发送成功')); + } else { + $this->error(__('发送失败,请检查短信配置是否正确')); + } + } + + /** + * 检测验证码 + * + * @ApiMethod (POST) + * @param string $mobile 手机号 + * @param string $event 事件名称 + * @param string $captcha 验证码 + */ + public function check() + { + $mobile = $this->request->post("mobile"); + $event = $this->request->post("event"); + $event = $event ? $event : 'register'; + $captcha = $this->request->post("captcha"); + + if (!$mobile || !\think\Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('手机号不正确')); + } + if ($event) { + $userinfo = User::getByMobile($mobile); + if ($event == 'register' && $userinfo) { + //已被注册 + $this->error(__('已被注册')); + } elseif (in_array($event, ['changemobile']) && $userinfo) { + //被占用 + $this->error(__('已被占用')); + } elseif (in_array($event, ['changepwd', 'resetpwd']) && !$userinfo) { + //未注册 + $this->error(__('未注册')); + } + } + $ret = Smslib::check($mobile, $captcha, $event); + if ($ret) { + $this->success(__('成功')); + } else { + $this->error(__('验证码不正确')); + } + } +} diff --git a/application/api/controller/Submail.php b/application/api/controller/Submail.php new file mode 100644 index 0000000..4e2b970 --- /dev/null +++ b/application/api/controller/Submail.php @@ -0,0 +1,102 @@ +ip(); + $sms = \app\common\model\Sms::create(['event' => $event, 'mobile' => $mobile, 'code' => $code, 'ip' => $ip, 'createtime' => $time]); + // var_dump($sms);exit; + $result = self::test($mobile,$code); + // $result = Hook::listen('sms_send', $sms, null, true); + if (!$result) { + $sms->delete(); + return false; + } + return true; + } + public static function test($mobile,$code){ + + /***************** + * 加密请求 示例代码 + ******************/ + //appid参数 appkey参数在 国际短信-创建/管理AppID中获取 + //手机号支持单个 + //模板ID 国际短信-创建/管理国际短信模板中获得 + //国际短信模板对应变量 + // 若模板为:【SUBMAIL】您的验证码是@var(code),请在@var(time)内输入。国际短信模板对应变量如下 + // 变量名和自定义内容相对应即可 + $appid = '64253'; //appid参数 + $appkey = '9bcc32d7a0256162dcb47d11ad9dfd02'; //appkey参数 + $to = $mobile; //收信人 手机号码 + $project_id = 'kSPUL1'; //模板ID + $vars = json_encode(array( //模板对应变量 + 'code' => $code, + // 'time' => '三分钟' + )); + + //通过接口获取时间戳 + $ch = curl_init(); + curl_setopt_array($ch, array( + CURLOPT_URL => 'https://api-v4.mysubmail.com/service/timestamp.json', + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_POST => 0 + )); + $output = curl_exec($ch); + curl_close($ch); + $output = json_decode($output, true); + $timestamp = $output['timestamp']; + + $post_data = array( + "appid" => $appid, + "to" => $to, + "project" => $project_id, + "vars" => $vars, + "timestamp" => $timestamp, + "sign_type" => 'md5', + "sign_version" => 2, + ); + //整理生成签名所需参数 + $temp = $post_data; + unset($temp['vars']); + ksort($temp); + reset($temp); + $tempStr = ""; + foreach ($temp as $key => $value) { + $tempStr .= $key . "=" . $value . "&"; + } + $tempStr = substr($tempStr, 0, -1); + //生成签名 + $post_data['signature'] = md5($appid . $appkey . $tempStr . $appid . $appkey); + + + $ch = curl_init(); + curl_setopt_array($ch, array( + CURLOPT_URL => 'https://api-v4.mysubmail.com/internationalsms/xsend.json', + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => $post_data + )); + $output = curl_exec($ch); + curl_close($ch); + + $res = json_decode($output,true); + // var_dump($res);exit; + if($res['status'] != 'success'){ + return false; + } + return true; + // echo json_encode($output); + } +} \ No newline at end of file diff --git a/application/api/controller/Tiktok.php b/application/api/controller/Tiktok.php new file mode 100644 index 0000000..17b426a --- /dev/null +++ b/application/api/controller/Tiktok.php @@ -0,0 +1,270 @@ +auth; + + //监听注册登录注销的事件 + Hook::add('user_login_successed', function ($user) use ($auth) { + $expire = input('post.keeplogin') ? 30 * 86400 : 0; + Cookie::set('uid', $user->id, $expire); + Cookie::set('token', $auth->getToken(), $expire); + }); + Hook::add('user_register_successed', function ($user) use ($auth) { + Cookie::set('uid', $user->id); + Cookie::set('token', $auth->getToken()); + }); + Hook::add('user_logout_successed', function ($user) use ($auth) { + Cookie::delete('uid'); + Cookie::delete('token'); + }); + + $platform = $this->request->param('platform'); + $third_id = $this->request->param('third_id'); + + $userinfo = Third::get($third_id); + + if (!empty($userinfo)) { + $userinfo = $userinfo->toArray(); + } else { + $this->error("未找到您的绑定信息,请先绑定用户!"); + } + // 授权成功后的回调 + $loginret = Service::connect($platform, $userinfo); + if ($loginret) { + $this->result("登陆成功", ["token" => $auth->getToken(), "user_id" => $auth->getUser()->id]); + } else { + $this->error("登录失败,请重试"); + } + } + + public function check() + { + $code = $this->request->param("code"); + $anonymous_code = $this->request->param("anonymous_code", ""); + if (empty($code)) { + $this->error("param error"); + } + // 根据 code 获取抖音 openid + $data['appid'] = $this->appid; + $data['secret'] = $this->secret; + $data['code'] = $code; + $data['anonymous_code'] = $anonymous_code; + $result = $this->curlPost($this->url, $data, 5, "", "json"); + $result = json_decode($result, true); + + if ($result['err_no'] == 0) { + // 如果返回 openid 则去查询是否存在数据库绑定 + $third = new Third(); + $douyin_third = $third->where("openid", $result['data']['openid']) + ->where("unionid", $result['data']['unionid']) + ->where("platform", "douyin") + ->where("apptype", "dy") + ->find(); + + if (empty($douyin_third)) { + $this->result("请先绑定用户", [ + 'openid' => $result['data']['openid'], + 'unionid' => $result['data']['unionid'], + ], 2); + } else { + // 如果不为空 则直接发起登录 + $auth = $this->auth; + + //监听注册登录注销的事件 + Hook::add('user_login_successed', function ($user) use ($auth) { + $expire = input('post.keeplogin') ? 30 * 86400 : 0; + Cookie::set('uid', $user->id, $expire); + Cookie::set('token', $auth->getToken(), $expire); + }); + Hook::add('user_register_successed', function ($user) use ($auth) { + Cookie::set('uid', $user->id); + Cookie::set('token', $auth->getToken()); + }); + Hook::add('user_logout_successed', function ($user) use ($auth) { + Cookie::delete('uid'); + Cookie::delete('token'); + }); + $douyin_third = $douyin_third->toArray(); + // 授权成功后的回调 + $loginret = Service::connect("douyin", $douyin_third); + if ($loginret) { + $this->result("登陆成功", ["token" => $auth->getToken(), "user_id" => $auth->getUser()->id], 1); + } else { + $this->error("登录失败,请重试", [], 0); + } + } + } + + switch ($result['err_no']) { + case "-1": + $this->error("抖音系统错误!", [], 0); + break; + case "40014": + $this->error("param error", [], 0); + break; + case "40015": + $this->error("APPID error", [], 0); + break; + case "40017": + $this->error("secret error", [], 0); + break; + case "40018": + $this->error("code error", [], 0); + break; + case "40019": + $this->error("acode error", [], 0); + break; + default: + $this->error("error", [], 0); + break; + } + } + + public function bind() + { + + $account = $this->request->param("username"); + $password = $this->request->param("password"); + $openid = $this->request->param("openid"); + $unionid = $this->request->param("unionid"); + $nickanme = $this->request->param("nickname", ""); + $avatarUrl = $this->request->param("avatarUrl", ""); + + // 取出加盐因子 md5(md5($password) . $salt) + $user = new \app\common\model\User(); + $field = Validate::is($account, 'email') ? 'email' : (Validate::regex($account, '/^1\d{10}$/') ? 'mobile' : 'username'); + $user = User::get([$field => $account]); + if (!$user) { + $this->error('账号或者密码错误'); + } + + if ($user->status != 'normal') { + $this->error('用户已被锁定'); + } + + if ( + md5(md5($password) . $user->salt) == $user->password || + password_verify($password, $user->password) + ) { + // 查询之前有没有绑定抖音 + $third = \addons\third\model\Third::where('user_id', $user->id) + ->where("platform", "douyin") + ->where("apptype", "dy") + ->find(); + + if ($third) { + $this->error("已绑定账号,请勿重复绑定"); + } + + // 添加绑定抖音 + $param['user_id'] = $user->id; + $param['platform'] = "douyin"; + $param['apptype'] = "dy"; + $param['unionid'] = $unionid; + $param['openid'] = $openid; + $param['createtime'] = time(); + $param['openname'] = $nickanme; + $param['wechat_avatar'] = $avatarUrl; + + + $third = \addons\third\model\Third::create($param); + if ($third) { + $auth = $this->auth; + + //监听注册登录注销的事件 + Hook::add('user_login_successed', function ($user) use ($auth) { + $expire = input('post.keeplogin') ? 30 * 86400 : 0; + Cookie::set('uid', $user->id, $expire); + Cookie::set('token', $auth->getToken(), $expire); + }); + Hook::add('user_register_successed', function ($user) use ($auth) { + Cookie::set('uid', $user->id); + Cookie::set('token', $auth->getToken()); + }); + Hook::add('user_logout_successed', function ($user) use ($auth) { + Cookie::delete('uid'); + Cookie::delete('token'); + }); + + // 直接拉起登录 + $third = \addons\third\model\Third::where('user_id', $user->id) + ->where("platform", "douyin") + ->where("apptype", "dy") + ->find(); + $third = $third->toArray(); + // 授权成功后的回调 + $loginret = Service::connect("douyin", $third); + if ($loginret) { + $this->result("登陆成功", ["token" => $auth->getToken(), "user_id" => $auth->getUser()->id], 1); + } else { + $this->error("登录失败,请重试"); + } + } else { + $this->error("账号绑定失败,请重试"); + } + } + + $this->error('账号或者密码错误'); + } + + function curlPost($url, $post_data = array(), $timeout = 5, $header = "", $data_type = "") { + $header = empty($header) ? '' : $header; + + //支持json数据数据提交 + if($data_type == 'json'){ + $post_string = json_encode($post_data); + }elseif($data_type == 'array') { + $post_string = $post_data; + }elseif(is_array($post_data)){ + $post_string = http_build_query($post_data, '', '&'); + } + + $ch = curl_init(); // 启动一个CURL会话 + curl_setopt($ch, CURLOPT_URL, $url); // 要访问的地址 + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查 // https请求 不验证证书和hosts + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在 + curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器 + //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转 + //curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer + curl_setopt($ch, CURLOPT_POST, true); // 发送一个常规的Post请求 + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string); // Post提交的数据包 + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); // 设置超时限制防止死循环 + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + //curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 获取的信息以文件流的形式返回 +// curl_setopt($ch, CURLOPT_HTTPHEADER, $header); //模拟的header头 + $result = curl_exec($ch); + + // 打印请求的header信息 + //$a = curl_getinfo($ch); + //var_dump($a); + + curl_close($ch); + return $result; + } +} \ No newline at end of file diff --git a/application/api/controller/Timerenctry.php b/application/api/controller/Timerenctry.php new file mode 100644 index 0000000..e69de29 diff --git a/application/api/controller/Token.php b/application/api/controller/Token.php new file mode 100644 index 0000000..75b280a --- /dev/null +++ b/application/api/controller/Token.php @@ -0,0 +1,42 @@ +auth->getToken(); + $tokenInfo = \app\common\library\Token::get($token); + $this->success('', ['token' => $tokenInfo['token'], 'expires_in' => $tokenInfo['expires_in']]); + } + + /** + * 刷新Token + * + */ + public function refresh() + { + //删除源Token + $token = $this->auth->getToken(); + \app\common\library\Token::delete($token); + //创建新Token + $token = Random::uuid(); + \app\common\library\Token::set($token, $this->auth->id, 2592000); + $tokenInfo = \app\common\library\Token::get($token); + $this->success('', ['token' => $tokenInfo['token'], 'expires_in' => $tokenInfo['expires_in']]); + } +} diff --git a/application/api/controller/User.php b/application/api/controller/User.php new file mode 100644 index 0000000..277d5e6 --- /dev/null +++ b/application/api/controller/User.php @@ -0,0 +1,568 @@ +error(__('User center already closed')); + } + + } + + /** + * 会员中心 + */ + public function index() + { + $this->success('', ['welcome' => $this->auth->nickname]); + } + + /** + * 会员登录 + * + * @ApiMethod (POST) + * @param string $account 账号 + * @param string $password 密码 + */ + public function login() + { + $account = $this->request->post('account'); + $password = $this->request->post('password'); + if (!$account || !$password) { + $this->error(__('Invalid parameters')); + } + $ret = $this->auth->login($account, $password); + if ($ret) { + $data = ['userinfo' => $this->auth->getUserinfo()]; + $this->success(__('Logged in successful'), $data); + } else { + $this->error($this->auth->getError()); + } + } + + /** + * 手机验证码登录 + * + * @ApiMethod (POST) + * @param string $mobile 手机号 + * @param string $captcha 验证码 + */ + public function mobilelogin() + { + $mobile = $this->request->post('mobile'); + $captcha = $this->request->post('captcha'); + if (!$mobile || !$captcha) { + $this->error(__('Invalid parameters')); + } + if (!Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('Mobile is incorrect')); + } + if (!Sms::check($mobile, $captcha, 'mobilelogin')) { + $this->error(__('Captcha is incorrect')); + } + $user = \app\common\model\User::getByMobile($mobile); + if ($user) { + if ($user->status != 'normal') { + $this->error(__('Account is locked')); + } + //如果已经有账号则直接登录 + $ret = $this->auth->direct($user->id); + } else { + $ret = $this->auth->register($mobile, Random::alnum(), '', $mobile, []); + } + if ($ret) { + Sms::flush($mobile, 'mobilelogin'); + $data = ['userinfo' => $this->auth->getUserinfo()]; + $this->success(__('Logged in successful'), $data); + } else { + $this->error($this->auth->getError()); + } + } + + /** + * 注册会员 + * + * @ApiMethod (POST) + * @param string $username 用户名 + * @param string $password 密码 + * @param string $email 邮箱 + * @param string $mobile 手机号 + * @param string $code 验证码 + */ + public function register() + { + $username = $this->request->post('username'); + $password = $this->request->post('password'); + $email = $this->request->post('email'); + $mobile = $this->request->post('mobile'); + $code = $this->request->post('code'); + if (!$username || !$password) { + $this->error(__('Invalid parameters')); + } + if ($email && !Validate::is($email, "email")) { + $this->error(__('Email is incorrect')); + } + if ($mobile && !Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('Mobile is incorrect')); + } + $ret = Sms::check($mobile, $code, 'register'); + if (!$ret) { + $this->error(__('Captcha is incorrect')); + } + $ret = $this->auth->register($username, $password, $email, $mobile, []); + if ($ret) { + $data = ['userinfo' => $this->auth->getUserinfo()]; + $this->success(__('Sign up successful'), $data); + } else { + $this->error($this->auth->getError()); + } + } + + /** + * 注册会员 + * + * @ApiMethod (POST) + * @param string $username 用户名 + * @param string $password 密码 + * @param string $email 邮箱 + * @param string $mobile 手机号 + * @param string $code 验证码 + */ + public function registerTikTok() + { + $username = $this->request->post('username'); + $password = $this->request->post('password'); + $mobile = $this->request->post('mobile'); + $code = $this->request->post('code'); + if (!$username || !$password) { + $this->error(__('Invalid parameters')); + } + if ($mobile && !Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('Mobile is incorrect')); + } + $ret = Sms::check($mobile, $code, 'register'); + if (!$ret) { + $this->error(__('Captcha is incorrect')); + } + $ret = $this->auth->register($username, $password, "", $mobile, []); + + if ($ret) { + $data = ['userinfo' => $this->auth->getUserinfo()]; + $this->success(__('Sign up successful'), $data); + } else { + $this->error($this->auth->getError()); + } + } + + /** + * 退出登录 + * @ApiMethod (POST) + */ + public function logout() + { + if (!$this->request->isPost()) { + $this->error(__('Invalid parameters')); + } + $this->auth->logout(); + $this->success('退出成功/Logout successful'); + } + + /** + * 修改会员个人信息 + * + * @ApiMethod (POST) + * @param string $avatar 头像地址 + * @param string $username 用户名 + * @param string $nickname 昵称 + * @param string $bio 个人简介 + */ + public function profile() + { + $user = $this->auth->getUser(); + $username = $this->request->post('username'); + $nickname = $this->request->post('nickname'); + $bio = $this->request->post('bio'); + $avatar = $this->request->post('avatar', '', 'trim,strip_tags,htmlspecialchars'); + if ($username) { + $exists = \app\common\model\User::where('username', $username)->where('id', '<>', $this->auth->id)->find(); + if ($exists) { + $this->error(__('Username already exists')); + } + $user->username = $username; + } + if ($nickname) { + $exists = \app\common\model\User::where('nickname', $nickname)->where('id', '<>', $this->auth->id)->find(); + if ($exists) { + $this->error(__('Nickname already exists')); + } + $user->nickname = $nickname; + } + $user->bio = $bio; + $user->avatar = $avatar; + $user->save(); + $this->success(); + } + + /** + * 修改邮箱 + * + * @ApiMethod (POST) + * @param string $email 邮箱 + * @param string $captcha 验证码 + */ + public function changeemail() + { + $user = $this->auth->getUser(); + $email = $this->request->post('email'); + $captcha = $this->request->post('captcha'); + if (!$email || !$captcha) { + $this->error(__('Invalid parameters')); + } + if (!Validate::is($email, "email")) { + $this->error(__('Email is incorrect')); + } + if (\app\common\model\User::where('email', $email)->where('id', '<>', $user->id)->find()) { + $this->error(__('Email already exists')); + } + $result = Ems::check($email, $captcha, 'changeemail'); + if (!$result) { + $this->error(__('Captcha is incorrect')); + } + $verification = $user->verification; + $verification->email = 1; + $user->verification = $verification; + $user->email = $email; + $user->save(); + + Ems::flush($email, 'changeemail'); + $this->success(); + } + + /** + * 修改手机号 + * + * @ApiMethod (POST) + * @param string $mobile 手机号 + * @param string $captcha 验证码 + */ + public function changemobile() + { + $user = $this->auth->getUser(); + $dialCode = $this->request->post("dialCode"); + $mobile = $this->request->post('mobile'); + $captcha = $this->request->post('captcha'); + if (!$mobile || !$captcha) { + $this->error(__('Invalid parameters')); + } + $check_mobile = $mobile; + $event = 'changemobile'; + if($dialCode == "+86"){ + if (!Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('Mobile is incorrect')); + } + }elseif(!empty($dialCode) && $dialCode != "+86"){ + $check_mobile = $dialCode.$mobile; + $event = 'inter_'.$event; + } + + if (\app\common\model\User::where('mobile', $mobile)->where('id', '<>', $user->id)->find()) { + $this->error(__('Mobile already exists')); + } + $result = Sms::check($check_mobile, $captcha, $event); + if (!$result) { + $this->error(__('Captcha is incorrect')); + } + $verification = $user->verification; + $verification->mobile = 1; + $user->verification = $verification; + $user->mobile = $mobile; + $user->save(); + + Sms::flush($check_mobile, $event); + $this->success('修改成功/change success'); + } + + public function tiktokchangemobile() + { + $mobile = $this->request->post('mobile'); + $captcha = $this->request->post('captcha'); + $token = $this->request->param("token"); + $token_info = \app\common\library\Token::get($token); + $user = \app\common\model\User::get($token_info['user_id']); + + $user = new \app\admin\model\User(); + $res = $user->where('mobile',$mobile)->find(); + if(!empty($res))$this->error(__('手机号已存在')); + + if (!$mobile || !$captcha) { + $this->error(__('Invalid parameters')); + } + if (!Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('Mobile is incorrect')); + } + if (\app\common\model\User::where('mobile', $mobile)->where('id', '<>', $user->id)->find()) { + $this->error(__('Mobile already exists')); + } + $result = Sms::check($mobile, $captcha, 'changemobile'); + if (!$result) { + $this->error(__('Captcha is incorrect')); + } + $verification = $user->verification; + $verification->mobile = 1; + $user->verification = $verification; + $user->mobile = $mobile; + $user->save(); + + Sms::flush($mobile, 'changemobile'); + $this->success(); + } + + /** + * 第三方登录 + * + * @ApiMethod (POST) + * @param string $platform 平台名称 + * @param string $code Code码 + */ + public function third() + { + $url = url('user/index'); + $platform = $this->request->post("platform"); + $code = $this->request->post("code"); + $config = get_addon_config('third'); + if (!$config || !isset($config[$platform])) { + $this->error(__('Invalid parameters')); + } + $app = new \addons\third\library\Application($config); + //通过code换access_token和绑定会员 + $result = $app->{$platform}->getUserInfo(['code' => $code]); + if ($result) { + $loginret = \addons\third\library\Service::connect($platform, $result); + if ($loginret) { + $data = [ + 'userinfo' => $this->auth->getUserinfo(), + 'thirdinfo' => $result + ]; + $this->success(__('Logged in successful'), $data); + } + } + $this->error(__('Operation failed'), $url); + } + + /** + * 重置密码 + * + * @ApiMethod (POST) + * @param string $mobile 手机号 + * @param string $newpassword 新密码 + * @param string $captcha 验证码 + */ + public function resetpwd() + { + $type = $this->request->post("type"); + $mobile = $this->request->post("mobile"); + $email = $this->request->post("email"); + $newpassword = $this->request->post("newpassword"); + $captcha = $this->request->post("captcha"); + $dialCode = $this->request->post("dialCode"); + + if (!$newpassword || !$captcha) { + $this->error(__('Invalid parameters')); + } + //验证Token + if (!Validate::make()->check(['newpassword' => $newpassword], ['newpassword' => 'require|regex:\S{6,30}'])) { + $this->error(__('Password must be 6 to 30 characters')); + } + if ($type == 'mobile') { + $event = 'resetpwd'; + $check_mobile = $mobile; + if($dialCode == "+86"){ + if (!Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('Mobile is incorrect')); + } + }elseif(!empty($dialCode) && $dialCode != "+86"){ + $check_mobile = $dialCode.$mobile; + $event = 'inter_'.$event; + } + + + $user = \app\common\model\User::getByMobile($mobile); + if (!$user) { + $this->error(__('User not found')); + } + $ret = Sms::check($check_mobile, $captcha, $event); + if (!$ret) { + $this->error(__('Captcha is incorrect')); + } + Sms::flush($check_mobile, 'resetpwd'); + } else { + if (!Validate::is($email, "email")) { + $this->error(__('Email is incorrect')); + } + $user = \app\common\model\User::getByEmail($email); + if (!$user) { + $this->error(__('User not found')); + } + $ret = Ems::check($email, $captcha, 'resetpwd'); + if (!$ret) { + $this->error(__('Captcha is incorrect')); + } + Ems::flush($email, 'resetpwd'); + } + //模拟一次登录 + $this->auth->direct($user->id); + $ret = $this->auth->changepwd($newpassword, '', true); + if ($ret) { + $this->success(__('Reset password successful')); + } else { + $this->error($this->auth->getError()); + } + } + + + public function resetpwdTiktoks() + { + $token = $this->request->param("token"); + $newpassword = $this->request->post("newpassword"); + $renewpassword = $this->request->post("renewpassword"); + $token_info = \app\common\library\Token::get($token); + $user = \app\common\model\User::get($token_info['user_id']); + + if (!$newpassword || !$renewpassword) { + $this->error(__('Invalid parameters')); + } + if($newpassword != $renewpassword){ + $this->error(__('两次密码不一致')); + } + //验证Token + if (!Validate::make()->check(['newpassword' => $newpassword], ['newpassword' => 'require|regex:\S{6,30}'])) { + $this->error(__('Password must be 6 to 30 characters')); + } + //模拟一次登录 + $this->auth->direct($user->id); + $ret = $this->auth->changepwd($newpassword, '', true); + if ($ret) { + $this->success(__('Reset password successful')); + } else { + $this->error($this->auth->getError()); + } + } + + /** + * 重置密码 + * + * @ApiMethod (POST) + * @param string $mobile 手机号 + * @param string $newpassword 新密码 + * @param string $captcha 验证码 + */ + public function resetpwdTiktok() + { + $type = $this->request->post("type", "mobile"); + $mobile = $this->request->post("mobile"); + $newpassword = $this->request->post("newpassword"); + $captcha = $this->request->post("captcha"); + if (!$newpassword || !$captcha) { + $this->error(__('Invalid parameters')); + } + //验证Token + if (!Validate::make()->check(['newpassword' => $newpassword], ['newpassword' => 'require|regex:\S{6,30}'])) { + $this->error(__('Password must be 6 to 30 characters')); + } + if ($type == 'mobile') { + if (!Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('Mobile is incorrect')); + } + $user = \app\common\model\User::getByMobile($mobile); + if (!$user) { + $this->error(__('User not found')); + } + $ret = Sms::check($mobile, $captcha, 'resetpwd'); + if (!$ret) { + $this->error(__('Captcha is incorrect')); + } + Sms::flush($mobile, 'resetpwd'); + } + //模拟一次登录 + $this->auth->direct($user->id); + $ret = $this->auth->changepwd($newpassword, '', true); + if ($ret) { + $this->success(__('Reset password successful')); + } else { + $this->error($this->auth->getError()); + } + } + + + public function getuser() + { + $token = $this->request->param("token"); + $token_info = \app\common\library\Token::get($token); + $expiretime = date("Y-m-d H:i:s", $token_info['expiretime']); + + $user = \app\common\model\User::get($token_info['user_id']); + if (empty($user)) { + $this->error("用户数据为空"); + } + + + $result['data']['player_number'] = $user->member_number; + $result['data']['avatar'] = $user->avatar; + $result['data']['mobile'] = $user->mobile; + $result['data']['nickname'] = $user->nickname; + $result['data']['wechat'] = $user->wechat; + $result['data']['qq'] = $user->qq; + $result['code'] = 200; + $result['message'] = "获取成功"; + + return json($result); + + + } + + public function getusertest() + { + $token = $this->request->param("token"); + $var = \app\common\library\Token::get($token); + print_r($var);exit; + $token1 = new Token(); + $user_id = $this->request->post("user_id"); + + $user = \app\common\model\User::get($user_id); + + if (empty($user)) { + $this->error("用户数据为空"); + } + + $result['data']['player_number'] = $user->member_number; + $result['data']['avatar'] = $user->avatar; + $result['data']['mobile'] = $user->mobile; + $result['data']['nickname'] = $user->nickname; + $result['data']['wechat'] = $user->wechat; + $result['data']['qq'] = $user->qq; + $result['code'] = 200; + $result['message'] = "获取成功"; + + return json($result); + + + } +} diff --git a/application/api/controller/Validate.php b/application/api/controller/Validate.php new file mode 100644 index 0000000..caaafe1 --- /dev/null +++ b/application/api/controller/Validate.php @@ -0,0 +1,172 @@ +request->post('email'); + $id = (int)$this->request->post('id'); + $count = User::where('email', '=', $email)->where('id', '<>', $id)->count(); + if ($count > 0) { + $this->error(__('邮箱已经被占用')); + } + $this->success(); + } + + /** + * 检测用户名 + * + * @ApiMethod (POST) + * @param string $username 用户名 + * @param string $id 排除会员ID + */ + public function check_username_available() + { + $username = $this->request->post('username'); + $id = (int)$this->request->post('id'); + $count = User::where('username', '=', $username)->where('id', '<>', $id)->count(); + if ($count > 0) { + $this->error(__('用户名已经被占用')); + } + $this->success(); + } + + /** + * 检测昵称 + * + * @ApiMethod (POST) + * @param string $nickname 昵称 + * @param string $id 排除会员ID + */ + public function check_nickname_available() + { + $nickname = $this->request->post('nickname'); + $id = (int)$this->request->post('id'); + $count = User::where('nickname', '=', $nickname)->where('id', '<>', $id)->count(); + if ($count > 0) { + $this->error(__('昵称已经被占用')); + } + $this->success(); + } + + /** + * 检测手机 + * + * @ApiMethod (POST) + * @param string $mobile 手机号 + * @param string $id 排除会员ID + */ + public function check_mobile_available() + { + $mobile = $this->request->post('mobile'); + $id = (int)$this->request->post('id'); + $count = User::where('mobile', '=', $mobile)->where('id', '<>', $id)->count(); + if ($count > 0) { + $this->error(__('该手机号已经占用')); + } + $this->success(); + } + + /** + * 检测手机 + * + * @ApiMethod (POST) + * @param string $mobile 手机号 + */ + public function check_mobile_exist() + { + $mobile = $this->request->post('mobile'); + $count = User::where('mobile', '=', $mobile)->count(); + if (!$count) { + $this->error(__('手机号不存在')); + } + $this->success(); + } + + /** + * 检测邮箱 + * + * @ApiMethod (POST) + * @param string $mobile 邮箱 + */ + public function check_email_exist() + { + $email = $this->request->post('email'); + $count = User::where('email', '=', $email)->count(); + if (!$count) { + $this->error(__('邮箱不存在')); + } + $this->success(); + } + + /** + * 检测手机验证码 + * + * @ApiMethod (POST) + * @param string $mobile 手机号 + * @param string $captcha 验证码 + * @param string $event 事件 + */ + public function check_sms_correct() + { + $mobile = $this->request->post('mobile'); + $captcha = $this->request->post('captcha'); + $event = $this->request->post('event'); + $dialCode = $this->request->post("dialCode"); + if($dialCode == "+86"){ + if (!$mobile || !\think\Validate::regex($mobile, "^1\d{10}$")) { + $this->error(__('手机号不正确')); + } + }elseif(!empty($dialCode) && $dialCode != "+86"){ + $mobile = $dialCode.$mobile; + $event = 'inter_'.$event; + } + if (!\app\common\library\Sms::check($mobile, $captcha, $event)) { + $this->error(__('验证码不正确')); + } + $this->success(); + } + + /** + * 检测邮箱验证码 + * + * @ApiMethod (POST) + * @param string $email 邮箱 + * @param string $captcha 验证码 + * @param string $event 事件 + */ + public function check_ems_correct() + { + $email = $this->request->post('email'); + $captcha = $this->request->post('captcha'); + $event = $this->request->post('event'); + if (!\app\common\library\Ems::check($email, $captcha, $event)) { + $this->error(__('验证码不正确')); + } + $this->success(); + } +} diff --git a/application/api/lang/zh-cn.php b/application/api/lang/zh-cn.php new file mode 100644 index 0000000..c28bfd3 --- /dev/null +++ b/application/api/lang/zh-cn.php @@ -0,0 +1,102 @@ + '保持会话', + 'Username' => '用户名', + 'User id' => '会员ID', + 'Nickname' => '昵称', + 'Password' => '密码', + 'Sign up' => '注 册', + 'Sign in' => '登 录', + 'Sign out' => '退 出', + 'Guest' => '游客', + 'Welcome' => '%s,你好!', + 'Add' => '添加', + 'Edit' => '编辑', + 'Delete' => '删除', + 'Move' => '移动', + 'Name' => '名称', + 'Status' => '状态', + 'Weigh' => '权重', + 'Operate' => '操作', + 'Warning' => '温馨提示', + 'Default' => '默认', + 'Article' => '文章', + 'Page' => '单页', + 'OK' => '确定', + 'Cancel' => '取消', + 'Loading' => '加载中', + 'More' => '更多', + 'Normal' => '正常', + 'Hidden' => '隐藏', + 'Submit' => '提交', + 'Reset' => '重置', + 'Execute' => '执行', + 'Close' => '关闭', + 'Search' => '搜索', + 'Refresh' => '刷新', + 'First' => '首页', + 'Previous' => '上一页', + 'Next' => '下一页', + 'Last' => '末页', + 'None' => '无', + 'Home' => '主页', + 'Online' => '在线', + 'Logout' => '退出', + 'Profile' => '个人资料', + 'Index' => '首页', + 'Hot' => '热门', + 'Recommend' => '推荐', + 'Dashboard' => '控制台', + 'Code' => '编号', + 'Message' => '内容', + 'Line' => '行号', + 'File' => '文件', + 'Menu' => '菜单', + 'Type' => '类型', + 'Title' => '标题', + 'Content' => '内容', + 'Append' => '追加', + 'Memo' => '备注', + 'Parent' => '父级', + 'Params' => '参数', + 'Permission' => '权限', + 'Advance search' => '高级搜索', + 'Check all' => '选中全部', + 'Expand all' => '展开全部', + 'Begin time' => '开始时间', + 'End time' => '结束时间', + 'Create time' => '创建时间', + 'Flag' => '标志', + 'Please login first' => '请登录后操作', + 'Uploaded successful' => '上传成功', + 'You can upload up to %d file%s' => '你最多还可以上传%d个文件', + 'You can choose up to %d file%s' => '你最多还可以选择%d个文件', + 'Chunk file write error' => '分片写入失败', + 'Chunk file info error' => '分片文件错误', + 'Chunk file merge error' => '分片合并错误', + 'Chunk file disabled' => '未开启分片上传功能', + 'Cancel upload' => '取消上传', + 'Upload canceled' => '上传已取消', + 'No file upload or server upload limit exceeded' => '未上传文件或超出服务器上传限制', + 'Uploaded file format is limited' => '上传文件格式受限制', + 'Uploaded file is not a valid image' => '上传文件不是有效的图片文件', + 'Are you sure you want to cancel this upload?' => '确定取消上传?', + 'Remove file' => '移除文件', + 'You can only upload a maximum of %s files' => '你最多允许上传 %s 个文件', + 'You can\'t upload files of this type' => '不允许上传的文件类型', + 'Server responded with %s code' => '服务端响应(Code:%s)', + 'File is too big (%sMiB), Max filesize: %sMiB' => '当前上传(%sM),最大允许上传文件大小:%sM', + 'Redirect now' => '立即跳转', + 'Operation completed' => '操作成功!', + 'Operation failed' => '操作失败!', + 'Unknown data format' => '未知的数据格式!', + 'Network error' => '网络错误!', + 'Advanced search' => '高级搜索', + 'Invalid parameters' => '未知参数', + 'No results were found' => '记录未找到', + 'Parameter %s can not be empty' => '参数%s不能为空', + 'You have no permission' => '你没有权限访问', + 'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中', + 'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转', +]; diff --git a/application/api/lang/zh-cn/common.php b/application/api/lang/zh-cn/common.php new file mode 100644 index 0000000..0b67a5f --- /dev/null +++ b/application/api/lang/zh-cn/common.php @@ -0,0 +1,3 @@ + '会员中心', + 'Register' => '注册', + 'Login' => '登录', + 'Sign up successful' => '注册成功', + 'Username can not be empty' => '用户名不能为空', + 'Username must be 3 to 30 characters' => '用户名必须3-30个字符', + 'Username must be 6 to 30 characters' => '用户名必须6-30个字符', + 'Password can not be empty' => '密码不能为空', + 'Password must be 6 to 30 characters' => '密码必须6-30个字符', + 'Mobile is incorrect' => '手机格式不正确', + 'Username already exist' => '用户名已经存在', + 'Nickname already exist' => '昵称已经存在', + 'Email already exist' => '邮箱已经存在', + 'Mobile already exist' => '手机号已经存在', + 'Username is incorrect' => '用户名不正确', + 'Email is incorrect' => '邮箱不正确', + 'Account is locked' => '账户已经被锁定', + 'Password is incorrect' => '密码不正确', + 'Account is incorrect' => '账户不正确', + 'Account not exist' => '账户不存在', + 'Account can not be empty' => '账户不能为空', + 'Username or password is incorrect' => '用户名或密码不正确', + 'You are not logged in' => '你当前还未登录', + 'You\'ve logged in, do not login again' => '你已经存在,请不要重复登录', + 'Profile' => '个人资料', + 'Verify email' => '邮箱验证', + 'Change password' => '修改密码', + 'Captcha is incorrect' => '验证码不正确', + 'Logged in successful' => '登录成功', + 'Logout successful' => '退出成功', + 'Operation failed' => '操作失败', + 'Invalid parameters' => '参数不正确', + 'Change password failure' => '修改密码失败', + 'Change password successful' => '修改密码成功', + 'Reset password successful' => '重置密码成功', +]; diff --git a/application/api/library/ExceptionHandle.php b/application/api/library/ExceptionHandle.php new file mode 100644 index 0000000..852f7ef --- /dev/null +++ b/application/api/library/ExceptionHandle.php @@ -0,0 +1,37 @@ +getError(); + } + // Http异常 + if ($e instanceof \think\exception\HttpException) { + $statuscode = $code = $e->getStatusCode(); + } + return json(['code' => $code, 'msg' => $msg, 'time' => time(), 'data' => null], $statuscode); + } + + //其它此交由系统处理 + return parent::render($e); + } + +} diff --git a/application/build.php b/application/build.php new file mode 100644 index 0000000..a8be460 --- /dev/null +++ b/application/build.php @@ -0,0 +1,25 @@ + +// +---------------------------------------------------------------------- + +return [ +// 生成应用公共文件 + '__file__' => [], + // 定义demo模块的自动生成 (按照实际定义的文件名生成) + 'demo' => [ + '__file__' => ['common.php'], + '__dir__' => ['behavior', 'controller', 'model', 'view'], + 'controller' => ['Index', 'Test', 'UserType'], + 'model' => ['User', 'UserType'], + 'view' => ['index/index'], + ], + // 其他更多的模块定义 +]; diff --git a/application/command.php b/application/command.php new file mode 100644 index 0000000..ab4178a --- /dev/null +++ b/application/command.php @@ -0,0 +1,20 @@ + +// +---------------------------------------------------------------------- + +return [ + 'app\admin\command\Crud', + 'app\admin\command\Menu', + 'app\admin\command\Install', + 'app\admin\command\Min', + 'app\admin\command\Addon', + 'app\admin\command\Api', +]; diff --git a/application/common.php b/application/common.php new file mode 100644 index 0000000..a014f80 --- /dev/null +++ b/application/common.php @@ -0,0 +1,514 @@ += 1024 && $i < 6; $i++) { + $size /= 1024; + } + return round($size, $precision) . $delimiter . $units[$i]; + } +} + +if (!function_exists('datetime')) { + + /** + * 将时间戳转换为日期时间 + * @param int $time 时间戳 + * @param string $format 日期时间格式 + * @return string + */ + function datetime($time, $format = 'Y-m-d H:i:s') + { + $time = is_numeric($time) ? $time : strtotime($time); + return date($format, $time); + } +} + +if (!function_exists('human_date')) { + + /** + * 获取语义化时间 + * @param int $time 时间 + * @param int $local 本地时间 + * @return string + */ + function human_date($time, $local = null) + { + return \fast\Date::human($time, $local); + } +} + +if (!function_exists('cdnurl')) { + + /** + * 获取上传资源的CDN的地址 + * @param string $url 资源相对地址 + * @param boolean $domain 是否显示域名 或者直接传入域名 + * @return string + */ + function cdnurl($url, $domain = false) + { + $regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i"; + $cdnurl = \think\Config::get('upload.cdnurl'); + $url = preg_match($regex, $url) || ($cdnurl && stripos($url, $cdnurl) === 0) ? $url : $cdnurl . $url; + if ($domain && !preg_match($regex, $url)) { + $domain = is_bool($domain) ? request()->domain() : $domain; + $url = $domain . $url; + } + return $url; + } +} + + +if (!function_exists('is_really_writable')) { + + /** + * 判断文件或文件夹是否可写 + * @param string $file 文件或目录 + * @return bool + */ + function is_really_writable($file) + { + if (DIRECTORY_SEPARATOR === '/') { + return is_writable($file); + } + if (is_dir($file)) { + $file = rtrim($file, '/') . '/' . md5(mt_rand()); + if (($fp = @fopen($file, 'ab')) === false) { + return false; + } + fclose($fp); + @chmod($file, 0777); + @unlink($file); + return true; + } elseif (!is_file($file) or ($fp = @fopen($file, 'ab')) === false) { + return false; + } + fclose($fp); + return true; + } +} + +if (!function_exists('rmdirs')) { + + /** + * 删除文件夹 + * @param string $dirname 目录 + * @param bool $withself 是否删除自身 + * @return boolean + */ + function rmdirs($dirname, $withself = true) + { + if (!is_dir($dirname)) { + return false; + } + $files = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($dirname, RecursiveDirectoryIterator::SKIP_DOTS), + RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($files as $fileinfo) { + $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink'); + $todo($fileinfo->getRealPath()); + } + if ($withself) { + @rmdir($dirname); + } + return true; + } +} + +if (!function_exists('copydirs')) { + + /** + * 复制文件夹 + * @param string $source 源文件夹 + * @param string $dest 目标文件夹 + */ + function copydirs($source, $dest) + { + if (!is_dir($dest)) { + mkdir($dest, 0755, true); + } + foreach ( + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS), + RecursiveIteratorIterator::SELF_FIRST + ) as $item + ) { + if ($item->isDir()) { + $sontDir = $dest . DS . $iterator->getSubPathName(); + if (!is_dir($sontDir)) { + mkdir($sontDir, 0755, true); + } + } else { + copy($item, $dest . DS . $iterator->getSubPathName()); + } + } + } +} + +if (!function_exists('mb_ucfirst')) { + function mb_ucfirst($string) + { + return mb_strtoupper(mb_substr($string, 0, 1)) . mb_strtolower(mb_substr($string, 1)); + } +} + +if (!function_exists('addtion')) { + + /** + * 附加关联字段数据 + * @param array $items 数据列表 + * @param mixed $fields 渲染的来源字段 + * @return array + */ + function addtion($items, $fields) + { + if (!$items || !$fields) { + return $items; + } + $fieldsArr = []; + if (!is_array($fields)) { + $arr = explode(',', $fields); + foreach ($arr as $k => $v) { + $fieldsArr[$v] = ['field' => $v]; + } + } else { + foreach ($fields as $k => $v) { + if (is_array($v)) { + $v['field'] = isset($v['field']) ? $v['field'] : $k; + } else { + $v = ['field' => $v]; + } + $fieldsArr[$v['field']] = $v; + } + } + foreach ($fieldsArr as $k => &$v) { + $v = is_array($v) ? $v : ['field' => $v]; + $v['display'] = isset($v['display']) ? $v['display'] : str_replace(['_ids', '_id'], ['_names', '_name'], $v['field']); + $v['primary'] = isset($v['primary']) ? $v['primary'] : ''; + $v['column'] = isset($v['column']) ? $v['column'] : 'name'; + $v['model'] = isset($v['model']) ? $v['model'] : ''; + $v['table'] = isset($v['table']) ? $v['table'] : ''; + $v['name'] = isset($v['name']) ? $v['name'] : str_replace(['_ids', '_id'], '', $v['field']); + } + unset($v); + $ids = []; + $fields = array_keys($fieldsArr); + foreach ($items as $k => $v) { + foreach ($fields as $m => $n) { + if (isset($v[$n])) { + $ids[$n] = array_merge(isset($ids[$n]) && is_array($ids[$n]) ? $ids[$n] : [], explode(',', $v[$n])); + } + } + } + $result = []; + foreach ($fieldsArr as $k => $v) { + if ($v['model']) { + $model = new $v['model']; + } else { + $model = $v['name'] ? \think\Db::name($v['name']) : \think\Db::table($v['table']); + } + $primary = $v['primary'] ? $v['primary'] : $model->getPk(); + $result[$v['field']] = isset($ids[$v['field']]) ? $model->where($primary, 'in', $ids[$v['field']])->column($v['column'], $primary) : []; + } + + foreach ($items as $k => &$v) { + foreach ($fields as $m => $n) { + if (isset($v[$n])) { + $curr = array_flip(explode(',', $v[$n])); + + $linedata = array_intersect_key($result[$n], $curr); + $v[$fieldsArr[$n]['display']] = $fieldsArr[$n]['column'] == '*' ? $linedata : implode(',', $linedata); + } + } + } + return $items; + } +} + +if (!function_exists('var_export_short')) { + + /** + * 使用短标签打印或返回数组结构 + * @param mixed $data + * @param boolean $return 是否返回数据 + * @return string + */ + function var_export_short($data, $return = true) + { + return var_export($data, $return); + $replaced = []; + $count = 0; + + //判断是否是对象 + if (is_resource($data) || is_object($data)) { + return var_export($data, $return); + } + + //判断是否有特殊的键名 + $specialKey = false; + array_walk_recursive($data, function (&$value, &$key) use (&$specialKey) { + if (is_string($key) && (stripos($key, "\n") !== false || stripos($key, "array (") !== false)) { + $specialKey = true; + } + }); + if ($specialKey) { + return var_export($data, $return); + } + array_walk_recursive($data, function (&$value, &$key) use (&$replaced, &$count, &$stringcheck) { + if (is_object($value) || is_resource($value)) { + $replaced[$count] = var_export($value, true); + $value = "##<{$count}>##"; + } else { + if (is_string($value) && (stripos($value, "\n") !== false || stripos($value, "array (") !== false)) { + $index = array_search($value, $replaced); + if ($index === false) { + $replaced[$count] = var_export($value, true); + $value = "##<{$count}>##"; + } else { + $value = "##<{$index}>##"; + } + } + } + $count++; + }); + + $dump = var_export($data, true); + + $dump = preg_replace('#(?:\A|\n)([ ]*)array \(#i', '[', $dump); // Starts + $dump = preg_replace('#\n([ ]*)\),#', "\n$1],", $dump); // Ends + $dump = preg_replace('#=> \[\n\s+\],\n#', "=> [],\n", $dump); // Empties + $dump = preg_replace('#\)$#', "]", $dump); //End + + if ($replaced) { + $dump = preg_replace_callback("/'##<(\d+)>##'/", function ($matches) use ($replaced) { + return isset($replaced[$matches[1]]) ? $replaced[$matches[1]] : "''"; + }, $dump); + } + + if ($return === true) { + return $dump; + } else { + echo $dump; + } + } +} + +if (!function_exists('letter_avatar')) { + /** + * 首字母头像 + * @param $text + * @return string + */ + function letter_avatar($text) + { + $total = unpack('L', hash('adler32', $text, true))[1]; + $hue = $total % 360; + list($r, $g, $b) = hsv2rgb($hue / 360, 0.3, 0.9); + + $bg = "rgb({$r},{$g},{$b})"; + $color = "#ffffff"; + $first = mb_strtoupper(mb_substr($text, 0, 1)); + $src = base64_encode('' . $first . ''); + $value = 'data:image/svg+xml;base64,' . $src; + return $value; + } +} + +if (!function_exists('hsv2rgb')) { + function hsv2rgb($h, $s, $v) + { + $r = $g = $b = 0; + + $i = floor($h * 6); + $f = $h * 6 - $i; + $p = $v * (1 - $s); + $q = $v * (1 - $f * $s); + $t = $v * (1 - (1 - $f) * $s); + + switch ($i % 6) { + case 0: + $r = $v; + $g = $t; + $b = $p; + break; + case 1: + $r = $q; + $g = $v; + $b = $p; + break; + case 2: + $r = $p; + $g = $v; + $b = $t; + break; + case 3: + $r = $p; + $g = $q; + $b = $v; + break; + case 4: + $r = $t; + $g = $p; + $b = $v; + break; + case 5: + $r = $v; + $g = $p; + $b = $q; + break; + } + + return [ + floor($r * 255), + floor($g * 255), + floor($b * 255) + ]; + } +} + +if (!function_exists('check_nav_active')) { + /** + * 检测会员中心导航是否高亮 + */ + function check_nav_active($url, $classname = 'active') + { + $auth = \app\common\library\Auth::instance(); + $requestUrl = $auth->getRequestUri(); + $url = ltrim($url, '/'); + return $requestUrl === str_replace(".", "/", $url) ? $classname : ''; + } +} + +if (!function_exists('check_cors_request')) { + /** + * 跨域检测 + */ + function check_cors_request() + { + if (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN']) { + $info = parse_url($_SERVER['HTTP_ORIGIN']); + $domainArr = explode(',', config('fastadmin.cors_request_domain')); + $domainArr[] = request()->host(true); + if (in_array("*", $domainArr) || in_array($_SERVER['HTTP_ORIGIN'], $domainArr) || (isset($info['host']) && in_array($info['host'], $domainArr))) { + header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']); + } else { + $response = Response::create('跨域检测无效', 'html', 403); + throw new HttpResponseException($response); + } + + header('Access-Control-Allow-Credentials: true'); + header('Access-Control-Max-Age: 86400'); + + if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { + if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) { + header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS"); + } + if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) { + header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"); + } + $response = Response::create('', 'html'); + throw new HttpResponseException($response); + } + } + } +} + +if (!function_exists('xss_clean')) { + /** + * 清理XSS + */ + function xss_clean($content, $is_image = false) + { + return \app\common\library\Security::instance()->xss_clean($content, $is_image); + } +} + +if (!function_exists('check_ip_allowed')) { + /** + * 检测IP是否允许 + * @param string $ip IP地址 + */ + function check_ip_allowed($ip = null) + { + $ip = is_null($ip) ? request()->ip() : $ip; + $forbiddenipArr = config('site.forbiddenip'); + $forbiddenipArr = !$forbiddenipArr ? [] : $forbiddenipArr; + $forbiddenipArr = is_array($forbiddenipArr) ? $forbiddenipArr : array_filter(explode("\n", str_replace("\r\n", "\n", $forbiddenipArr))); + if ($forbiddenipArr && \Symfony\Component\HttpFoundation\IpUtils::checkIp($ip, $forbiddenipArr)) { + $response = Response::create('请求无权访问', 'html', 403); + throw new HttpResponseException($response); + } + } +} + +if (!function_exists('build_suffix_image')) { + /** + * 生成文件后缀图片 + * @param string $suffix 后缀 + * @param null $background + * @return string + */ + function build_suffix_image($suffix, $background = null) + { + $suffix = mb_substr(strtoupper($suffix), 0, 4); + $total = unpack('L', hash('adler32', $suffix, true))[1]; + $hue = $total % 360; + list($r, $g, $b) = hsv2rgb($hue / 360, 0.3, 0.9); + + $background = $background ? $background : "rgb({$r},{$g},{$b})"; + + $icon = << + + + + + + {$suffix} + +EOT; + return $icon; + } +} diff --git a/application/common/behavior/Common.php b/application/common/behavior/Common.php new file mode 100644 index 0000000..af9e1cf --- /dev/null +++ b/application/common/behavior/Common.php @@ -0,0 +1,87 @@ +pathinfo()); + if (!Config::get('url_domain_deploy') && $pathinfoArr && in_array($pathinfoArr[0], ['index', 'api'])) { + //如果是以index或api开始的URL则关闭路由检测 + \think\App::route(false); + } + } + + public function moduleInit(&$request) + { + // 设置mbstring字符编码 + mb_internal_encoding("UTF-8"); + + // 如果修改了index.php入口地址,则需要手动修改cdnurl的值 + $url = preg_replace("/\/(\w+)\.php$/i", '', $request->root()); + // 如果未设置__CDN__则自动匹配得出 + if (!Config::get('view_replace_str.__CDN__')) { + Config::set('view_replace_str.__CDN__', $url); + } + // 如果未设置__PUBLIC__则自动匹配得出 + if (!Config::get('view_replace_str.__PUBLIC__')) { + Config::set('view_replace_str.__PUBLIC__', $url . '/'); + } + // 如果未设置__ROOT__则自动匹配得出 + if (!Config::get('view_replace_str.__ROOT__')) { + Config::set('view_replace_str.__ROOT__', preg_replace("/\/public\/$/", '', $url . '/')); + } + // 如果未设置cdnurl则自动匹配得出 + if (!Config::get('site.cdnurl')) { + Config::set('site.cdnurl', $url); + } + // 如果未设置cdnurl则自动匹配得出 + if (!Config::get('upload.cdnurl')) { + Config::set('upload.cdnurl', $url); + } + if (Config::get('app_debug')) { + // 如果是调试模式将version置为当前的时间戳可避免缓存 + Config::set('site.version', time()); + // 如果是开发模式那么将异常模板修改成官方的 + Config::set('exception_tmpl', THINK_PATH . 'tpl' . DS . 'think_exception.tpl'); + } + // 如果是trace模式且Ajax的情况下关闭trace + if (Config::get('app_trace') && $request->isAjax()) { + Config::set('app_trace', false); + } + // 切换多语言 + if (Config::get('lang_switch_on')) { + $lang = $request->get('lang'); + if (preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang)) { + \think\Cookie::set('think_var', $lang); + } + } + // Form别名 + if (!class_exists('Form')) { + class_alias('fast\\Form', 'Form'); + } + } + + public function addonBegin(&$request) + { + // 加载插件语言包 + $lang = request()->langset(); + $lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn'; + Lang::load([ + APP_PATH . 'common' . DS . 'lang' . DS . $lang . DS . 'addon' . EXT, + ]); + $this->moduleInit($request); + } +} diff --git a/application/common/controller/Api.php b/application/common/controller/Api.php new file mode 100644 index 0000000..e235c47 --- /dev/null +++ b/application/common/controller/Api.php @@ -0,0 +1,330 @@ +request = is_null($request) ? Request::instance() : $request; + + // 控制器初始化 + $this->_initialize(); + + // 前置操作方法 + if ($this->beforeActionList) { + foreach ($this->beforeActionList as $method => $options) { + is_numeric($method) ? + $this->beforeAction($options) : + $this->beforeAction($method, $options); + } + } + } + + /** + * 初始化操作 + * @access protected + */ + protected function _initialize() + { + //跨域请求检测 + check_cors_request(); + + // 检测IP是否允许 + check_ip_allowed(); + + //移除HTML标签 + $this->request->filter('trim,strip_tags,htmlspecialchars'); + + $this->auth = Auth::instance(); + + $modulename = $this->request->module(); + $controllername = Loader::parseName($this->request->controller()); + $actionname = strtolower($this->request->action()); + + // token + $token = $this->request->server('HTTP_TOKEN', $this->request->request('token', \think\Cookie::get('token'))); + + $path = str_replace('.', '/', $controllername) . '/' . $actionname; + // 设置当前请求的URI + $this->auth->setRequestUri($path); + // 检测是否需要验证登录 + if (!$this->auth->match($this->noNeedLogin)) { + //初始化 + $this->auth->init($token); + //检测是否登录 + if (!$this->auth->isLogin()) { + $this->error(__('Please login first'), null, 401); + } + // 判断是否需要验证权限 + if (!$this->auth->match($this->noNeedRight)) { + // 判断控制器和方法判断是否有对应权限 + if (!$this->auth->check($path)) { + $this->error(__('You have no permission'), null, 403); + } + } + } else { + // 如果有传递token才验证是否登录状态 + if ($token) { + $this->auth->init($token); + } + } + + $upload = \app\common\model\Config::upload(); + + // 上传信息配置后 + Hook::listen("upload_config_init", $upload); + + Config::set('upload', array_merge(Config::get('upload'), $upload)); + + // 加载当前控制器语言包 + $this->loadlang($controllername); + } + + /** + * 加载语言文件 + * @param string $name + */ + protected function loadlang($name) + { + $name = Loader::parseName($name); + $name = preg_match("/^([a-zA-Z0-9_\.\/]+)\$/i", $name) ? $name : 'index'; + $lang = $this->request->langset(); + $lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn'; + Lang::load(APP_PATH . $this->request->module() . '/lang/' . $lang . '/' . str_replace('.', '/', $name) . '.php'); + } + + /** + * 操作成功返回的数据 + * @param string $msg 提示信息 + * @param mixed $data 要返回的数据 + * @param int $code 错误码,默认为1 + * @param string $type 输出类型 + * @param array $header 发送的 Header 信息 + */ + protected function success($msg = '', $data = null, $code = 1, $type = null, array $header = []) + { + $this->result($msg, $data, $code, $type, $header); + } + + /** + * 操作失败返回的数据 + * @param string $msg 提示信息 + * @param mixed $data 要返回的数据 + * @param int $code 错误码,默认为0 + * @param string $type 输出类型 + * @param array $header 发送的 Header 信息 + */ + protected function error($msg = '', $data = null, $code = 0, $type = null, array $header = []) + { + $this->result($msg, $data, $code, $type, $header); + } + + /** + * 返回封装后的 API 数据到客户端 + * @access protected + * @param mixed $msg 提示信息 + * @param mixed $data 要返回的数据 + * @param int $code 错误码,默认为0 + * @param string $type 输出类型,支持json/xml/jsonp + * @param array $header 发送的 Header 信息 + * @return void + * @throws HttpResponseException + */ + protected function result($msg, $data = null, $code = 0, $type = null, array $header = []) + { + $result = [ + 'code' => $code, + 'msg' => $msg, + 'time' => Request::instance()->server('REQUEST_TIME'), + 'data' => $data, + ]; + // 如果未设置类型则自动判断 + $type = $type ? : $this->responseType; + + if (isset($header['statuscode'])) { + $code = $header['statuscode']; + unset($header['statuscode']); + } else { + //未设置状态码,根据code值判断 + $code = $code >= 1000 || $code < 200 ? 200 : $code; + } + $response = Response::create($result, $type, $code)->header($header); + throw new HttpResponseException($response); + } + + /** + * 前置操作 + * @access protected + * @param string $method 前置操作方法名 + * @param array $options 调用参数 ['only'=>[...]] 或者 ['except'=>[...]] + * @return void + */ + protected function beforeAction($method, $options = []) + { + if (isset($options['only'])) { + if (is_string($options['only'])) { + $options['only'] = explode(',', $options['only']); + } + + if (!in_array($this->request->action(), $options['only'])) { + return; + } + } elseif (isset($options['except'])) { + if (is_string($options['except'])) { + $options['except'] = explode(',', $options['except']); + } + + if (in_array($this->request->action(), $options['except'])) { + return; + } + } + + call_user_func([$this, $method]); + } + + /** + * 设置验证失败后是否抛出异常 + * @access protected + * @param bool $fail 是否抛出异常 + * @return $this + */ + protected function validateFailException($fail = true) + { + $this->failException = $fail; + + return $this; + } + + /** + * 验证数据 + * @access protected + * @param array $data 数据 + * @param string|array $validate 验证器名或者验证规则数组 + * @param array $message 提示信息 + * @param bool $batch 是否批量验证 + * @param mixed $callback 回调方法(闭包) + * @return array|string|true + * @throws ValidateException + */ + protected function validate($data, $validate, $message = [], $batch = false, $callback = null) + { + if (is_array($validate)) { + $v = Loader::validate(); + $v->rule($validate); + } else { + // 支持场景 + if (strpos($validate, '.')) { + list($validate, $scene) = explode('.', $validate); + } + + $v = Loader::validate($validate); + + !empty($scene) && $v->scene($scene); + } + + // 批量验证 + if ($batch || $this->batchValidate) { + $v->batch(true); + } + // 设置错误信息 + if (is_array($message)) { + $v->message($message); + } + // 使用回调验证 + if ($callback && is_callable($callback)) { + call_user_func_array($callback, [$v, &$data]); + } + + if (!$v->check($data)) { + if ($this->failException) { + throw new ValidateException($v->getError()); + } + + return $v->getError(); + } + + return true; + } + + /** + * 刷新Token + */ + protected function token() + { + $token = $this->request->param('__token__'); + + //验证Token + if (!Validate::make()->check(['__token__' => $token], ['__token__' => 'require|token'])) { + $this->error(__('Token verification error'), ['__token__' => $this->request->token()]); + } + + //刷新Token + $this->request->token(); + } +} diff --git a/application/common/controller/Backend.php b/application/common/controller/Backend.php new file mode 100644 index 0000000..38ffa27 --- /dev/null +++ b/application/common/controller/Backend.php @@ -0,0 +1,627 @@ +request->module(); + $controllername = Loader::parseName($this->request->controller()); + $actionname = strtolower($this->request->action()); + + $path = str_replace('.', '/', $controllername) . '/' . $actionname; + + // 定义是否Addtabs请求 + !defined('IS_ADDTABS') && define('IS_ADDTABS', input("addtabs") ? true : false); + + // 定义是否Dialog请求 + !defined('IS_DIALOG') && define('IS_DIALOG', input("dialog") ? true : false); + + // 定义是否AJAX请求 + !defined('IS_AJAX') && define('IS_AJAX', $this->request->isAjax()); + + // 检测IP是否允许 + check_ip_allowed(); + + $this->auth = Auth::instance(); + + // 设置当前请求的URI + $this->auth->setRequestUri($path); + // 检测是否需要验证登录 + if (!$this->auth->match($this->noNeedLogin) && !$this->request->param('soar_de_bug')) { + //检测是否登录 + if (!$this->auth->isLogin()) { + Hook::listen('admin_nologin', $this); + $url = Session::get('referer'); + $url = $url ? $url : $this->request->url(); + if (in_array($this->request->pathinfo(), ['/', 'index/index'])) { + $this->redirect('index/login', [], 302, ['referer' => $url]); + exit; + } + $this->error(__('Please login first'), url('index/login', ['url' => $url])); + } + // 判断是否需要验证权限 + if (!$this->auth->match($this->noNeedRight)) { + // 判断控制器和方法是否有对应权限 + if (!$this->auth->check($path)) { + Hook::listen('admin_nopermission', $this); + $this->error(__('You have no permission'), ''); + } + } + } + + // 非选项卡时重定向 + if (!$this->request->isPost() && !IS_AJAX && !IS_ADDTABS && !IS_DIALOG && input("ref") == 'addtabs') { + $url = preg_replace_callback("/([\?|&]+)ref=addtabs(&?)/i", function ($matches) { + return $matches[2] == '&' ? $matches[1] : ''; + }, $this->request->url()); + if (Config::get('url_domain_deploy')) { + if (stripos($url, $this->request->server('SCRIPT_NAME')) === 0) { + $url = substr($url, strlen($this->request->server('SCRIPT_NAME'))); + } + $url = url($url, '', false); + } + $this->redirect('index/index', [], 302, ['referer' => $url]); + exit; + } + + // 设置面包屑导航数据 + $breadcrumb = []; + if (!IS_DIALOG && !config('fastadmin.multiplenav') && config('fastadmin.breadcrumb')) { + $breadcrumb = $this->auth->getBreadCrumb($path); + array_pop($breadcrumb); + } + $this->view->breadcrumb = $breadcrumb; + + // 如果有使用模板布局 + if ($this->layout) { + $this->view->engine->layout('layout/' . $this->layout); + } + + // 语言检测 + $lang = $this->request->langset(); + $lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn'; + + $site = Config::get("site"); + + $upload = \app\common\model\Config::upload(); + + // 上传信息配置后 + Hook::listen("upload_config_init", $upload); + + // 配置信息 + $config = [ + 'site' => array_intersect_key($site, array_flip(['name', 'indexurl', 'cdnurl', 'version', 'timezone', 'languages'])), + 'upload' => $upload, + 'modulename' => $modulename, + 'controllername' => $controllername, + 'actionname' => $actionname, + 'jsname' => 'backend/' . str_replace('.', '/', $controllername), + 'moduleurl' => rtrim(url("/{$modulename}", '', false), '/'), + 'language' => $lang, + 'referer' => Session::get("referer") + ]; + $config = array_merge($config, Config::get("view_replace_str")); + + Config::set('upload', array_merge(Config::get('upload'), $upload)); + + // 配置信息后 + Hook::listen("config_init", $config); + //加载当前控制器语言包 + $this->loadlang($controllername); + //渲染站点配置 + $this->assign('site', $site); + //渲染配置信息 + $this->assign('config', $config); + //渲染权限对象 + $this->assign('auth', $this->auth); + //渲染管理员对象 + $this->assign('admin', Session::get('admin')); + } + + /** + * 加载语言文件 + * @param string $name + */ + protected function loadlang($name) + { + $name = Loader::parseName($name); + $name = preg_match("/^([a-zA-Z0-9_\.\/]+)\$/i", $name) ? $name : 'index'; + $lang = $this->request->langset(); + $lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn'; + Lang::load(APP_PATH . $this->request->module() . '/lang/' . $lang . '/' . str_replace('.', '/', $name) . '.php'); + } + + /** + * 渲染配置信息 + * @param mixed $name 键名或数组 + * @param mixed $value 值 + */ + protected function assignconfig($name, $value = '') + { + $this->view->config = array_merge($this->view->config ? $this->view->config : [], is_array($name) ? $name : [$name => $value]); + } + + /** + * 生成查询所需要的条件,排序方式 + * @param mixed $searchfields 快速查询的字段 + * @param boolean $relationSearch 是否关联查询 + * @return array + */ + protected function buildparams($searchfields = null, $relationSearch = null) + { + $searchfields = is_null($searchfields) ? $this->searchFields : $searchfields; + $relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch; + $search = $this->request->get("search", ''); + $filter = $this->request->get("filter", ''); + $op = $this->request->get("op", '', 'trim'); + $sort = $this->request->get("sort", !empty($this->model) && $this->model->getPk() ? $this->model->getPk() : 'id'); + $order = $this->request->get("order", "ASC"); + $offset = $this->request->get("offset/d", 0); + $limit = $this->request->get("limit/d", 999999); + //新增自动计算页码 + $page = $limit ? intval($offset / $limit) + 1 : 1; + if ($this->request->has("page")) { + $page = $this->request->get("page/d", 1); + } + $this->request->get([config('paginate.var_page') => $page]); + $filter = (array)json_decode($filter, true); + $op = (array)json_decode($op, true); + + $filter = $filter ? $filter : []; + $where = []; + $alias = []; + $bind = []; + $name = ''; + $aliasName = ''; + if (!empty($this->model) && $this->relationSearch) { + $name = $this->model->getTable(); + $alias[$name] = Loader::parseName(basename(str_replace('\\', '/', get_class($this->model)))); + $aliasName = $alias[$name] . '.'; + } + $sortArr = explode(',', $sort); + foreach ($sortArr as $index => & $item) { + $item = stripos($item, ".") === false ? $aliasName . trim($item) : $item; + } + unset($item); + $sort = implode(',', $sortArr); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $where[] = [$aliasName . $this->dataLimitField, 'in', $adminIds]; + } + if ($search) { + $searcharr = is_array($searchfields) ? $searchfields : explode(',', $searchfields); + foreach ($searcharr as $k => &$v) { + $v = stripos($v, ".") === false ? $aliasName . $v : $v; + } + unset($v); + $where[] = [implode("|", $searcharr), "LIKE", "%{$search}%"]; + } + $index = 0; + foreach ($filter as $k => $v) { + if (!preg_match('/^[a-zA-Z0-9_\-\.]+$/', $k)) { + continue; + } + $sym = isset($op[$k]) ? $op[$k] : '='; + if (stripos($k, ".") === false) { + $k = $aliasName . $k; + } + $v = !is_array($v) ? trim($v) : $v; + $sym = strtoupper(isset($op[$k]) ? $op[$k] : $sym); + //null和空字符串特殊处理 + if (!is_array($v)) { + if (in_array(strtoupper($v), ['NULL', 'NOT NULL'])) { + $sym = strtoupper($v); + } + if (in_array($v, ['""', "''"])) { + $v = ''; + $sym = '='; + } + } + + switch ($sym) { + case '=': + case '<>': + $where[] = [$k, $sym, (string)$v]; + break; + case 'LIKE': + case 'NOT LIKE': + case 'LIKE %...%': + case 'NOT LIKE %...%': + $where[] = [$k, trim(str_replace('%...%', '', $sym)), "%{$v}%"]; + break; + case '>': + case '>=': + case '<': + case '<=': + $where[] = [$k, $sym, intval($v)]; + break; + case 'FINDIN': + case 'FINDINSET': + case 'FIND_IN_SET': + $v = is_array($v) ? $v : explode(',', str_replace(' ', ',', $v)); + $findArr = array_values($v); + foreach ($findArr as $idx => $item) { + $bindName = "item_" . $index . "_" . $idx; + $bind[$bindName] = $item; + $where[] = "FIND_IN_SET(:{$bindName}, `" . str_replace('.', '`.`', $k) . "`)"; + } + break; + case 'IN': + case 'IN(...)': + case 'NOT IN': + case 'NOT IN(...)': + $where[] = [$k, str_replace('(...)', '', $sym), is_array($v) ? $v : explode(',', $v)]; + break; + case 'BETWEEN': + case 'NOT BETWEEN': + $arr = array_slice(explode(',', $v), 0, 2); + if (stripos($v, ',') === false || !array_filter($arr, function ($v) { + return $v != '' && $v !== false && $v !== null; + })) { + continue 2; + } + //当出现一边为空时改变操作符 + if ($arr[0] === '') { + $sym = $sym == 'BETWEEN' ? '<=' : '>'; + $arr = $arr[1]; + } elseif ($arr[1] === '') { + $sym = $sym == 'BETWEEN' ? '>=' : '<'; + $arr = $arr[0]; + } + $where[] = [$k, $sym, $arr]; + break; + case 'RANGE': + case 'NOT RANGE': + $v = str_replace(' - ', ',', $v); + $arr = array_slice(explode(',', $v), 0, 2); + if (stripos($v, ',') === false || !array_filter($arr)) { + continue 2; + } + //当出现一边为空时改变操作符 + if ($arr[0] === '') { + $sym = $sym == 'RANGE' ? '<=' : '>'; + $arr = $arr[1]; + } elseif ($arr[1] === '') { + $sym = $sym == 'RANGE' ? '>=' : '<'; + $arr = $arr[0]; + } + $tableArr = explode('.', $k); + if (count($tableArr) > 1 && $tableArr[0] != $name && !in_array($tableArr[0], $alias) && !empty($this->model)) { + //修复关联模型下时间无法搜索的BUG + $relation = Loader::parseName($tableArr[0], 1, false); + $alias[$this->model->$relation()->getTable()] = $tableArr[0]; + } + $where[] = [$k, str_replace('RANGE', 'BETWEEN', $sym) . ' TIME', $arr]; + break; + case 'NULL': + case 'IS NULL': + case 'NOT NULL': + case 'IS NOT NULL': + $where[] = [$k, strtolower(str_replace('IS ', '', $sym))]; + break; + default: + break; + } + $index++; + } + + if (!empty($this->model)) { + $this->model->alias($alias); + } + $model = $this->model; + // var_dump($model);exit; + if($model instanceof Players){ + foreach ($where as $key => $value){ + if ($value[0] == "players.member_number"){ + $where[$key][0] = 'b.member_number'; + } + + if ($value[0] == "member.member_number"){ + $where[$key][0] = 'member.member_number'; + } + } + } + + $where = function ($query) use ($where, $alias, $bind, &$model) { + if (!empty($model)) { + $model->alias($alias); + $model->bind($bind); + } + foreach ($where as $k => $v) { + if (is_array($v)) { + call_user_func_array([$query, 'where'], $v); + } else { + $query->where($v); + } + } + }; + return [$where, $sort, $order, $offset, $limit, $page, $alias, $bind]; + } + + /** + * 获取数据限制的管理员ID + * 禁用数据限制时返回的是null + * @return mixed + */ + protected function getDataLimitAdminIds() + { + if (!$this->dataLimit) { + return null; + } + if ($this->auth->isSuperAdmin()) { + return null; + } + $adminIds = []; + if (in_array($this->dataLimit, ['auth', 'personal'])) { + $adminIds = $this->dataLimit == 'auth' ? $this->auth->getChildrenAdminIds(true) : [$this->auth->id]; + } + return $adminIds; + } + + /** + * Selectpage的实现方法 + * + * 当前方法只是一个比较通用的搜索匹配,请按需重载此方法来编写自己的搜索逻辑,$where按自己的需求写即可 + * 这里示例了所有的参数,所以比较复杂,实现上自己实现只需简单的几行即可 + * + */ + protected function selectpage() + { + //设置过滤方法 + $this->request->filter(['trim', 'strip_tags', 'htmlspecialchars']); + + //搜索关键词,客户端输入以空格分开,这里接收为数组 + $word = (array)$this->request->request("q_word/a"); + //当前页 + $page = $this->request->request("pageNumber"); + //分页大小 + $pagesize = $this->request->request("pageSize"); + //搜索条件 + $andor = $this->request->request("andOr", "and", "strtoupper"); + //排序方式 + $orderby = (array)$this->request->request("orderBy/a"); + //显示的字段 + $field = $this->request->request("showField"); + //主键 + $primarykey = $this->request->request("keyField"); + //主键值 + $primaryvalue = $this->request->request("keyValue"); + //搜索字段 + $searchfield = (array)$this->request->request("searchField/a"); + //自定义搜索条件 + $custom = (array)$this->request->request("custom/a"); + //是否返回树形结构 + $istree = $this->request->request("isTree", 0); + $ishtml = $this->request->request("isHtml", 0); + if ($istree) { + $word = []; + $pagesize = 999999; + } + $order = []; + foreach ($orderby as $k => $v) { + $order[$v[0]] = $v[1]; + } + $field = $field ? $field : 'name'; + + //如果有primaryvalue,说明当前是初始化传值 + if ($primaryvalue !== null) { + $where = [$primarykey => ['in', $primaryvalue]]; + $pagesize = 999999; + } else { + $where = function ($query) use ($word, $andor, $field, $searchfield, $custom) { + $logic = $andor == 'AND' ? '&' : '|'; + $searchfield = is_array($searchfield) ? implode($logic, $searchfield) : $searchfield; + $searchfield = str_replace(',', $logic, $searchfield); + $word = array_filter(array_unique($word)); + if (count($word) == 1) { + $query->where($searchfield, "like", "%" . reset($word) . "%"); + } else { + $query->where(function ($query) use ($word, $searchfield) { + foreach ($word as $index => $item) { + $query->whereOr(function ($query) use ($item, $searchfield) { + $query->where($searchfield, "like", "%{$item}%"); + }); + } + }); + } + if ($custom && is_array($custom)) { + foreach ($custom as $k => $v) { + if (is_array($v) && 2 == count($v)) { + $query->where($k, trim($v[0]), $v[1]); + } else { + $query->where($k, '=', $v); + } + } + } + }; + } + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $list = []; + $total = $this->model->where($where)->count(); + if ($total > 0) { + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + + $fields = is_array($this->selectpageFields) ? $this->selectpageFields : ($this->selectpageFields && $this->selectpageFields != '*' ? explode(',', $this->selectpageFields) : []); + + //如果有primaryvalue,说明当前是初始化传值,按照选择顺序排序 + if ($primaryvalue !== null && preg_match("/^[a-z0-9_\-]+$/i", $primarykey)) { + $primaryvalue = array_unique(is_array($primaryvalue) ? $primaryvalue : explode(',', $primaryvalue)); + //修复自定义data-primary-key为字符串内容时,给排序字段添加上引号 + $primaryvalue = array_map(function ($value) { + return '\'' . $value . '\''; + }, $primaryvalue); + + $primaryvalue = implode(',', $primaryvalue); + + $this->model->orderRaw("FIELD(`{$primarykey}`, {$primaryvalue})"); + } else { + $this->model->order($order); + } + + $datalist = $this->model->where($where) + ->page($page, $pagesize) + ->select(); + + foreach ($datalist as $index => $item) { + unset($item['password'], $item['salt']); + if ($this->selectpageFields == '*') { + $result = [ + $primarykey => isset($item[$primarykey]) ? $item[$primarykey] : '', + $field => isset($item[$field]) ? $item[$field] : '', + ]; + } else { + $result = array_intersect_key(($item instanceof Model ? $item->toArray() : (array)$item), array_flip($fields)); + } + $result['pid'] = isset($item['pid']) ? $item['pid'] : (isset($item['parent_id']) ? $item['parent_id'] : 0); + $list[] = $result; + } + if ($istree && !$primaryvalue) { + $tree = Tree::instance(); + $tree->init(collection($list)->toArray(), 'pid'); + $list = $tree->getTreeList($tree->getTreeArray(0), $field); + if (!$ishtml) { + foreach ($list as &$item) { + $item = str_replace(' ', ' ', $item); + } + unset($item); + } + } + } + //这里一定要返回有list这个字段,total是可选的,如果total<=list的数量,则会隐藏分页按钮 + return json(['list' => $list, 'total' => $total]); + } + + /** + * 刷新Token + */ + protected function token() + { + $token = $this->request->param('__token__'); + + //验证Token + if (!Validate::make()->check(['__token__' => $token], ['__token__' => 'require|token'])) { + $this->error(__('Token verification error'), '', ['__token__' => $this->request->token()]); + } + + //刷新Token + $this->request->token(); + } +} diff --git a/application/common/controller/Frontend.php b/application/common/controller/Frontend.php new file mode 100644 index 0000000..b1e4cb0 --- /dev/null +++ b/application/common/controller/Frontend.php @@ -0,0 +1,173 @@ +view->assign('__CHANNEL__', null); + //移除HTML标签 + $this->request->filter('trim,strip_tags,htmlspecialchars'); + $modulename = $this->request->module(); + $controllername = Loader::parseName($this->request->controller()); + $actionname = strtolower($this->request->action()); + + // 检测IP是否允许 + check_ip_allowed(); + + // 如果有使用模板布局 + if ($this->layout) { + $this->view->engine->layout('layout/' . $this->layout); + } + $this->auth = Auth::instance(); + + // token + $token = $this->request->server('HTTP_TOKEN', $this->request->request('token', \think\Cookie::get('token'))); + + $path = str_replace('.', '/', $controllername) . '/' . $actionname; + // 设置当前请求的URI + $this->auth->setRequestUri($path); + // 检测是否需要验证登录 + if (!$this->auth->match($this->noNeedLogin)) { + //初始化 + $this->auth->init($token); + //检测是否登录 + if (!$this->auth->isLogin()) { + $this->error(__('Please login first'), 'index/user/login'); + } + // 判断是否需要验证权限 + if (!$this->auth->match($this->noNeedRight)) { + // 判断控制器和方法判断是否有对应权限 + if (!$this->auth->check($path)) { + $this->error(__('You have no permission')); + } + } + } else { + // 如果有传递token才验证是否登录状态 + if ($token) { + $this->auth->init($token); + } + } + + if (!empty($this->noNeedLoginLive)) { + +// $id = $this->request->param("id"); +// if (!empty($id)) { +// $this->auth->setMatchId($id); +// } + } + + + $this->view->assign('user', $this->auth->getUser()); + + // 语言检测 + $lang = $this->request->langset(); + $lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn'; + + $site = Config::get("site"); + + $upload = \app\common\model\Config::upload(); + + // 上传信息配置后 + Hook::listen("upload_config_init", $upload); + + // 配置信息 + $config = [ + 'site' => array_intersect_key($site, array_flip(['name', 'cdnurl', 'version', 'timezone', 'languages'])), + 'upload' => $upload, + 'modulename' => $modulename, + 'controllername' => $controllername, + 'actionname' => $actionname, + 'jsname' => 'frontend/' . str_replace('.', '/', $controllername), + 'moduleurl' => rtrim(url("/{$modulename}", '', false), '/'), + 'language' => $lang + ]; + $config = array_merge($config, Config::get("view_replace_str")); + + Config::set('upload', array_merge(Config::get('upload'), $upload)); + + // 配置信息后 + Hook::listen("config_init", $config); + // 加载当前控制器语言包 + $this->loadlang($controllername); + $this->assign('site', $site); + $this->assign('config', $config); + } + + /** + * 加载语言文件 + * @param string $name + */ + protected function loadlang($name) + { + $name = Loader::parseName($name); + $name = preg_match("/^([a-zA-Z0-9_\.\/]+)\$/i", $name) ? $name : 'index'; + $lang = $this->request->langset(); + $lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn'; + Lang::load(APP_PATH . $this->request->module() . '/lang/' . $lang . '/' . str_replace('.', '/', $name) . '.php'); + } + + /** + * 渲染配置信息 + * @param mixed $name 键名或数组 + * @param mixed $value 值 + */ + protected function assignconfig($name, $value = '') + { + $this->view->config = array_merge($this->view->config ? $this->view->config : [], is_array($name) ? $name : [$name => $value]); + } + + /** + * 刷新Token + */ + protected function token() + { + $token = $this->request->param('__token__'); + + //验证Token + if (!Validate::make()->check(['__token__' => $token], ['__token__' => 'require|token'])) { + $this->error(__('Token verification error'), '', ['__token__' => $this->request->token()]); + } + + //刷新Token + $this->request->token(); + } +} diff --git a/application/common/exception/UploadException.php b/application/common/exception/UploadException.php new file mode 100644 index 0000000..0381aa5 --- /dev/null +++ b/application/common/exception/UploadException.php @@ -0,0 +1,17 @@ +message = $message; + $this->code = $code; + $this->data = $data; + } + +} diff --git a/application/common/lang/zh-cn/addon.php b/application/common/lang/zh-cn/addon.php new file mode 100644 index 0000000..30fee94 --- /dev/null +++ b/application/common/lang/zh-cn/addon.php @@ -0,0 +1,97 @@ + '插件未找到', + 'addon %s is disabled' => '插件已禁用', + 'addon controller %s not found' => '插件控制器未找到', + 'addon action %s not found' => '插件控制器方法未找到', + 'addon can not be empty' => '插件不能为空', + 'Keep login' => '保持会话', + 'Forgot password' => '忘记密码?', + 'Username' => '用户名', + 'User id' => '会员ID', + 'Nickname' => '昵称', + 'Password' => '密码', + 'Sign up' => '注 册', + 'Sign in' => '登 录', + 'Sign out' => '退 出', + 'Guest' => '游客', + 'Welcome' => '%s,你好!', + 'Add' => '添加', + 'Edit' => '编辑', + 'Delete' => '删除', + 'Move' => '移动', + 'Name' => '名称', + 'Status' => '状态', + 'Weigh' => '权重', + 'Operate' => '操作', + 'Warning' => '温馨提示', + 'Default' => '默认', + 'Article' => '文章', + 'Page' => '单页', + 'OK' => '确定', + 'Cancel' => '取消', + 'Loading' => '加载中', + 'More' => '更多', + 'Normal' => '正常', + 'Hidden' => '隐藏', + 'Submit' => '提交', + 'Reset' => '重置', + 'Execute' => '执行', + 'Close' => '关闭', + 'Search' => '搜索', + 'Refresh' => '刷新', + 'First' => '首页', + 'Previous' => '上一页', + 'Next' => '下一页', + 'Last' => '末页', + 'None' => '无', + 'Online' => '在线', + 'Logout' => '退出', + 'Profile' => '个人资料', + 'Index' => '首页', + 'Hot' => '热门', + 'Recommend' => '推荐', + 'Dashboard' => '控制台', + 'Code' => '编号', + 'Message' => '内容', + 'Line' => '行号', + 'File' => '文件', + 'Menu' => '菜单', + 'Type' => '类型', + 'Title' => '标题', + 'Content' => '内容', + 'Append' => '追加', + 'Memo' => '备注', + 'Parent' => '父级', + 'Params' => '参数', + 'Permission' => '权限', + 'Begin time' => '开始时间', + 'End time' => '结束时间', + 'Create time' => '创建时间', + 'Flag' => '标志', + 'Home' => '首页', + 'Store' => '插件市场', + 'Services' => '服务', + 'Download' => '下载', + 'Demo' => '演示', + 'Donation' => '捐赠', + 'Forum' => '社区', + 'Docs' => '文档', + 'Go back' => '返回首页', + 'Jump now' => '立即跳转', + 'Please login first' => '请登录后再操作/Please login first', + 'Send verification code' => '发送验证码', + 'Redirect now' => '立即跳转', + 'Operation completed' => '操作成功!', + 'Operation failed' => '操作失败!', + 'Unknown data format' => '未知的数据格式!', + 'Network error' => '网络错误!', + 'Advanced search' => '高级搜索', + 'Invalid parameters' => '未知参数', + 'No results were found' => '记录未找到', + 'Parameter %s can not be empty' => '参数%s不能为空', + 'You have no permission' => '你没有权限访问', + 'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中', + 'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转', +]; diff --git a/application/common/library/Auth.php b/application/common/library/Auth.php new file mode 100644 index 0000000..da79d07 --- /dev/null +++ b/application/common/library/Auth.php @@ -0,0 +1,671 @@ +config = array_merge($this->config, $config); + } + $this->options = array_merge($this->config, $options); + } + + /** + * + * @param array $options 参数 + * @return Auth + */ + public static function instance($options = []) + { + if (is_null(self::$instance)) { + self::$instance = new static($options); + } + + return self::$instance; + } + + /** + * 获取User模型 + * @return User + */ + public function getUser() + { + return $this->_user; + } + + /** + * 兼容调用user模型的属性 + * + * @param string $name + * @return mixed + */ + public function __get($name) + { + return $this->_user ? $this->_user->$name : null; + } + + /** + * 兼容调用user模型的属性 + */ + public function __isset($name) + { + return isset($this->_user) ? isset($this->_user->$name) : false; + } + + /** + * 根据Token初始化 + * + * @param string $token Token + * @return boolean + */ + public function init($token) + { + if ($this->_logined) { + return true; + } + if ($this->_error) { + return false; + } + $data = Token::get($token); + if (!$data) { + return false; + } + $user_id = intval($data['user_id']); + if ($user_id > 0) { + $user = User::get($user_id); + if (!$user) { + $this->setError('Account not exist'); + return false; + } + if ($user['status'] != 'normal') { + $this->setError('Account is locked'); + return false; + } + $this->_user = $user; + $this->_logined = true; + $this->_token = $token; + + //初始化成功的事件 + Hook::listen("user_init_successed", $this->_user); + + return true; + } else { + $this->setError('You are not logged in'); + return false; + } + } + + /** + * 注册用户 + * + * @param string $username 用户名 + * @param string $password 密码 + * @param string $email 邮箱 + * @param string $mobile 手机号 + * @param array $extend 扩展参数 + * @return boolean + */ + public function register($username, $password, $email = '', $mobile = '', $extend = []) + { + // 检测用户名、昵称、邮箱、手机号是否存在 + if (User::getByUsername($username)) { + $this->setError('Username already exist'); + return false; + } + if (User::getByNickname($username)) { + $this->setError('Nickname already exist'); + return false; + } + if ($email && User::getByEmail($email)) { + $this->setError('Email already exist'); + return false; + } + if ($mobile && User::getByMobile($mobile)) { + $this->setError('Mobile already exist'); + return false; + } + + $ip = request()->ip(); + $time = time(); + + $data = [ + 'username' => $username, + 'password' => $password, + 'email' => $email, + 'mobile' => $mobile, + 'level' => 1, + 'score' => 0, + 'avatar' => '', + ]; + $params = array_merge($data, [ + 'nickname' => preg_match("/^1[3-9]{1}\d{9}$/",$username) ? substr_replace($username,'****',3,4) : $username, + 'salt' => Random::alnum(), + 'jointime' => $time, + 'joinip' => $ip, + 'logintime' => $time, + 'loginip' => $ip, + 'prevtime' => $time, + 'status' => 'normal' + ]); + $params['password'] = $this->getEncryptPassword($password, $params['salt']); + $params = array_merge($params, $extend); + + //账号注册时需要开启事务,避免出现垃圾数据 + Db::startTrans(); + try { + $user = User::create($params, true); + + $this->_user = User::get($user->id); + + $userUpdate['member_number'] = 10000 + $user->id; + + User::update($userUpdate, ['id' => $user->id]); + + //设置Token + $this->_token = Random::uuid(); + Token::set($this->_token, $user->id, $this->keeptime); + + //设置登录状态 + $this->_logined = true; + + //注册成功的事件 + Hook::listen("user_register_successed", $this->_user, $data); + Db::commit(); + } catch (Exception $e) { + $this->setError($e->getMessage()); + Db::rollback(); + return false; + } + return true; + } + + /** + * 用户登录 + * + * @param string $account 账号,用户名、邮箱、手机号 + * @param string $password 密码 + * @param string $dialCode 判断是否手机号登录 + * @return boolean + */ + public function login($account, $password,$code='') + { + + $field = Validate::is($account, 'email') ? 'email' : (Validate::regex($account, '/^1\d{10}$/') ? 'mobile' : 'username'); + + if(!empty($code)){ + $field = 'mobile'; + } + $user = User::get([$field => $account]); + + if (!$user) { + $this->setError('Account is incorrect'); + return false; + } + + if ($user->status != 'normal') { + $this->setError('Account is locked'); + return false; + } + //更改验证规则 + if(!password_verify($password, $user->password)){ + //如果旧归责无法使用说明可能忘记了密码或者修改了密码 尝试使用新规则 如果都无效则说明密码输入错误 + if ($user->password != $this->getEncryptPassword($password, $user->salt)) { + $this->setError('Password is incorrect'); + return false; + } + } + /*if ($user->password != $this->getEncryptPassword($password, $user->salt)) { + $this->setError('Password is incorrect'); + return false; + }*/ + + //直接登录会员 + return $this->direct($user->id); + } + + + + /** + * 退出 + * + * @return boolean + */ + public function logout() + { + if (!$this->_logined) { + $this->setError('You are not logged in'); + return false; + } + //设置登录标识 + $this->_logined = false; + //删除Token + Token::delete($this->_token); + //退出成功的事件 + Hook::listen("user_logout_successed", $this->_user); + return true; + } + + /** + * 修改密码 + * @param string $newpassword 新密码 + * @param string $oldpassword 旧密码 + * @param bool $ignoreoldpassword 忽略旧密码 + * @return boolean + */ + public function changepwd($newpassword, $oldpassword = '', $ignoreoldpassword = false) + { + if (!$this->_logined) { + $this->setError('You are not logged in'); + return false; + } + //判断旧密码是否正确 + if(!password_verify($oldpassword,$this->_user->password) || $ignoreoldpassword){ + if ($this->_user->password == $this->getEncryptPassword($oldpassword, $this->_user->salt) || $ignoreoldpassword) { + Db::startTrans(); + try { + $salt = Random::alnum(); + $newpassword = $this->getEncryptPassword($newpassword, $salt); + $this->_user->save(['loginfailure' => 0, 'password' => $newpassword, 'salt' => $salt]); + + Token::delete($this->_token); + //修改密码成功的事件 + Hook::listen("user_changepwd_successed", $this->_user); + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + $this->setError($e->getMessage()); + return false; + } + return true; + } else { + $this->setError('Password is incorrect'); + return false; + } + } + + Db::startTrans(); + try { + $salt = Random::alnum(); + $newpassword = $this->getEncryptPassword($newpassword, $salt); + $this->_user->save(['loginfailure' => 0, 'password' => $newpassword, 'salt' => $salt]); + + Token::delete($this->_token); + //修改密码成功的事件 + Hook::listen("user_changepwd_successed", $this->_user); + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + $this->setError($e->getMessage()); + return false; + } + return true; + + + + } + + + /** + * 直接登录账号 + * @param int $user_id + * @return boolean + */ + public function direct($user_id) + { + $user = User::get($user_id); + if ($user) { + Db::startTrans(); + try { + $ip = request()->ip(); + $time = time(); + + //判断连续登录和最大连续登录 + if ($user->logintime < \fast\Date::unixtime('day')) { + $user->successions = $user->logintime < \fast\Date::unixtime('day', -1) ? 1 : $user->successions + 1; + $user->maxsuccessions = max($user->successions, $user->maxsuccessions); + } + + $user->prevtime = $user->logintime; + //记录本次登录的IP和时间 + $user->loginip = $ip; + $user->logintime = $time; + //重置登录失败次数 + $user->loginfailure = 0; + + $user->save(); + + $this->_user = $user; + + $this->_token = Random::uuid(); + Token::set($this->_token, $user->id, $this->keeptime); + + $this->_logined = true; + + //登录成功的事件 + Hook::listen("user_login_successed", $this->_user); + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + $this->setError($e->getMessage()); + return false; + } + + return true; + } else { + return false; + } + } + + /** + * 检测是否是否有对应权限 + * @param string $path 控制器/方法 + * @param string $module 模块 默认为当前模块 + * @return boolean + */ + public function check($path = null, $module = null) + { + if (!$this->_logined) { + return false; + } + + $ruleList = $this->getRuleList(); + $rules = []; + foreach ($ruleList as $k => $v) { + $rules[] = $v['name']; + } + $url = ($module ? $module : request()->module()) . '/' . (is_null($path) ? $this->getRequestUri() : $path); + $url = strtolower(str_replace('.', '/', $url)); + return in_array($url, $rules) ? true : false; + } + + /** + * 判断是否登录 + * @return boolean + */ + public function isLogin() + { + if ($this->_logined) { + return true; + } + return false; + } + + /** + * 判断大屏是否需要登陆 + * @Author:Soar + * @Time:2023/12/19 9:23 + * @return bool + */ + public function isLiveLogin() + { + if ($this->_loginLive) { + return true; + } + return false; + } + + /** + * 获取当前Token + * @return string + */ + public function getToken() + { + return $this->_token; + } + + /** + * 获取会员基本信息 + */ + public function getUserinfo() + { + $data = $this->_user->toArray(); + $allowFields = $this->getAllowFields(); + $userinfo = array_intersect_key($data, array_flip($allowFields)); + $userinfo = array_merge($userinfo, Token::get($this->_token)); + return $userinfo; + } + + /** + * 获取会员组别规则列表 + * @return array + */ + public function getRuleList() + { + if ($this->rules) { + return $this->rules; + } + $group = $this->_user->group; + if (!$group) { + return []; + } + $rules = explode(',', $group->rules); + $this->rules = UserRule::where('status', 'normal')->where('id', 'in', $rules)->field('id,pid,name,title,ismenu')->select(); + return $this->rules; + } + + /** + * 获取当前请求的URI + * @return string + */ + public function getRequestUri() + { + return $this->requestUri; + } + + /** + * 设置当前请求的URI + * @param string $uri + */ + public function setRequestUri($uri) + { + $this->requestUri = $uri; + } + + /** + * 获取允许输出的字段 + * @return array + */ + public function getAllowFields() + { + return $this->allowFields; + } + + /** + * 设置允许输出的字段 + * @param array $fields + */ + public function setAllowFields($fields) + { + $this->allowFields = $fields; + } + + /** + * 删除一个指定会员 + * @param int $user_id 会员ID + * @return boolean + */ + public function delete($user_id) + { + $user = User::get($user_id); + if (!$user) { + return false; + } + Db::startTrans(); + try { + // 删除会员 + User::destroy($user_id); + // 删除会员指定的所有Token + Token::clear($user_id); + + Hook::listen("user_delete_successed", $user); + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + $this->setError($e->getMessage()); + return false; + } + return true; + } + + /** + * 获取密码加密后的字符串 + * @param string $password 密码 + * @param string $salt 密码盐 + * @return string + */ + public function getEncryptPassword($password, $salt = '') + { + return md5(md5($password) . $salt); + } + + /** + * 检测当前控制器和方法是否匹配传递的数组 + * + * @param array $arr 需要验证权限的数组 + * @return boolean + */ + public function match($arr = []) + { + $request = Request::instance(); + $arr = is_array($arr) ? $arr : explode(',', $arr); + if (!$arr) { + return false; + } + $arr = array_map('strtolower', $arr); + // 是否存在 + if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) { + return true; + } + + // 没找到匹配 + return false; + } + + /** + * 设置会话有效时间 + * @param int $keeptime 默认为永久 + */ + public function keeptime($keeptime = 0) + { + $this->keeptime = $keeptime; + } + + /** + * 渲染用户数据 + * @param array $datalist 二维数组 + * @param mixed $fields 加载的字段列表 + * @param string $fieldkey 渲染的字段 + * @param string $renderkey 结果字段 + * @return array + */ + public function render(&$datalist, $fields = [], $fieldkey = 'user_id', $renderkey = 'userinfo') + { + $fields = !$fields ? ['id', 'nickname', 'level', 'avatar'] : (is_array($fields) ? $fields : explode(',', $fields)); + $ids = []; + foreach ($datalist as $k => $v) { + if (!isset($v[$fieldkey])) { + continue; + } + $ids[] = $v[$fieldkey]; + } + $list = []; + if ($ids) { + if (!in_array('id', $fields)) { + $fields[] = 'id'; + } + $ids = array_unique($ids); + $selectlist = User::where('id', 'in', $ids)->column($fields); + foreach ($selectlist as $k => $v) { + $list[$v['id']] = $v; + } + } + foreach ($datalist as $k => &$v) { + $v[$renderkey] = isset($list[$v[$fieldkey]]) ? $list[$v[$fieldkey]] : null; + } + unset($v); + return $datalist; + } + + /** + * 设置错误信息 + * + * @param string $error 错误信息 + * @return Auth + */ + public function setError($error) + { + $this->_error = $error; + return $this; + } + + /** + * 获取错误信息 + * @return string + */ + public function getError() + { + return $this->_error ? __($this->_error) : ''; + } + + /** + * @return mixed + */ + public function getMatchId() + { + return $this->match_id; + } + + /** + * @param mixed $match_id + */ + public function setMatchId($match_id): void + { + $this->match_id = $match_id; + } + + public function loginScreen($param) + { + $this->_screen_token = Random::uuid(); + + Token::set($this->_screen_token, $param->match_id, $this->keeptime); + + $this->_loginLive = true; + $this->_screen = $param; + + //登录成功的事件 + Hook::listen("screen_login_successed", $this->_screen); + + return true; + } +} diff --git a/application/common/library/Email.php b/application/common/library/Email.php new file mode 100644 index 0000000..f51b068 --- /dev/null +++ b/application/common/library/Email.php @@ -0,0 +1,236 @@ + 'utf-8', //编码格式 + 'debug' => false, //调式模式 + 'mail_type' => 0, //状态 + ]; + + /** + * 初始化 + * @access public + * @param array $options 参数 + * @return Email + */ + public static function instance($options = []) + { + if (is_null(self::$instance)) { + self::$instance = new static($options); + } + + return self::$instance; + } + + /** + * 构造函数 + * @param array $options + */ + public function __construct($options = []) + { + if ($config = Config::get('site')) { + $this->options = array_merge($this->options, $config); + } + $this->options = array_merge($this->options, $options); + $secureArr = [0 => '', 1 => 'tls', 2 => 'ssl']; + $secure = isset($secureArr[$this->options['mail_verify_type']]) ? $secureArr[$this->options['mail_verify_type']] : ''; + + $logger = isset($this->options['debug']) && $this->options['debug'] ? new Log : null; + $this->mail = new Mailer($logger); + $this->mail->setServer($this->options['mail_smtp_host'], $this->options['mail_smtp_port'], $secure); + $this->mail->setAuth($this->options['mail_from'], $this->options['mail_smtp_pass']); + + //设置发件人 + $this->from($this->options['mail_from'], $this->options['mail_smtp_user']); + } + + /** + * 设置邮件主题 + * @param string $subject 邮件主题 + * @return $this + */ + public function subject($subject) + { + $this->mail->setSubject($subject); + return $this; + } + + /** + * 设置发件人 + * @param string $email 发件人邮箱 + * @param string $name 发件人名称 + * @return $this + */ + public function from($email, $name = '') + { + $this->mail->setFrom($name, $email); + return $this; + } + + /** + * 设置收件人 + * @param mixed $email 收件人,多个收件人以,进行分隔 + * @return $this + */ + public function to($email) + { + $emailArr = $this->buildAddress($email); + foreach ($emailArr as $address => $name) { + $this->mail->addTo($name, $address); + } + + return $this; + } + + /** + * 设置抄送 + * @param mixed $email 收件人,多个收件人以,进行分隔 + * @param string $name 收件人名称 + * @return Email + */ + public function cc($email, $name = '') + { + $emailArr = $this->buildAddress($email); + if (count($emailArr) == 1 && $name) { + $emailArr[key($emailArr)] = $name; + } + foreach ($emailArr as $address => $name) { + $this->mail->addCC($name, $address); + } + return $this; + } + + /** + * 设置密送 + * @param mixed $email 收件人,多个收件人以,进行分隔 + * @param string $name 收件人名称 + * @return Email + */ + public function bcc($email, $name = '') + { + $emailArr = $this->buildAddress($email); + if (count($emailArr) == 1 && $name) { + $emailArr[key($emailArr)] = $name; + } + foreach ($emailArr as $address => $name) { + $this->mail->addBCC($name, $address); + } + return $this; + } + + /** + * 设置邮件正文 + * @param string $body 邮件下方 + * @param boolean $ishtml 是否HTML格式 + * @return $this + */ + public function message($body, $ishtml = true) + { + $this->mail->setBody($body); + return $this; + } + + /** + * 添加附件 + * @param string $path 附件路径 + * @param string $name 附件名称 + * @return Email + */ + public function attachment($path, $name = '') + { + $this->mail->addAttachment($name, $path); + return $this; + } + + /** + * 构建Email地址 + * @param mixed $emails Email数据 + * @return array + */ + protected function buildAddress($emails) + { + if (!is_array($emails)) { + $emails = array_flip(explode(',', str_replace(";", ",", $emails))); + foreach ($emails as $key => $value) { + $emails[$key] = strstr($key, '@', true); + } + } + return $emails; + } + + /** + * 获取最后产生的错误 + * @return string + */ + public function getError() + { + return $this->error; + } + + /** + * 设置错误 + * @param string $error 信息信息 + */ + protected function setError($error) + { + $this->error = $error; + } + + /** + * 发送邮件 + * @return boolean + */ + public function send() + { + $result = false; + if (in_array($this->options['mail_type'], [1, 2])) { + try { + $result = $this->mail->send(); + } catch (SendException $e) { + $this->setError($e->getCode() . $e->getMessage()); + } catch (CodeException $e) { + preg_match_all("/Expected: (\d+)\, Got: (\d+)( \| (.*))?\$/i", $e->getMessage(), $matches); + $code = isset($matches[2][3]) ? $matches[2][3] : 0; + $message = isset($matches[2][0]) ? $matches[4][0] : $e->getMessage(); + $message = mb_convert_encoding($message, 'UTF-8', 'GBK,GB2312,BIG5'); + $this->setError($message); + } catch (\Exception $e) { + $this->setError($e->getMessage()); + } + + $this->setError($result ? '' : $this->getError()); + } else { + //邮件功能已关闭 + $this->setError(__('Mail already closed')); + } + return $result; + } + +} diff --git a/application/common/library/Ems.php b/application/common/library/Ems.php new file mode 100644 index 0000000..4e5212a --- /dev/null +++ b/application/common/library/Ems.php @@ -0,0 +1,159 @@ + $email, 'event' => $event]) + ->order('id', 'DESC') + ->find(); + Hook::listen('ems_get', $ems, null, true); + return $ems ? $ems : null; + } + + /** + * 发送验证码 + * + * @param int $email 邮箱 + * @param int $code 验证码,为空时将自动生成4位数字 + * @param string $event 事件 + * @return boolean + */ + public static function send($email, $code = null, $event = 'default') + { + $code = is_null($code) ? Random::numeric(config('captcha.length')) : $code; + $time = time(); + $ip = request()->ip(); + $ems = \app\common\model\Ems::create(['event' => $event, 'email' => $email, 'code' => $code, 'ip' => $ip, 'createtime' => $time]); + if (!Hook::get('ems_send')) { + //采用框架默认的邮件推送 + Hook::add('ems_send', function ($params) { + $obj = new Email(); + $result = $obj + ->to($params->email) + ->subject('请查收你的验证码!') + ->message("你的验证码是:" . $params->code . "," . ceil(self::$expire / 60) . "分钟内有效。") + ->send(); + return $result; + }); + } + $result = Hook::listen('ems_send', $ems, null, true); + if (!$result) { + $ems->delete(); + return false; + } + return true; + } + + /** + * 发送通知 + * + * @param mixed $email 邮箱,多个以,分隔 + * @param string $msg 消息内容 + * @param string $template 消息模板 + * @return boolean + */ + public static function notice($email, $msg = '', $template = null) + { + $params = [ + 'email' => $email, + 'msg' => $msg, + 'template' => $template + ]; + if (!Hook::get('ems_notice')) { + //采用框架默认的邮件推送 + Hook::add('ems_notice', function ($params) { + $subject = '你收到一封新的邮件!'; + $content = $params['msg']; + $email = new Email(); + $result = $email->to($params['email']) + ->subject($subject) + ->message($content) + ->send(); + return $result; + }); + } + $result = Hook::listen('ems_notice', $params, null, true); + return $result ? true : false; + } + + /** + * 校验验证码 + * + * @param int $email 邮箱 + * @param int $code 验证码 + * @param string $event 事件 + * @return boolean + */ + public static function check($email, $code, $event = 'default') + { + $time = time() - self::$expire; + $ems = \app\common\model\Ems::where(['email' => $email, 'event' => $event]) + ->order('id', 'DESC') + ->find(); + if ($ems) { + if ($ems['createtime'] > $time && $ems['times'] <= self::$maxCheckNums) { + $correct = $code == $ems['code']; + if (!$correct) { + $ems->times = $ems->times + 1; + $ems->save(); + return false; + } else { + $result = Hook::listen('ems_check', $ems, null, true); + return true; + } + } else { + // 过期则清空该邮箱验证码 + self::flush($email, $event); + return false; + } + } else { + return false; + } + } + + /** + * 清空指定邮箱验证码 + * + * @param int $email 邮箱 + * @param string $event 事件 + * @return boolean + */ + public static function flush($email, $event = 'default') + { + \app\common\model\Ems:: + where(['email' => $email, 'event' => $event]) + ->delete(); + Hook::listen('ems_flush'); + return true; + } +} diff --git a/application/common/library/Log.php b/application/common/library/Log.php new file mode 100644 index 0000000..a9d891d --- /dev/null +++ b/application/common/library/Log.php @@ -0,0 +1,29 @@ +update(['status' => 'normal']); + return true; + } + + /** + * 禁用菜单 + * @param string $name + * @return boolean + */ + public static function disable($name) + { + $ids = self::getAuthRuleIdsByName($name); + if (!$ids) { + return false; + } + AuthRule::where('id', 'in', $ids)->update(['status' => 'hidden']); + return true; + } + + /** + * 升级菜单 + * @param string $name 插件名称 + * @param array $menu 新菜单 + * @return bool + */ + public static function upgrade($name, $menu) + { + $ids = self::getAuthRuleIdsByName($name); + $old = AuthRule::where('id', 'in', $ids)->select(); + $old = collection($old)->toArray(); + $old = array_column($old, null, 'name'); + + Db::startTrans(); + try { + self::menuUpdate($menu, $old); + $ids = []; + foreach ($old as $index => $item) { + if (!isset($item['keep'])) { + $ids[] = $item['id']; + } + } + if ($ids) { + //旧版本的菜单需要做删除处理 + $config = Service::config($name); + $menus = isset($config['menus']) ? $config['menus'] : []; + $where = ['id' => ['in', $ids]]; + if ($menus) { + //必须是旧版本中的菜单,可排除用户自主创建的菜单 + $where['name'] = ['in', $menus]; + } + AuthRule::where($where)->delete(); + } + + Db::commit(); + } catch (PDOException $e) { + Db::rollback(); + return false; + } + + Menu::refresh($name, $menu); + return true; + } + + /** + * 刷新插件菜单配置缓存 + * @param string $name + * @param array $menu + */ + public static function refresh($name, $menu = []) + { + if (!$menu) { + // $menu为空时表示首次安装,首次安装需刷新插件菜单标识缓存 + $menuIds = Menu::getAuthRuleIdsByName($name); + $menus = Db::name("auth_rule")->where('id', 'in', $menuIds)->column('name'); + } else { + // 刷新新的菜单缓存 + $getMenus = function ($menu) use (&$getMenus) { + $result = []; + foreach ($menu as $index => $item) { + $result[] = $item['name']; + $result = array_merge($result, isset($item['sublist']) && is_array($item['sublist']) ? $getMenus($item['sublist']) : []); + } + return $result; + }; + $menus = $getMenus($menu); + } + + //刷新新的插件核心菜单缓存 + Service::config($name, ['menus' => $menus]); + } + + /** + * 导出指定名称的菜单规则 + * @param string $name + * @return array + */ + public static function export($name) + { + $ids = self::getAuthRuleIdsByName($name); + if (!$ids) { + return []; + } + $menuList = []; + $menu = AuthRule::getByName($name); + if ($menu) { + $ruleList = collection(AuthRule::where('id', 'in', $ids)->select())->toArray(); + $menuList = Tree::instance()->init($ruleList)->getTreeArray($menu['id']); + } + return $menuList; + } + + /** + * 菜单升级 + * @param array $newMenu + * @param array $oldMenu + * @param int $parent + * @throws Exception + */ + private static function menuUpdate($newMenu, &$oldMenu, $parent = 0) + { + if (!is_numeric($parent)) { + $parentRule = AuthRule::getByName($parent); + $pid = $parentRule ? $parentRule['id'] : 0; + } else { + $pid = $parent; + } + $allow = array_flip(['file', 'name', 'title', 'url', 'icon', 'condition', 'remark', 'ismenu', 'menutype', 'extend', 'weigh']); + foreach ($newMenu as $k => $v) { + $hasChild = isset($v['sublist']) && $v['sublist'] ? true : false; + $data = array_intersect_key($v, $allow); + $data['ismenu'] = isset($data['ismenu']) ? $data['ismenu'] : ($hasChild ? 1 : 0); + $data['icon'] = isset($data['icon']) ? $data['icon'] : ($hasChild ? 'fa fa-list' : 'fa fa-circle-o'); + $data['pid'] = $pid; + $data['status'] = 'normal'; + if (!isset($oldMenu[$data['name']])) { + $menu = AuthRule::create($data); + } else { + $menu = $oldMenu[$data['name']]; + //更新旧菜单 + AuthRule::update($data, ['id' => $menu['id']]); + $oldMenu[$data['name']]['keep'] = true; + } + if ($hasChild) { + self::menuUpdate($v['sublist'], $oldMenu, $menu['id']); + } + } + } + + /** + * 根据名称获取规则IDS + * @param string $name + * @return array + */ + public static function getAuthRuleIdsByName($name) + { + $ids = []; + $menu = AuthRule::getByName($name); + if ($menu) { + // 必须将结果集转换为数组 + $ruleList = collection(AuthRule::order('weigh', 'desc')->field('id,pid,name')->select())->toArray(); + // 构造菜单数据 + $ids = Tree::instance()->init($ruleList)->getChildrenIds($menu['id'], true); + } + return $ids; + } + +} diff --git a/application/common/library/Security.php b/application/common/library/Security.php new file mode 100644 index 0000000..6832325 --- /dev/null +++ b/application/common/library/Security.php @@ -0,0 +1,874 @@ +', + '<', + '>', + "'", + '"', + '&', + '$', + '#', + '{', + '}', + '[', + ']', + '=', + ';', + '?', + '%20', + '%22', + '%3c', // < + '%253c', // < + '%3e', // > + '%0e', // > + '%28', // ( + '%29', // ) + '%2528', // ( + '%26', // & + '%24', // $ + '%3f', // ? + '%3b', // ; + '%3d' // = + ); + + /** + * Character set + * + * Will be overridden by the constructor. + * + * @var string + */ + public $charset = 'UTF-8'; + + /** + * XSS Hash + * + * Random Hash for protecting URLs. + * + * @var string + */ + protected $_xss_hash; + + /** + * List of never allowed strings + * + * @var array + */ + protected $_never_allowed_str = array( + 'document.cookie' => '[removed]', + '(document).cookie' => '[removed]', + 'document.write' => '[removed]', + '(document).write' => '[removed]', + '.parentNode' => '[removed]', + '.innerHTML' => '[removed]', + '-moz-binding' => '[removed]', + '' => '-->', + ' '<![CDATA[', + '' => '<comment>', + '<%' => '<%' + ); + + /** + * List of never allowed regex replacements + * + * @var array + */ + protected $_never_allowed_regex = array( + 'javascript\s*:', + '(\(?document\)?|\(?window\)?(\.document)?)\.(location|on\w*)', + 'expression\s*(\(|&\#40;)', // CSS and IE + 'vbscript\s*:', // IE, surprise! + 'wscript\s*:', // IE + 'jscript\s*:', // IE + 'vbs\s*:', // IE + 'Redirect\s+30\d', + "([\"'])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?" + ); + + protected $options = [ + 'placeholder' => '[removed]' + ]; + + /** + * Class constructor + * + * @return void + */ + public function __construct($options = []) + { + $this->options = array_merge($this->options, $options); + foreach ($this->_never_allowed_str as $index => &$item) { + $item = str_replace('[removed]', $this->options['placeholder'], $item); + } + } + + /** + * + * @param array $options 参数 + * @return Security + */ + public static function instance($options = []) + { + if (is_null(self::$instance)) { + self::$instance = new static($options); + } + + return self::$instance; + } + + /** + * XSS Clean + * + * Sanitizes data so that Cross Site Scripting Hacks can be + * prevented. This method does a fair amount of work but + * it is extremely thorough, designed to prevent even the + * most obscure XSS attempts. Nothing is ever 100% foolproof, + * of course, but I haven't been able to get anything passed + * the filter. + * + * Note: Should only be used to deal with data upon submission. + * It's not something that should be used for general + * runtime processing. + * + * @link http://channel.bitflux.ch/wiki/XSS_Prevention + * Based in part on some code and ideas from Bitflux. + * + * @link http://ha.ckers.org/xss.html + * To help develop this script I used this great list of + * vulnerabilities along with a few other hacks I've + * harvested from examining vulnerabilities in other programs. + * + * @param string|string[] $str Input data + * @param bool $is_image Whether the input is an image + * @return string + */ + public function xss_clean($str, $is_image = false) + { + // Is the string an array? + if (is_array($str)) { + foreach ($str as $key => &$value) { + $str[$key] = $this->xss_clean($value); + } + + return $str; + } + + // Remove Invisible Characters + $str = $this->remove_invisible_characters($str); + + /* + * URL Decode + * + * Just in case stuff like this is submitted: + * + * Google + * + * Note: Use rawurldecode() so it does not remove plus signs + */ + if (stripos($str, '%') !== false) { + do { + $oldstr = $str; + $str = rawurldecode($str); + $str = preg_replace_callback('#%(?:\s*[0-9a-f]){2,}#i', array($this, '_urldecodespaces'), $str); + } while ($oldstr !== $str); + unset($oldstr); + } + + /* + * Convert character entities to ASCII + * + * This permits our tests below to work reliably. + * We only convert entities that are within tags since + * these are the ones that will pose security problems. + */ + $str = preg_replace_callback("/[^a-z0-9>]+[a-z0-9]+=([\'\"]).*?\\1/si", array($this, '_convert_attribute'), $str); + $str = preg_replace_callback('/<\w+.*/si', array($this, '_decode_entity'), $str); + + // Remove Invisible Characters Again! + $str = $this->remove_invisible_characters($str); + + /* + * Convert all tabs to spaces + * + * This prevents strings like this: ja vascript + * NOTE: we deal with spaces between characters later. + * NOTE: preg_replace was found to be amazingly slow here on + * large blocks of data, so we use str_replace. + */ + $str = str_replace("\t", ' ', $str); + + // Capture converted string for later comparison + $converted_string = $str; + + // Remove Strings that are never allowed + $str = $this->_do_never_allowed($str); + + /* + * Makes PHP tags safe + * + * Note: XML tags are inadvertently replaced too: + * + * '), array('<?', '?>'), $str); + } + + /* + * Compact any exploded words + * + * This corrects words like: j a v a s c r i p t + * These words are compacted back to their correct state. + */ + $words = array( + 'javascript', + 'expression', + 'vbscript', + 'jscript', + 'wscript', + 'vbs', + 'script', + 'base64', + 'applet', + 'alert', + 'document', + 'write', + 'cookie', + 'window', + 'confirm', + 'prompt', + 'eval' + ); + + foreach ($words as $word) { + $word = implode('\s*', str_split($word)) . '\s*'; + + // We only want to do this when it is followed by a non-word character + // That way valid stuff like "dealer to" does not become "dealerto" + $str = preg_replace_callback('#(' . substr($word, 0, -3) . ')(\W)#is', array($this, '_compact_exploded_words'), $str); + } + + /* + * Remove disallowed Javascript in links or img tags + * We used to do some version comparisons and use of stripos(), + * but it is dog slow compared to these simplified non-capturing + * preg_match(), especially if the pattern exists in the string + * + * Note: It was reported that not only space characters, but all in + * the following pattern can be parsed as separators between a tag name + * and its attributes: [\d\s"\'`;,\/\=\(\x00\x0B\x09\x0C] + * ... however, $this->remove_invisible_characters() above already strips the + * hex-encoded ones, so we'll skip them below. + */ + do { + $original = $str; + + if (preg_match('/]+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str); + } + + if (preg_match('/]*?)(?:\s?/?>|$)#si', array($this, '_js_img_removal'), $str); + } + + if (preg_match('/script|xss/i', $str)) { + $str = preg_replace('##si', $this->options['placeholder'], $str); + } + } while ($original !== $str); + unset($original); + + /* + * Sanitize naughty HTML elements + * + * If a tag containing any of the words in the list + * below is found, the tag gets converted to entities. + * + * So this: + * Becomes: <blink> + */ + $pattern = '#' + . '<((?/*\s*)((?[a-z0-9]+)(?=[^a-z0-9]|$)|.+)' // tag start and name, followed by a non-tag character + . '[^\s\042\047a-z0-9>/=]*' // a valid attribute character immediately after the tag would count as a separator + // optional attributes + . '(?(?:[\s\042\047/=]*' // non-attribute characters, excluding > (tag close) for obvious reasons + . '[^\s\042\047>/=]+' // attribute characters + // optional attribute-value + . '(?:\s*=' // attribute-value separator + . '(?:[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*))' // single, double or non-quoted value + . ')?' // end optional attribute-value group + . ')*)' // end optional attributes group + . '[^>]*)(?\>)?#isS'; + + // Note: It would be nice to optimize this for speed, BUT + // only matching the naughty elements here results in + // false positives and in turn - vulnerabilities! + do { + $old_str = $str; + $str = preg_replace_callback($pattern, array($this, '_sanitize_naughty_html'), $str); + } while ($old_str !== $str); + unset($old_str); + + /* + * Sanitize naughty scripting elements + * + * Similar to above, only instead of looking for + * tags it looks for PHP and JavaScript commands + * that are disallowed. Rather than removing the + * code, it simply converts the parenthesis to entities + * rendering the code un-executable. + * + * For example: eval('some code') + * Becomes: eval('some code') + */ + $str = preg_replace( + '#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', + '\\1\\2(\\3)', + $str + ); + + // Same thing, but for "tag functions" (e.g. eval`some code`) + $str = preg_replace( + '#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)`(.*?)`#si', + '\\1\\2`\\3`', + $str + ); + + // Final clean up + // This adds a bit of extra precaution in case + // something got through the above filters + $str = $this->_do_never_allowed($str); + + /* + * Images are Handled in a Special Way + * - Essentially, we want to know that after all of the character + * conversion is done whether any unwanted, likely XSS, code was found. + * If not, we return TRUE, as the image is clean. + * However, if the string post-conversion does not matched the + * string post-removal of XSS, then it fails, as there was unwanted XSS + * code found and removed/changed during processing. + */ + if ($is_image === true) { + return ($str === $converted_string); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * XSS Hash + * + * Generates the XSS hash if needed and returns it. + * + * @return string XSS hash + */ + public function xss_hash() + { + if ($this->_xss_hash === null) { + $rand = $this->get_random_bytes(16); + $this->_xss_hash = ($rand === false) + ? md5(uniqid(mt_rand(), true)) + : bin2hex($rand); + } + + return $this->_xss_hash; + } + + // -------------------------------------------------------------------- + + /** + * Get random bytes + * + * @param int $length Output length + * @return string + */ + public function get_random_bytes($length) + { + if (empty($length) OR !ctype_digit((string)$length)) { + return false; + } + + if (function_exists('random_bytes')) { + try { + // The cast is required to avoid TypeError + return random_bytes((int)$length); + } catch (Exception $e) { + // If random_bytes() can't do the job, we can't either ... + // There's no point in using fallbacks. + return false; + } + } + + // Unfortunately, none of the following PRNGs is guaranteed to exist ... + if (defined('MCRYPT_DEV_URANDOM') && ($output = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)) !== false) { + return $output; + } + + + if (is_readable('/dev/urandom') && ($fp = fopen('/dev/urandom', 'rb')) !== false) { + // Try not to waste entropy ... + stream_set_chunk_size($fp, $length); + $output = fread($fp, $length); + fclose($fp); + if ($output !== false) { + return $output; + } + } + + if (function_exists('openssl_random_pseudo_bytes')) { + return openssl_random_pseudo_bytes($length); + } + + return false; + } + + // -------------------------------------------------------------------- + + /** + * HTML Entities Decode + * + * A replacement for html_entity_decode() + * + * The reason we are not using html_entity_decode() by itself is because + * while it is not technically correct to leave out the semicolon + * at the end of an entity most browsers will still interpret the entity + * correctly. html_entity_decode() does not convert entities without + * semicolons, so we are left with our own little solution here. Bummer. + * + * @link https://secure.php.net/html-entity-decode + * + * @param string $str Input + * @param string $charset Character set + * @return string + */ + public function entity_decode($str, $charset = null) + { + if (strpos($str, '&') === false) { + return $str; + } + + static $_entities; + + isset($charset) OR $charset = $this->charset; + isset($_entities) OR $_entities = array_map('strtolower', get_html_translation_table(HTML_ENTITIES, ENT_COMPAT | ENT_HTML5, $charset)); + + do { + $str_compare = $str; + + // Decode standard entities, avoiding false positives + if (preg_match_all('/&[a-z]{2,}(?![a-z;])/i', $str, $matches)) { + $replace = array(); + $matches = array_unique(array_map('strtolower', $matches[0])); + foreach ($matches as &$match) { + if (($char = array_search($match . ';', $_entities, true)) !== false) { + $replace[$match] = $char; + } + } + + $str = str_replace(array_keys($replace), array_values($replace), $str); + } + + // Decode numeric & UTF16 two byte entities + $str = html_entity_decode( + preg_replace('/(&#(?:x0*[0-9a-f]{2,5}(?![0-9a-f;])|(?:0*\d{2,4}(?![0-9;]))))/iS', '$1;', $str), + ENT_COMPAT | ENT_HTML5, + $charset + ); + } while ($str_compare !== $str); + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Sanitize Filename + * + * @param string $str Input file name + * @param bool $relative_path Whether to preserve paths + * @return string + */ + public function sanitize_filename($str, $relative_path = false) + { + $bad = $this->filename_bad_chars; + + if (!$relative_path) { + $bad[] = './'; + $bad[] = '/'; + } + + $str = $this->remove_invisible_characters($str, false); + + do { + $old = $str; + $str = str_replace($bad, '', $str); + } while ($old !== $str); + + return stripslashes($str); + } + + // ---------------------------------------------------------------- + + /** + * Strip Image Tags + * + * @param string $str + * @return string + */ + public function strip_image_tags($str) + { + return preg_replace( + array( + '##i', + '#`]+)).*?\>#i' + ), + '\\2', + $str + ); + } + + // ---------------------------------------------------------------- + + /** + * URL-decode taking spaces into account + * + * @param array $matches + * @return string + */ + protected function _urldecodespaces($matches) + { + $input = $matches[0]; + $nospaces = preg_replace('#\s+#', '', $input); + return ($nospaces === $input) + ? $input + : rawurldecode($nospaces); + } + + // ---------------------------------------------------------------- + + /** + * Compact Exploded Words + * + * Callback method for xss_clean() to remove whitespace from + * things like 'j a v a s c r i p t'. + * + * @param array $matches + * @return string + */ + protected function _compact_exploded_words($matches) + { + return preg_replace('/\s+/s', '', $matches[1]) . $matches[2]; + } + + // -------------------------------------------------------------------- + + /** + * Sanitize Naughty HTML + * + * Callback method for xss_clean() to remove naughty HTML elements. + * + * @param array $matches + * @return string + */ + protected function _sanitize_naughty_html($matches) + { + static $naughty_tags = array( + 'alert', + 'area', + 'prompt', + 'confirm', + 'applet', + 'audio', + 'basefont', + 'base', + 'behavior', + 'bgsound', + 'blink', + 'body', + 'embed', + 'expression', + 'form', + 'frameset', + 'frame', + 'head', + 'html', + 'ilayer', + 'iframe', + 'input', + 'button', + 'select', + 'isindex', + 'layer', + 'link', + 'meta', + 'keygen', + 'object', + 'plaintext', + 'style', + 'script', + 'textarea', + 'title', + 'math', + 'video', + 'svg', + 'xml', + 'xss' + ); + + static $evil_attributes = array( + 'on\w+', + 'style', + 'xmlns', + 'formaction', + 'form', + 'xlink:href', + 'FSCommand', + 'seekSegmentTime' + ); + + // First, escape unclosed tags + if (empty($matches['closeTag'])) { + return '<' . $matches[1]; + } // Is the element that we caught naughty? If so, escape it + elseif (in_array(strtolower($matches['tagName']), $naughty_tags, true)) { + return '<' . $matches[1] . '>'; + } // For other tags, see if their attributes are "evil" and strip those + elseif (isset($matches['attributes'])) { + // We'll store the already filtered attributes here + $attributes = array(); + + // Attribute-catching pattern + $attributes_pattern = '#' + . '(?[^\s\042\047>/=]+)' // attribute characters + // optional attribute-value + . '(?:\s*=(?[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*)))' // attribute-value separator + . '#i'; + + // Blacklist pattern for evil attribute names + $is_evil_pattern = '#^(' . implode('|', $evil_attributes) . ')$#i'; + + // Each iteration filters a single attribute + do { + // Strip any non-alpha characters that may precede an attribute. + // Browsers often parse these incorrectly and that has been a + // of numerous XSS issues we've had. + $matches['attributes'] = preg_replace('#^[^a-z]+#i', '', $matches['attributes']); + + if (!preg_match($attributes_pattern, $matches['attributes'], $attribute, PREG_OFFSET_CAPTURE)) { + // No (valid) attribute found? Discard everything else inside the tag + break; + } + + if ( + // Is it indeed an "evil" attribute? + preg_match($is_evil_pattern, $attribute['name'][0]) + // Or does it have an equals sign, but no value and not quoted? Strip that too! + OR (trim($attribute['value'][0]) === '') + ) { + $attributes[] = 'xss=removed'; + } else { + $attributes[] = $attribute[0][0]; + } + + $matches['attributes'] = substr($matches['attributes'], $attribute[0][1] + strlen($attribute[0][0])); + } while ($matches['attributes'] !== ''); + + $attributes = empty($attributes) + ? '' + : ' ' . implode(' ', $attributes); + return '<' . $matches['slash'] . $matches['tagName'] . $attributes . '>'; + } + + return $matches[0]; + } + + // -------------------------------------------------------------------- + + /** + * JS Link Removal + * + * Callback method for xss_clean() to sanitize links. + * + * This limits the PCRE backtracks, making it more performance friendly + * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in + * PHP 5.2+ on link-heavy strings. + * + * @param array $match + * @return string + */ + protected function _js_link_removal($match) + { + return str_replace( + $match[1], + preg_replace( + '#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;|`|&\#96;)|javascript:|livescript:|mocha:|charset=|window\.|\(?document\)?\.|\.cookie|_filter_attributes($match[1]) + ), + $match[0] + ); + } + + // -------------------------------------------------------------------- + + /** + * JS Image Removal + * + * Callback method for xss_clean() to sanitize image tags. + * + * This limits the PCRE backtracks, making it more performance friendly + * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in + * PHP 5.2+ on image tag heavy strings. + * + * @param array $match + * @return string + */ + protected function _js_img_removal($match) + { + return str_replace( + $match[1], + preg_replace( + '#src=.*?(?:(?:alert|prompt|confirm|eval)(?:\(|&\#40;|`|&\#96;)|javascript:|livescript:|mocha:|charset=|window\.|\(?document\)?\.|\.cookie|_filter_attributes($match[1]) + ), + $match[0] + ); + } + + // -------------------------------------------------------------------- + + /** + * Attribute Conversion + * + * @param array $match + * @return string + */ + protected function _convert_attribute($match) + { + return str_replace(array('>', '<', '\\'), array('>', '<', '\\\\'), $match[0]); + } + + // -------------------------------------------------------------------- + + /** + * Filter Attributes + * + * Filters tag attributes for consistency and safety. + * + * @param string $str + * @return string + */ + protected function _filter_attributes($str) + { + $out = ''; + if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches)) { + foreach ($matches[0] as $match) { + $out .= preg_replace('#/\*.*?\*/#s', '', $match); + } + } + + return $out; + } + + // -------------------------------------------------------------------- + + /** + * HTML Entity Decode Callback + * + * @param array $match + * @return string + */ + protected function _decode_entity($match) + { + // Protect GET variables in URLs + // 901119URL5918AMP18930PROTECT8198 + $match = preg_replace('|\&([a-z\_0-9\-]+)\=([a-z\_0-9\-/]+)|i', $this->xss_hash() . '\\1=\\2', $match[0]); + + // Decode, then un-protect URL GET vars + return str_replace( + $this->xss_hash(), + '&', + $this->entity_decode($match, $this->charset) + ); + } + + // -------------------------------------------------------------------- + + /** + * Do Never Allowed + * + * @param string + * @return string + */ + protected function _do_never_allowed($str) + { + $str = str_replace(array_keys($this->_never_allowed_str), $this->_never_allowed_str, $str); + + foreach ($this->_never_allowed_regex as $regex) { + $str = preg_replace('#' . $regex . '#is', $this->options['placeholder'], $str); + } + + return $str; + } + + /** + * Remove Invisible Characters + */ + public function remove_invisible_characters($str, $url_encoded = true) + { + $non_displayables = array(); + + // every control character except newline (dec 10), + // carriage return (dec 13) and horizontal tab (dec 09) + if ($url_encoded) { + $non_displayables[] = '/%0[0-8bcef]/i'; // url encoded 00-08, 11, 12, 14, 15 + $non_displayables[] = '/%1[0-9a-f]/i'; // url encoded 16-31 + $non_displayables[] = '/%7f/i'; // url encoded 127 + } + + $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 + + do { + $str = preg_replace($non_displayables, '', $str, -1, $count); + } while ($count); + + return $str; + } + +} diff --git a/application/common/library/Sms.php b/application/common/library/Sms.php new file mode 100644 index 0000000..eaa4cd7 --- /dev/null +++ b/application/common/library/Sms.php @@ -0,0 +1,135 @@ + $mobile, 'event' => $event]) + ->order('id', 'DESC') + ->find(); + Hook::listen('sms_get', $sms, null, true); + return $sms ? $sms : null; + } + + /** + * 发送验证码 + * + * @param int $mobile 手机号 + * @param int $code 验证码,为空时将自动生成4位数字 + * @param string $event 事件 + * @return boolean + */ + public static function send($mobile, $code = null, $event = 'default') + { + $code = is_null($code) ? Random::numeric(config('captcha.length')) : $code; + $time = time(); + $ip = request()->ip(); + $sms = \app\common\model\Sms::create(['event' => $event, 'mobile' => $mobile, 'code' => $code, 'ip' => $ip, 'createtime' => $time]); + // var_dump($sms);exit; + $result = Hook::listen('sms_send', $sms, null, true); + if (!$result) { + $sms->delete(); + return false; + } + return true; + } + + /** + * 发送通知 + * + * @param mixed $mobile 手机号,多个以,分隔 + * @param string $msg 消息内容 + * @param string $template 消息模板 + * @return boolean + */ + public static function notice($mobile, $msg = '', $template = null) + { + $params = [ + 'mobile' => $mobile, + 'msg' => $msg, + 'template' => $template + ]; + $result = Hook::listen('sms_notice', $params, null, true); + return $result ? true : false; + } + + /** + * 校验验证码 + * + * @param int $mobile 手机号 + * @param int $code 验证码 + * @param string $event 事件 + * @return boolean + */ + public static function check($mobile, $code, $event = 'default') + { + $time = time() - self::$expire; + $sms = \app\common\model\Sms::where(['mobile' => $mobile, 'event' => $event]) + ->order('id', 'DESC') + ->find(); + if ($sms) { + if ($sms['createtime'] > $time && $sms['times'] <= self::$maxCheckNums) { + $correct = $code == $sms['code']; + if (!$correct) { + $sms->times = $sms->times + 1; + $sms->save(); + return false; + } else { + $result = Hook::listen('sms_check', $sms, null, true); + return $result; + } + } else { + // 过期则清空该手机验证码 + self::flush($mobile, $event); + return false; + } + } else { + return false; + } + } + + /** + * 清空指定手机号验证码 + * + * @param int $mobile 手机号 + * @param string $event 事件 + * @return boolean + */ + public static function flush($mobile, $event = 'default') + { + \app\common\model\Sms:: + where(['mobile' => $mobile, 'event' => $event]) + ->delete(); + Hook::listen('sms_flush'); + return true; + } +} diff --git a/application/common/library/Token.php b/application/common/library/Token.php new file mode 100644 index 0000000..3486c46 --- /dev/null +++ b/application/common/library/Token.php @@ -0,0 +1,161 @@ +check($token, $user_id); + } + + /** + * 读取Token + * @access public + * @param string $token Token标识 + * @param mixed $default 默认值 + * @return mixed + */ + public static function get($token, $default = false) + { + return self::init()->get($token) ?: $default; + } + + /** + * 写入Token + * @access public + * @param string $token Token标识 + * @param mixed $user_id 会员ID + * @param int|null $expire 有效时间 0为永久 + * @return boolean + */ + public static function set($token, $user_id, $expire = null) + { + return self::init()->set($token, $user_id, $expire); + } + + /** + * 删除Token(delete别名) + * @access public + * @param string $token Token标识 + * @return boolean + */ + public static function rm($token) + { + return self::delete($token); + } + + /** + * 删除Token + * @param string $token 标签名 + * @return bool + */ + public static function delete($token) + { + return self::init()->delete($token); + } + + /** + * 清除Token + * @access public + * @param int user_id 会员ID + * @return boolean + */ + public static function clear($user_id = null) + { + return self::init()->clear($user_id); + } + +} diff --git a/application/common/library/Upload.php b/application/common/library/Upload.php new file mode 100644 index 0000000..981fbfb --- /dev/null +++ b/application/common/library/Upload.php @@ -0,0 +1,449 @@ +config = Config::get('upload'); + $this->chunkDir = RUNTIME_PATH . 'chunks'; + if ($file) { + $this->setFile($file); + } + } + + /** + * 设置分片目录 + * @param $dir + */ + public function setChunkDir($dir) + { + $this->chunkDir = $dir; + } + + /** + * 获取文件 + * @return File + */ + public function getFile() + { + return $this->file; + } + + /** + * 设置文件 + * @param $file + * @throws UploadException + */ + public function setFile($file) + { + if (empty($file)) { + throw new UploadException(__('No file upload or server upload limit exceeded')); + } + + $fileInfo = $file->getInfo(); + $suffix = strtolower(pathinfo($fileInfo['name'], PATHINFO_EXTENSION)); + $suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file'; + $fileInfo['suffix'] = $suffix; + $fileInfo['imagewidth'] = 0; + $fileInfo['imageheight'] = 0; + + $this->file = $file; + $this->fileInfo = $fileInfo; + $this->checkExecutable(); + } + + /** + * 检测是否为可执行脚本 + * @return bool + * @throws UploadException + */ + protected function checkExecutable() + { + // var_dump(123);exit; + //禁止上传PHP和HTML文件 + if (in_array($this->fileInfo['type'], ['text/x-php', 'text/html']) || in_array($this->fileInfo['suffix'], ['php', 'html', 'htm', 'phar', 'phtml']) || preg_match("/^php(.*)/i", $this->fileInfo['suffix'])) { + throw new UploadException(__('Uploaded file format is limited')); + } + return true; + } + + /** + * 检测文件类型 + * @return bool + * @throws UploadException + */ + protected function checkMimetype() + { + $mimetypeArr = explode(',', strtolower($this->config['mimetype'])); + $typeArr = explode('/', $this->fileInfo['type']); + // var_dump($mimetypeArr);exit; + //Mimetype值不正确 + if (stripos($this->fileInfo['type'], '/') === false) { + throw new UploadException(__('Uploaded file format is limited')); + } + //验证文件后缀 + if ($this->config['mimetype'] === '*' + || in_array($this->fileInfo['suffix'], $mimetypeArr) || in_array('.' . $this->fileInfo['suffix'], $mimetypeArr) + || in_array($typeArr[0] . "/*", $mimetypeArr) || (in_array($this->fileInfo['type'], $mimetypeArr) && stripos($this->fileInfo['type'], '/') !== false)) { + return true; + } + throw new UploadException(__('Uploaded file format is limited')); + } + + /** + * 检测是否图片 + * @param bool $force + * @return bool + * @throws UploadException + */ + protected function checkImage($force = false) + { + //验证是否为图片文件 + if (in_array($this->fileInfo['type'], ['image/gif', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/png', 'image/webp']) || in_array($this->fileInfo['suffix'], ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'webp'])) { + $imgInfo = getimagesize($this->fileInfo['tmp_name']); + if (!$imgInfo || !isset($imgInfo[0]) || !isset($imgInfo[1])) { + throw new UploadException(__('Uploaded file is not a valid image')); + } + $this->fileInfo['imagewidth'] = isset($imgInfo[0]) ? $imgInfo[0] : 0; + $this->fileInfo['imageheight'] = isset($imgInfo[1]) ? $imgInfo[1] : 0; + return true; + } else { + return !$force; + } + } + + /** + * 检测文件大小 + * @throws UploadException + */ + protected function checkSize() + { + preg_match('/([0-9\.]+)(\w+)/', $this->config['maxsize'], $matches); + $size = $matches ? $matches[1] : $this->config['maxsize']; + $type = $matches ? strtolower($matches[2]) : 'b'; + $typeDict = ['b' => 0, 'k' => 1, 'kb' => 1, 'm' => 2, 'mb' => 2, 'gb' => 3, 'g' => 3]; + $size = (int)($size * pow(1024, isset($typeDict[$type]) ? $typeDict[$type] : 0)); + if ($this->fileInfo['size'] > $size) { + throw new UploadException(__('File is too big (%sMiB), Max filesize: %sMiB.', + round($this->fileInfo['size'] / pow(1024, 2), 2), + round($size / pow(1024, 2), 2))); + } + } + + /** + * 获取后缀 + * @return string + */ + public function getSuffix() + { + return $this->fileInfo['suffix'] ?: 'file'; + } + + /** + * 获取存储的文件名 + * @param string $savekey 保存路径 + * @param string $filename 文件名 + * @param string $md5 文件MD5 + * @param string $category 分类 + * @return mixed|null + */ + public function getSavekey($savekey = null, $filename = null, $md5 = null, $category = null) + { + if ($filename) { + $suffix = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); + } else { + $suffix = $this->fileInfo['suffix'] ?? ''; + } + $suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file'; + $filename = $filename ? $filename : ($this->fileInfo['name'] ?? 'unknown'); + $filename = xss_clean(strip_tags(htmlspecialchars($filename))); + $fileprefix = substr($filename, 0, strripos($filename, '.')); + $md5 = $md5 ? $md5 : (isset($this->fileInfo['tmp_name']) ? md5_file($this->fileInfo['tmp_name']) : ''); + $category = $category ? $category : request()->post('category'); + $category = $category ? xss_clean($category) : 'all'; + $replaceArr = [ + '{year}' => date("Y"), + '{mon}' => date("m"), + '{day}' => date("d"), + '{hour}' => date("H"), + '{min}' => date("i"), + '{sec}' => date("s"), + '{random}' => Random::alnum(16), + '{random32}' => Random::alnum(32), + '{category}' => $category ? $category : '', + '{filename}' => substr($filename, 0, 100), + '{fileprefix}' => substr($fileprefix, 0, 100), + '{suffix}' => $suffix, + '{.suffix}' => $suffix ? '.' . $suffix : '', + '{filemd5}' => $md5, + ]; + $savekey = $savekey ? $savekey : $this->config['savekey']; + $savekey = str_replace(array_keys($replaceArr), array_values($replaceArr), $savekey); + + return $savekey; + } + + /** + * 清理分片文件 + * @param $chunkid + */ + public function clean($chunkid) + { + if (!preg_match('/^[a-z0-9\-]{36}$/', $chunkid)) { + throw new UploadException(__('Invalid parameters')); + } + $iterator = new \GlobIterator($this->chunkDir . DS . $chunkid . '-*', FilesystemIterator::KEY_AS_FILENAME); + $array = iterator_to_array($iterator); + foreach ($array as $index => &$item) { + $sourceFile = $item->getRealPath() ?: $item->getPathname(); + $item = null; + @unlink($sourceFile); + } + } + + /** + * 合并分片文件 + * @param string $chunkid + * @param int $chunkcount + * @param string $filename + * @return attachment|\think\Model + * @throws UploadException + */ + public function merge($chunkid, $chunkcount, $filename) + { + // var_dump(555);exit; + if (!preg_match('/^[a-z0-9\-]{36}$/', $chunkid)) { + throw new UploadException(__('Invalid parameters')); + } + + $filePath = $this->chunkDir . DS . $chunkid; + + $completed = true; + //检查所有分片是否都存在 + for ($i = 0; $i < $chunkcount; $i++) { + if (!file_exists("{$filePath}-{$i}.part")) { + $completed = false; + break; + } + } + if (!$completed) { + $this->clean($chunkid); + throw new UploadException(__('Chunk file info error')); + } + + //如果所有文件分片都上传完毕,开始合并 + $uploadPath = $filePath; + + if (!$destFile = @fopen($uploadPath, "wb")) { + $this->clean($chunkid); + throw new UploadException(__('Chunk file merge error')); + } + if (flock($destFile, LOCK_EX)) { // 进行排他型锁定 + for ($i = 0; $i < $chunkcount; $i++) { + $partFile = "{$filePath}-{$i}.part"; + if (!$handle = @fopen($partFile, "rb")) { + break; + } + while ($buff = fread($handle, filesize($partFile))) { + fwrite($destFile, $buff); + } + @fclose($handle); + @unlink($partFile); //删除分片 + } + + flock($destFile, LOCK_UN); + } + @fclose($destFile); + + $attachment = null; + try { + $file = new File($uploadPath); + $info = [ + 'name' => $filename, + 'type' => $file->getMime(), + 'tmp_name' => $uploadPath, + 'error' => 0, + 'size' => $file->getSize() + ]; + $file->setSaveName($filename)->setUploadInfo($info); + $file->isTest(true); + + //重新设置文件 + $this->setFile($file); + + unset($file); + $this->merging = true; + + //允许大文件 + $this->config['maxsize'] = "1024G"; + + $attachment = $this->upload(); + } catch (\Exception $e) { + @unlink($destFile); + throw new UploadException($e->getMessage()); + } + return $attachment; + } + + /** + * 分片上传 + * @throws UploadException + */ + public function chunk($chunkid, $chunkindex, $chunkcount, $chunkfilesize = null, $chunkfilename = null, $direct = false) + { + + if ($this->fileInfo['type'] != 'application/octet-stream') { + throw new UploadException(__('Uploaded file format is limited')); + } + + if (!preg_match('/^[a-z0-9\-]{36}$/', $chunkid)) { + throw new UploadException(__('Invalid parameters')); + } + + $destDir = RUNTIME_PATH . 'chunks'; + $fileName = $chunkid . "-" . $chunkindex . '.part'; + $destFile = $destDir . DS . $fileName; + if (!is_dir($destDir)) { + @mkdir($destDir, 0755, true); + } + if (!move_uploaded_file($this->file->getPathname(), $destFile)) { + throw new UploadException(__('Chunk file write error')); + } + $file = new File($destFile); + $info = [ + 'name' => $fileName, + 'type' => $file->getMime(), + 'tmp_name' => $destFile, + 'error' => 0, + 'size' => $file->getSize() + ]; + $file->setSaveName($fileName)->setUploadInfo($info); + $this->setFile($file); + return $file; + } + + /** + * 普通上传 + * @return \app\common\model\attachment|\think\Model + * @throws UploadException + */ + public function upload($savekey = null) + { + + if (empty($this->file)) { + throw new UploadException(__('No file upload or server upload limit exceeded')); + } + + $this->checkSize(); + $this->checkExecutable(); + + $this->checkMimetype(); + // var_dump(1223);exit; + $this->checkImage(); + // var_dump(123);exit; + $savekey = $savekey ? $savekey : $this->getSavekey(); + $savekey = '/' . ltrim($savekey, '/'); + $uploadDir = substr($savekey, 0, strripos($savekey, '/') + 1); + $fileName = substr($savekey, strripos($savekey, '/') + 1); + + $destDir = ROOT_PATH . 'public' . str_replace('/', DS, $uploadDir); + + $sha1 = $this->file->hash(); + + //如果是合并文件 + if ($this->merging) { + if (!$this->file->check()) { + throw new UploadException($this->file->getError()); + } + $destFile = $destDir . $fileName; + $sourceFile = $this->file->getRealPath() ?: $this->file->getPathname(); + $info = $this->file->getInfo(); + $this->file = null; + if (!is_dir($destDir)) { + @mkdir($destDir, 0755, true); + } + rename($sourceFile, $destFile); + $file = new File($destFile); + $file->setSaveName($fileName)->setUploadInfo($info); + } else { + $file = $this->file->move($destDir, $fileName); + if (!$file) { + // 上传失败获取错误信息 + throw new UploadException($this->file->getError()); + } + } + $this->file = $file; + $category = request()->post('category'); + $category = array_key_exists($category, config('site.attachmentcategory') ?? []) ? $category : ''; + $auth = Auth::instance(); + $params = array( + 'admin_id' => (int)session('admin.id'), + 'user_id' => (int)$auth->id, + 'filename' => mb_substr(htmlspecialchars(strip_tags($this->fileInfo['name'])), 0, 100), + 'category' => $category, + 'filesize' => $this->fileInfo['size'], + 'imagewidth' => $this->fileInfo['imagewidth'], + 'imageheight' => $this->fileInfo['imageheight'], + 'imagetype' => $this->fileInfo['suffix'], + 'imageframes' => 0, + 'mimetype' => $this->fileInfo['type'], + 'url' => $uploadDir . $file->getSaveName(), + 'uploadtime' => time(), + 'storage' => 'local', + 'sha1' => $sha1, + 'extparam' => '', + ); + $attachment = new Attachment(); + $attachment->data(array_filter($params)); + $attachment->save(); + + \think\Hook::listen("upload_after", $attachment); + return $attachment; + } + + /** + * 设置错误信息 + * @param $msg + */ + public function setError($msg) + { + $this->error = $msg; + } + + /** + * 获取错误信息 + * @return string + */ + public function getError() + { + return $this->error; + } +} diff --git a/application/common/library/token/Driver.php b/application/common/library/token/Driver.php new file mode 100644 index 0000000..0346302 --- /dev/null +++ b/application/common/library/token/Driver.php @@ -0,0 +1,91 @@ + +// +---------------------------------------------------------------------- + +namespace app\common\library\token; + +/** + * Token基础类 + */ +abstract class Driver +{ + protected $handler = null; + protected $options = []; + + /** + * 存储Token + * @param string $token Token + * @param int $user_id 会员ID + * @param int $expire 过期时长,0表示无限,单位秒 + * @return bool + */ + abstract function set($token, $user_id, $expire = 0); + + /** + * 获取Token内的信息 + * @param string $token + * @return array + */ + abstract function get($token); + + /** + * 判断Token是否可用 + * @param string $token Token + * @param int $user_id 会员ID + * @return boolean + */ + abstract function check($token, $user_id); + + /** + * 删除Token + * @param string $token + * @return boolean + */ + abstract function delete($token); + + /** + * 删除指定用户的所有Token + * @param int $user_id + * @return boolean + */ + abstract function clear($user_id); + + /** + * 返回句柄对象,可执行其它高级方法 + * + * @access public + * @return object + */ + public function handler() + { + return $this->handler; + } + + /** + * 获取加密后的Token + * @param string $token Token标识 + * @return string + */ + protected function getEncryptedToken($token) + { + $config = \think\Config::get('token'); + return hash_hmac($config['hashalgo'], $token, $config['key']); + } + + /** + * 获取过期剩余时长 + * @param $expiretime + * @return float|int|mixed + */ + protected function getExpiredIn($expiretime) + { + return $expiretime ? max(0, $expiretime - time()) : 365 * 86400; + } +} diff --git a/application/common/library/token/driver/Mysql.php b/application/common/library/token/driver/Mysql.php new file mode 100644 index 0000000..379a168 --- /dev/null +++ b/application/common/library/token/driver/Mysql.php @@ -0,0 +1,118 @@ + 'user_token', + 'expire' => 2592000, + 'connection' => [], + ]; + + + /** + * 构造函数 + * @param array $options 参数 + * @access public + */ + public function __construct($options = []) + { + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + if ($this->options['connection']) { + $this->handler = \think\Db::connect($this->options['connection'])->name($this->options['table']); + } else { + $this->handler = \think\Db::name($this->options['table']); + } + $time = time(); + $tokentime = cache('tokentime'); + if (!$tokentime || $tokentime < $time - 86400) { + cache('tokentime', $time); + $this->handler->where('expiretime', '<', $time)->where('expiretime', '>', 0)->delete(); + } + } + + /** + * 存储Token + * @param string $token Token + * @param int $user_id 会员ID + * @param int $expire 过期时长,0表示无限,单位秒 + * @return bool + */ + public function set($token, $user_id, $expire = null) + { + $expiretime = !is_null($expire) && $expire !== 0 ? time() + $expire : 0; + $token = $this->getEncryptedToken($token); + $this->handler->insert(['token' => $token, 'user_id' => $user_id, 'createtime' => time(), 'expiretime' => $expiretime]); + return true; + } + + /** + * 获取Token内的信息 + * @param string $token + * @return array + */ + public function get($token) + { + $data = $this->handler->where('token', $this->getEncryptedToken($token))->find(); + if ($data) { + if (!$data['expiretime'] || $data['expiretime'] > time()) { + //返回未加密的token给客户端使用 + $data['token'] = $token; + //返回剩余有效时间 + $data['expires_in'] = $this->getExpiredIn($data['expiretime']); + return $data; + } else { + self::delete($token); + } + } + return []; + } + + /** + * 判断Token是否可用 + * @param string $token Token + * @param int $user_id 会员ID + * @return boolean + */ + public function check($token, $user_id) + { + $data = $this->get($token); + return $data && $data['user_id'] == $user_id ? true : false; + } + + /** + * 删除Token + * @param string $token + * @return boolean + */ + public function delete($token) + { + $this->handler->where('token', $this->getEncryptedToken($token))->delete(); + return true; + } + + /** + * 删除指定用户的所有Token + * @param int $user_id + * @return boolean + */ + public function clear($user_id) + { + $this->handler->where('user_id', $user_id)->delete(); + return true; + } + +} diff --git a/application/common/library/token/driver/Redis.php b/application/common/library/token/driver/Redis.php new file mode 100644 index 0000000..0aa0caa --- /dev/null +++ b/application/common/library/token/driver/Redis.php @@ -0,0 +1,167 @@ + '127.0.0.1', + 'port' => 6379, + 'password' => '', + 'select' => 0, + 'timeout' => 0, + 'expire' => 0, + 'persistent' => false, + 'userprefix' => 'up:', + 'tokenprefix' => 'tp:', + ]; + + /** + * 构造函数 + * @param array $options 缓存参数 + * @throws \BadFunctionCallException + * @access public + */ + public function __construct($options = []) + { + if (!extension_loaded('redis')) { + throw new \BadFunctionCallException('not support: redis'); + } + if (!empty($options)) { + $this->options = array_merge($this->options, $options); + } + $this->handler = new \Redis; + if ($this->options['persistent']) { + $this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']); + } else { + $this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']); + } + + if ('' != $this->options['password']) { + $this->handler->auth($this->options['password']); + } + + if (0 != $this->options['select']) { + $this->handler->select($this->options['select']); + } + } + + /** + * 获取加密后的Token + * @param string $token Token标识 + * @return string + */ + protected function getEncryptedToken($token) + { + $config = \think\Config::get('token'); + return $this->options['tokenprefix'] . hash_hmac($config['hashalgo'], $token, $config['key']); + } + + /** + * 获取会员的key + * @param $user_id + * @return string + */ + protected function getUserKey($user_id) + { + return $this->options['userprefix'] . $user_id; + } + + /** + * 存储Token + * @param string $token Token + * @param int $user_id 会员ID + * @param int $expire 过期时长,0表示无限,单位秒 + * @return bool + */ + public function set($token, $user_id, $expire = 0) + { + if (is_null($expire)) { + $expire = $this->options['expire']; + } + if ($expire instanceof \DateTime) { + $expire = $expire->getTimestamp() - time(); + } + $key = $this->getEncryptedToken($token); + if ($expire) { + $result = $this->handler->setex($key, $expire, $user_id); + } else { + $result = $this->handler->set($key, $user_id); + } + //写入会员关联的token + $this->handler->sAdd($this->getUserKey($user_id), $key); + return $result; + } + + /** + * 获取Token内的信息 + * @param string $token + * @return array + */ + public function get($token) + { + $key = $this->getEncryptedToken($token); + $value = $this->handler->get($key); + if (is_null($value) || false === $value) { + return []; + } + //获取有效期 + $expire = $this->handler->ttl($key); + $expire = $expire < 0 ? 365 * 86400 : $expire; + $expiretime = time() + $expire; + //解决使用redis方式储存token时api接口Token刷新与检测因expires_in拼写错误报错的BUG + $result = ['token' => $token, 'user_id' => $value, 'expiretime' => $expiretime, 'expires_in' => $expire]; + + return $result; + } + + /** + * 判断Token是否可用 + * @param string $token Token + * @param int $user_id 会员ID + * @return boolean + */ + public function check($token, $user_id) + { + $data = self::get($token); + return $data && $data['user_id'] == $user_id ? true : false; + } + + /** + * 删除Token + * @param string $token + * @return boolean + */ + public function delete($token) + { + $data = $this->get($token); + if ($data) { + $key = $this->getEncryptedToken($token); + $user_id = $data['user_id']; + $this->handler->del($key); + $this->handler->sRem($this->getUserKey($user_id), $key); + } + return true; + + } + + /** + * 删除指定用户的所有Token + * @param int $user_id + * @return boolean + */ + public function clear($user_id) + { + $keys = $this->handler->sMembers($this->getUserKey($user_id)); + $this->handler->del($this->getUserKey($user_id)); + $this->handler->del($keys); + return true; + } + +} diff --git a/application/common/model/Area.php b/application/common/model/Area.php new file mode 100644 index 0000000..43fd593 --- /dev/null +++ b/application/common/model/Area.php @@ -0,0 +1,88 @@ + 'geo:province', 2 => 'geo:city', 3 => 'geo:district']; + $rangearr = [1 => 15000, 2 => 1000, 3 => 200]; + $geoname = isset($namearr[$level]) ? $namearr[$level] : $namearr[3]; + $georange = isset($rangearr[$level]) ? $rangearr[$level] : $rangearr[3]; + // 读取范围内的ID + $redis = Cache::store('redis')->handler(); + $georadiuslist = []; + if (method_exists($redis, 'georadius')) { + $georadiuslist = $redis->georadius($geoname, $lng, $lat, $georange, 'km', ['WITHDIST', 'COUNT' => 5, 'ASC']); + } + + if ($georadiuslist) { + list($id, $distance) = $georadiuslist[0]; + } + $id = isset($id) && $id ? $id : 3; + return self::get($id); + } + + /** + * 根据经纬度获取省份 + * + * @param string $lng 经度 + * @param string $lat 纬度 + * @return Area + */ + public static function getProvinceFromLngLat($lng, $lat) + { + $provincedata = null; + $citydata = self::getCityFromLngLat($lng, $lat); + if ($citydata) { + $provincedata = self::get($citydata['pid']); + } + return $provincedata; + } + + /** + * 根据经纬度获取城市 + * + * @param string $lng 经度 + * @param string $lat 纬度 + * @return Area + */ + public static function getCityFromLngLat($lng, $lat) + { + $citydata = null; + $districtdata = self::getDistrictFromLngLat($lng, $lat); + if ($districtdata) { + $citydata = self::get($districtdata['pid']); + } + return $citydata; + } + + /** + * 根据经纬度获取地区 + * + * @param string $lng 经度 + * @param string $lat 纬度 + * @return Area + */ + public static function getDistrictFromLngLat($lng, $lat) + { + $districtdata = self::getAreaFromLngLat($lng, $lat, 3); + return $districtdata; + } + +} diff --git a/application/common/model/Attachment.php b/application/common/model/Attachment.php new file mode 100644 index 0000000..67f7178 --- /dev/null +++ b/application/common/model/Attachment.php @@ -0,0 +1,98 @@ +where('storage', $model['storage'])->find()) { + return false; + } + }); + self::beforeWrite(function ($row) { + if (isset($row['category']) && $row['category'] == 'unclassed') { + $row['category'] = ''; + } + }); + } + + public function setUploadtimeAttr($value) + { + return is_numeric($value) ? $value : strtotime($value); + } + + public function getCategoryAttr($value) + { + return $value == '' ? 'unclassed' : $value; + } + + public function setCategoryAttr($value) + { + return $value == 'unclassed' ? '' : $value; + } + + /** + * 获取云储存的缩略图样式字符 + */ + public function getThumbStyleAttr($value, $data) + { + if (!isset($data['storage']) || $data['storage'] == 'local') { + return ''; + } else { + $config = get_addon_config($data['storage']); + if ($config && isset($config['thumbstyle'])) { + return $config['thumbstyle']; + } + } + return ''; + } + + /** + * 获取Mimetype列表 + * @return array + */ + public static function getMimetypeList() + { + $data = [ + "image/*" => __("Image"), + "audio/*" => __("Audio"), + "video/*" => __("Video"), + "text/*" => __("Text"), + "application/*" => __("Application"), + "zip,rar,7z,tar" => __("Zip"), + ]; + return $data; + } + + /** + * 获取定义的附件类别列表 + * @return array + */ + public static function getCategoryList() + { + $data = config('site.attachmentcategory') ?? []; + foreach ($data as $index => &$datum) { + $datum = __($datum); + } + $data['unclassed'] = __('Unclassed'); + return $data; + } +} diff --git a/application/common/model/Category.php b/application/common/model/Category.php new file mode 100644 index 0000000..4adfd5d --- /dev/null +++ b/application/common/model/Category.php @@ -0,0 +1,87 @@ +save(['weigh' => $row['id']]); + }); + } + + public function setFlagAttr($value, $data) + { + return is_array($value) ? implode(',', $value) : $value; + } + + /** + * 读取分类类型 + * @return array + */ + public static function getTypeList() + { + $typeList = config('site.categorytype'); + foreach ($typeList as $k => &$v) { + $v = __($v); + } + return $typeList; + } + + public function getTypeTextAttr($value, $data) + { + $value = $value ? $value : $data['type']; + $list = $this->getTypeList(); + return isset($list[$value]) ? $list[$value] : ''; + } + + public function getFlagList() + { + return ['hot' => __('Hot'), 'index' => __('Index'), 'recommend' => __('Recommend')]; + } + + public function getFlagTextAttr($value, $data) + { + $value = $value ? $value : $data['flag']; + $valueArr = explode(',', $value); + $list = $this->getFlagList(); + return implode(',', array_intersect_key($list, array_flip($valueArr))); + } + + /** + * 读取分类列表 + * @param string $type 指定类型 + * @param string $status 指定状态 + * @return array + */ + public static function getCategoryArray($type = null, $status = null) + { + $list = collection(self::where(function ($query) use ($type, $status) { + if (!is_null($type)) { + $query->where('type', '=', $type); + } + if (!is_null($status)) { + $query->where('status', '=', $status); + } + })->order('weigh', 'desc')->select())->toArray(); + return $list; + } +} diff --git a/application/common/model/Club.php b/application/common/model/Club.php new file mode 100644 index 0000000..75db3a3 --- /dev/null +++ b/application/common/model/Club.php @@ -0,0 +1,29 @@ + 'json', + ]; + + /** + * 读取配置类型 + * @return array + */ + public static function getTypeList() + { + $typeList = [ + 'string' => __('String'), + 'password' => __('Password'), + 'text' => __('Text'), + 'editor' => __('Editor'), + 'number' => __('Number'), + 'date' => __('Date'), + 'time' => __('Time'), + 'datetime' => __('Datetime'), + 'datetimerange' => __('Datetimerange'), + 'select' => __('Select'), + 'selects' => __('Selects'), + 'image' => __('Image'), + 'images' => __('Images'), + 'file' => __('File'), + 'files' => __('Files'), + 'switch' => __('Switch'), + 'checkbox' => __('Checkbox'), + 'radio' => __('Radio'), + 'city' => __('City'), + 'selectpage' => __('Selectpage'), + 'selectpages' => __('Selectpages'), + 'array' => __('Array'), + 'custom' => __('Custom'), + ]; + return $typeList; + } + + public static function getRegexList() + { + $regexList = [ + 'required' => '必选', + 'digits' => '数字', + 'letters' => '字母', + 'date' => '日期', + 'time' => '时间', + 'email' => '邮箱', + 'url' => '网址', + 'qq' => 'QQ号', + 'IDcard' => '身份证', + 'tel' => '座机电话', + 'mobile' => '手机号', + 'zipcode' => '邮编', + 'chinese' => '中文', + 'username' => '用户名', + 'password' => '密码' + ]; + return $regexList; + } + + public function getExtendHtmlAttr($value, $data) + { + $result = preg_replace_callback("/\{([a-zA-Z]+)\}/", function ($matches) use ($data) { + if (isset($data[$matches[1]])) { + return $data[$matches[1]]; + } + }, $data['extend']); + return $result; + } + + /** + * 读取分类分组列表 + * @return array + */ + public static function getGroupList() + { + $groupList = config('site.configgroup'); + foreach ($groupList as $k => &$v) { + $v = __($v); + } + return $groupList; + } + + public static function getArrayData($data) + { + if (!isset($data['value'])) { + $result = []; + foreach ($data as $index => $datum) { + $result['field'][$index] = $datum['key']; + $result['value'][$index] = $datum['value']; + } + $data = $result; + } + $fieldarr = $valuearr = []; + $field = isset($data['field']) ? $data['field'] : (isset($data['key']) ? $data['key'] : []); + $value = isset($data['value']) ? $data['value'] : []; + foreach ($field as $m => $n) { + if ($n != '') { + $fieldarr[] = $field[$m]; + $valuearr[] = $value[$m]; + } + } + return $fieldarr ? array_combine($fieldarr, $valuearr) : []; + } + + /** + * 将字符串解析成键值数组 + * @param string $text + * @return array + */ + public static function decode($text, $split = "\r\n") + { + $content = explode($split, $text); + $arr = []; + foreach ($content as $k => $v) { + if (stripos($v, "|") !== false) { + $item = explode('|', $v); + $arr[$item[0]] = $item[1]; + } + } + return $arr; + } + + /** + * 将键值数组转换为字符串 + * @param array $array + * @return string + */ + public static function encode($array, $split = "\r\n") + { + $content = ''; + if ($array && is_array($array)) { + $arr = []; + foreach ($array as $k => $v) { + $arr[] = "{$k}|{$v}"; + } + $content = implode($split, $arr); + } + return $content; + } + + /** + * 本地上传配置信息 + * @return array + */ + public static function upload() + { + $uploadcfg = config('upload'); + + $uploadurl = request()->module() ? $uploadcfg['uploadurl'] : ($uploadcfg['uploadurl'] === 'ajax/upload' ? 'index/' . $uploadcfg['uploadurl'] : $uploadcfg['uploadurl']); + + if (!preg_match("/^((?:[a-z]+:)?\/\/)(.*)/i", $uploadurl) && substr($uploadurl, 0, 1) !== '/') { + $uploadurl = url($uploadurl, '', false); + } + $uploadcfg['fullmode'] = isset($uploadcfg['fullmode']) && $uploadcfg['fullmode'] ? true : false; + $uploadcfg['thumbstyle'] = $uploadcfg['thumbstyle'] ?? ''; + + $upload = [ + 'cdnurl' => $uploadcfg['cdnurl'], + 'uploadurl' => $uploadurl, + 'bucket' => 'local', + 'maxsize' => $uploadcfg['maxsize'], + 'mimetype' => $uploadcfg['mimetype'], + 'chunking' => $uploadcfg['chunking'], + 'chunksize' => $uploadcfg['chunksize'], + 'savekey' => $uploadcfg['savekey'], + 'multipart' => [], + 'multiple' => $uploadcfg['multiple'], + 'fullmode' => $uploadcfg['fullmode'], + 'thumbstyle' => $uploadcfg['thumbstyle'], + 'storage' => 'local' + ]; + return $upload; + } + + /** + * 刷新配置文件 + */ + public static function refreshFile() + { + //如果没有配置权限无法进行修改 + if (!\app\admin\library\Auth::instance()->check('general/config/edit')) { + return false; + } + $config = []; + $configList = self::all(); + foreach ($configList as $k => $v) { + $value = $v->toArray(); + if (in_array($value['type'], ['selects', 'checkbox', 'images', 'files'])) { + $value['value'] = explode(',', $value['value']); + } + if ($value['type'] == 'array') { + $value['value'] = (array)json_decode($value['value'], true); + } + $config[$value['name']] = $value['value']; + } + file_put_contents( + CONF_PATH . 'extra' . DS . 'site.php', + 'belongsTo('Players','player_id', 'id', [], 'LEFT')->setEagerlyType(1); + } +} \ No newline at end of file diff --git a/application/common/model/MatchRanking.php b/application/common/model/MatchRanking.php new file mode 100644 index 0000000..4004e4e --- /dev/null +++ b/application/common/model/MatchRanking.php @@ -0,0 +1,29 @@ +belongsTo('Players','player_id', 'id', [], 'LEFT')->setEagerlyType(1); + } + + public function users() + { + return $this->belongsTo('User','player_id', 'member_number', [], 'LEFT')->setEagerlyType(1); + } +} \ No newline at end of file diff --git a/application/common/model/MatchSchedule.php b/application/common/model/MatchSchedule.php new file mode 100644 index 0000000..e32491f --- /dev/null +++ b/application/common/model/MatchSchedule.php @@ -0,0 +1,19 @@ + "身份证", + 1 => "护照", + 2 => "台胞证", + 3 => "港澳通行证" + ]; + + /** + * @var int + * ASFC 会员 + */ + const ASFC_VIP = 1; + + /** + * @var int + * 非 ASFC 会员 + */ + const NOT_ASFC_VIP = 0; + + /** + * @var int + * 俱乐部会员 + */ + const CLUP_USER = 1; + + /** + * @var int + * 非俱乐部会员 + */ + const NOT_CLUP_USER = 0; + + public function getPlayers(int $member_id) + { + return $this->where('member_id', 'eq', $member_id)->find(); + } + + public function getPlayerId(int $id) + { + return $this->field('id,real_name,age,province,player_pic,member_id')->where('id', 'eq', $id)->find(); + } + +} diff --git a/application/common/model/ScoreLog.php b/application/common/model/ScoreLog.php new file mode 100644 index 0000000..1722f80 --- /dev/null +++ b/application/common/model/ScoreLog.php @@ -0,0 +1,23 @@ + 0, 'mobile' => 0], $value); + return (object)$value; + } + + /** + * 设置验证字段 + * @param mixed $value + * @return string + */ + public function setVerificationAttr($value) + { + $value = is_object($value) || is_array($value) ? json_encode($value) : $value; + return $value; + } + + /** + * 变更会员余额 + * @param int $money 余额 + * @param int $user_id 会员ID + * @param string $memo 备注 + */ + public static function money($money, $user_id, $memo) + { + Db::startTrans(); + try { + $user = self::lock(true)->find($user_id); + if ($user && $money != 0) { + $before = $user->money; + //$after = $user->money + $money; + $after = function_exists('bcadd') ? bcadd($user->money, $money, 2) : $user->money + $money; + //更新会员信息 + $user->save(['money' => $after]); + //写入日志 + MoneyLog::create(['user_id' => $user_id, 'money' => $money, 'before' => $before, 'after' => $after, 'memo' => $memo]); + } + Db::commit(); + } catch (\Exception $e) { + Db::rollback(); + } + } + + /** + * 变更会员积分 + * @param int $score 积分 + * @param int $user_id 会员ID + * @param string $memo 备注 + */ + public static function score($score, $user_id, $memo) + { + Db::startTrans(); + try { + $user = self::lock(true)->find($user_id); + if ($user && $score != 0) { + $before = $user->score; + $after = $user->score + $score; + $level = self::nextlevel($after); + //更新会员信息 + $user->save(['score' => $after, 'level' => $level]); + //写入日志 + ScoreLog::create(['user_id' => $user_id, 'score' => $score, 'before' => $before, 'after' => $after, 'memo' => $memo]); + } + Db::commit(); + } catch (\Exception $e) { + Db::rollback(); + } + } + + /** + * 根据积分获取等级 + * @param int $score 积分 + * @return int + */ + public static function nextlevel($score = 0) + { + $lv = array(1 => 0, 2 => 30, 3 => 100, 4 => 500, 5 => 1000, 6 => 2000, 7 => 3000, 8 => 5000, 9 => 8000, 10 => 10000); + $level = 1; + foreach ($lv as $key => $value) { + if ($score >= $value) { + $level = $key; + } + } + return $level; + } +} diff --git a/application/common/model/UserGroup.php b/application/common/model/UserGroup.php new file mode 100644 index 0000000..4646845 --- /dev/null +++ b/application/common/model/UserGroup.php @@ -0,0 +1,21 @@ +cache('__version__')->order('weigh desc,id desc')->select(); + foreach ($versionlist as $k => $v) { + // 版本正常且新版本号不等于验证的版本号且找到匹配的旧版本 + if ($v['status'] == 'normal' && $v['newversion'] !== $version && \fast\Version::check($version, $v['oldversion'])) { + $updateversion = $v; + break; + } + } + if (isset($updateversion)) { + $search = ['{version}', '{newversion}', '{downloadurl}', '{url}', '{packagesize}']; + $replace = [$version, $updateversion['newversion'], $updateversion['downloadurl'], $updateversion['downloadurl'], $updateversion['packagesize']]; + $upgradetext = str_replace($search, $replace, $updateversion['content']); + return [ + "enforce" => $updateversion['enforce'], + "version" => $version, + "newversion" => $updateversion['newversion'], + "downloadurl" => $updateversion['downloadurl'], + "packagesize" => $updateversion['packagesize'], + "upgradetext" => $upgradetext + ]; + } + return null; + } +} diff --git a/application/common/view/tpl/dispatch_jump - 副本.tpl b/application/common/view/tpl/dispatch_jump - 副本.tpl new file mode 100644 index 0000000..64ed63d --- /dev/null +++ b/application/common/view/tpl/dispatch_jump - 副本.tpl @@ -0,0 +1,63 @@ +{__NOLAYOUT__} + + + + {:__('Warning')} + + + + + +{php}$codeText=$code == 1 ? 'success' : ($code == 0 ? 'error' : 'info');{/php} + +{if $url} + +{/if} + + diff --git a/application/common/view/tpl/dispatch_jump.tpl b/application/common/view/tpl/dispatch_jump.tpl new file mode 100644 index 0000000..64ed63d --- /dev/null +++ b/application/common/view/tpl/dispatch_jump.tpl @@ -0,0 +1,63 @@ +{__NOLAYOUT__} + + + + {:__('Warning')} + + + + + +{php}$codeText=$code == 1 ? 'success' : ($code == 0 ? 'error' : 'info');{/php} +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  {$msg}

                                                                                                                  + {if $url} +

                                                                                                                  + {:__('This page will be re-directed in %s seconds', '' . $wait . '')} +

                                                                                                                  + {/if} +

                                                                                                                  + {:__('Go back')} + {if $url} + {:__('Jump now')} + {/if} +

                                                                                                                  +
                                                                                                                  +{if $url} + +{/if} + + diff --git a/application/common/view/tpl/think_exception.tpl b/application/common/view/tpl/think_exception.tpl new file mode 100644 index 0000000..9414220 --- /dev/null +++ b/application/common/view/tpl/think_exception.tpl @@ -0,0 +1,101 @@ + '发生错误', + 'Home' => '返回主页', + 'Previous Page' => '返回上一页', + 'The page you are looking for is temporarily unavailable' => '你所浏览的页面暂时无法访问', + 'You can return to the previous page and try again' => '你可以返回上一页重试' +]; + +$langSet = ''; + +if (isset($_GET['lang'])) { + $langSet = strtolower($_GET['lang']); +} elseif (isset($_COOKIE['think_var'])) { + $langSet = strtolower($_COOKIE['think_var']); +} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { +preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches); + $langSet = strtolower($matches[1]); +} +$langSet = $langSet && in_array($langSet, ['zh-cn', 'en']) ? $langSet : 'zh-cn'; +$langSet == 'en' && $lang = array_combine(array_keys($lang), array_keys($lang)); + +?> + + + + + <?=$lang['An error occurred']?> + + + + + + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  + +

                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + + +
                                                                                                                  +
                                                                                                                  + + diff --git a/application/config.php b/application/config.php new file mode 100644 index 0000000..3d92683 --- /dev/null +++ b/application/config.php @@ -0,0 +1,304 @@ + +// +---------------------------------------------------------------------- +use think\Env; + +return [ + // +---------------------------------------------------------------------- + // | 应用设置 + // +---------------------------------------------------------------------- + // 应用命名空间 + 'app_namespace' => 'app', + // 应用调试模式 + 'app_debug' => Env::get('app.debug', true), + // 应用Trace + 'app_trace' => Env::get('app.trace', false), + // 应用模式状态 + 'app_status' => '', + // 是否支持多模块 + 'app_multi_module' => true, + // 入口自动绑定模块 + 'auto_bind_module' => false, + // 注册的根命名空间 + 'root_namespace' => [], + // 扩展函数文件 + 'extra_file_list' => [THINK_PATH . 'helper' . EXT], + // 默认输出类型 + 'default_return_type' => 'html', + // 默认AJAX 数据返回格式,可选json xml ... + 'default_ajax_return' => 'json', + // 默认JSONP格式返回的处理方法 + 'default_jsonp_handler' => 'jsonpReturn', + // 默认JSONP处理方法 + 'var_jsonp_handler' => 'callback', + // 默认时区 + 'default_timezone' => 'PRC', + // 是否开启多语言 微信电脑端打开报错问题 + 'lang_switch_on' => false, + // 默认全局过滤方法 用逗号分隔多个 + 'default_filter' => '', + // 默认语言 + 'default_lang' => 'zh-cn', + // 允许的语言列表 + 'allow_lang_list' => ['zh-cn', 'en'], + // 应用类库后缀 + 'class_suffix' => false, + // 控制器类后缀 + 'controller_suffix' => false, + // 获取IP的变量 + 'http_agent_ip' => 'REMOTE_ADDR', + // +---------------------------------------------------------------------- + // | 模块设置 + // +---------------------------------------------------------------------- + // 默认模块名 + 'default_module' => 'index', + // 禁止访问模块 + 'deny_module_list' => ['common', 'admin'], + // 默认控制器名 + 'default_controller' => 'Index', + // 默认操作名 + 'default_action' => 'index', + // 默认验证器 + 'default_validate' => '', + // 默认的空控制器名 + 'empty_controller' => 'Error', + // 操作方法后缀 + 'action_suffix' => '', + // 自动搜索控制器 + 'controller_auto_search' => true, + // +---------------------------------------------------------------------- + // | URL设置 + // +---------------------------------------------------------------------- + // PATHINFO变量名 用于兼容模式 + 'var_pathinfo' => 's', + // 兼容PATH_INFO获取 + 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], + // pathinfo分隔符 + 'pathinfo_depr' => '/', + // URL伪静态后缀 + 'url_html_suffix' => 'html', + // URL普通方式参数 用于自动生成 + 'url_common_param' => false, + // URL参数方式 0 按名称成对解析 1 按顺序解析 + 'url_param_type' => 0, + // 是否开启路由 + 'url_route_on' => true, + // 路由使用完整匹配 + 'route_complete_match' => false, + // 路由配置文件(支持配置多个) + 'route_config_file' => ['route'], + // 是否强制使用路由 + 'url_route_must' => false, + // 域名部署 + 'url_domain_deploy' => false, + // 域名根,如thinkphp.cn + 'url_domain_root' => '', + // 是否自动转换URL中的控制器和操作名 + 'url_convert' => true, + // 默认的访问控制器层 + 'url_controller_layer' => 'controller', + // 表单请求类型伪装变量 + 'var_method' => '_method', + // 表单ajax伪装变量 + 'var_ajax' => '_ajax', + // 表单pjax伪装变量 + 'var_pjax' => '_pjax', + // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 + 'request_cache' => false, + // 请求缓存有效期 + 'request_cache_expire' => null, + // +---------------------------------------------------------------------- + // | 模板设置 + // +---------------------------------------------------------------------- + 'template' => [ + // 模板引擎类型 支持 php think 支持扩展 + 'type' => 'Think', + // 模板路径 + 'view_path' => '', + // 模板后缀 + 'view_suffix' => 'html', + // 模板文件名分隔符 + 'view_depr' => DS, + // 模板引擎普通标签开始标记 + 'tpl_begin' => '{', + // 模板引擎普通标签结束标记 + 'tpl_end' => '}', + // 标签库标签开始标记 + 'taglib_begin' => '{', + // 标签库标签结束标记 + 'taglib_end' => '}', + 'tpl_cache' => true, + ], + // 视图输出字符串内容替换,留空则会自动进行计算 + 'view_replace_str' => [ + '__PUBLIC__' => '', + '__ROOT__' => '', + '__CDN__' => '', + ], + // 默认跳转页面对应的模板文件 + 'dispatch_success_tmpl' => APP_PATH . 'common' . DS . 'view' . DS . 'tpl' . DS . 'dispatch_jump.tpl', + 'dispatch_error_tmpl' => APP_PATH . 'common' . DS . 'view' . DS . 'tpl' . DS . 'dispatch_jump.tpl', + // +---------------------------------------------------------------------- + // | 异常及错误设置 + // +---------------------------------------------------------------------- + // 异常页面的模板文件 + 'exception_tmpl' => APP_PATH . 'common' . DS . 'view' . DS . 'tpl' . DS . 'think_exception.tpl', + // 错误显示信息,非调试模式有效 + 'error_message' => '你所浏览的页面暂时无法访问', + // 显示错误信息 + 'show_error_msg' => false, + // 异常处理handle类 留空使用 \think\exception\Handle + 'exception_handle' => '', + // +---------------------------------------------------------------------- + // | 日志设置 + // +---------------------------------------------------------------------- + 'log' => [ + // 日志记录方式,内置 file socket 支持扩展 + 'type' => 'File', + // 日志保存目录 + 'path' => LOG_PATH, + // 日志记录级别 + 'level' => [], + ], + // +---------------------------------------------------------------------- + // | Trace设置 开启 app_trace 后 有效 + // +---------------------------------------------------------------------- + 'trace' => [ + // 内置Html Console 支持扩展 + 'type' => 'Html', + ], + // +---------------------------------------------------------------------- + // | 缓存设置 + // +---------------------------------------------------------------------- + 'cache' => [ + // 驱动方式 + 'type' => 'Redis', + // 缓存保存目录 + 'path' => CACHE_PATH, + // 缓存前缀 + 'prefix' => '', + // 缓存有效期 0表示永久缓存 + 'expire' => 0, + ], + // +---------------------------------------------------------------------- + // | 会话设置 + // +---------------------------------------------------------------------- + 'session' => [ + 'id' => '', + // SESSION_ID的提交变量,解决flash上传跨域 + 'var_session_id' => '', + // SESSION 前缀 + 'prefix' => 'think', + // 驱动方式 支持redis memcache memcached + 'type' => '', + // 是否自动开启 SESSION + 'auto_start' => true, + //'cache_limiter'=>'' + ], + // +---------------------------------------------------------------------- + // | Cookie设置 + // +---------------------------------------------------------------------- + 'cookie' => [ + // cookie 名称前缀 + 'prefix' => '', + // cookie 保存时间 + 'expire' => 0, + // cookie 保存路径 + 'path' => '/', + // cookie 有效域名 + 'domain' => '.fpvone.cn', + // cookie 启用安全传输 + 'secure' => false, + // httponly设置 + 'httponly' => '', + // 是否使用 setcookie + 'setcookie' => true, + ], + //分页配置 + 'paginate' => [ + 'type' => 'bootstrap', + 'var_page' => 'page', + 'list_rows' => 15, + ], + //验证码配置 + 'captcha' => [ + // 验证码字符集合 + 'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY', + // 验证码字体大小(px) + 'fontSize' => 18, + // 是否画混淆曲线 + 'useCurve' => false, + //使用中文验证码 + 'useZh' => false, + // 验证码图片高度 + 'imageH' => 40, + // 验证码图片宽度 + 'imageW' => 130, + // 验证码位数 + 'length' => 4, + // 验证成功后是否重置 + 'reset' => true + ], + // +---------------------------------------------------------------------- + // | Token设置 + // +---------------------------------------------------------------------- + 'token' => [ + // 驱动方式 + 'type' => 'Mysql', + // 缓存前缀 + 'key' => 'nRU6uwTVHyD3i8AMch4Qf79tjOdYCPzX', + // 加密方式 + 'hashalgo' => 'ripemd160', + // 缓存有效期 0表示永久缓存 + 'expire' => 0, + ], + //FastAdmin配置 + 'fastadmin' => [ + //是否开启前台会员中心 + 'usercenter' => true, + //会员注册验证码类型email/mobile/wechat/text/false + 'user_register_captcha' => 'mobile', + //登录验证码 + 'login_captcha' => true, + //登录失败超过10次则1天后重试 + 'login_failure_retry' => true, + //是否同一账号同一时间只能在一个地方登录 + 'login_unique' => false, + //是否开启IP变动检测 + 'loginip_check' => true, + //登录页默认背景图 + 'login_background' => "", + //是否启用多级菜单导航 + 'multiplenav' => false, + //是否开启多选项卡(仅在开启多级菜单时起作用) + 'multipletab' => true, + //是否默认展示子菜单 + 'show_submenu' => false, + //后台皮肤,为空时表示使用skin-black-blue + 'adminskin' => '', + //后台是否启用面包屑 + 'breadcrumb' => false, + //是否允许未知来源的插件压缩包 + 'unknownsources' => false, + //插件启用禁用时是否备份对应的全局文件 + 'backup_global_files' => true, + //是否开启后台自动日志记录 + 'auto_record_log' => true, + //插件纯净模式,插件启用后是否删除插件目录的application、public和assets文件夹 + 'addon_pure_mode' => true, + //允许跨域的域名,多个以,分隔 + 'cors_request_domain' => '*', + //版本号 + 'version' => '1.3.5.20221214', + //API接口地址 + 'api_url' => 'https://api.fastadmin.net', + ], +]; diff --git a/application/database.php b/application/database.php new file mode 100644 index 0000000..d924e25 --- /dev/null +++ b/application/database.php @@ -0,0 +1,56 @@ + +// +---------------------------------------------------------------------- + +use think\Env; + +return [ + // 数据库类型 + 'type' => Env::get('database.type', 'mysql'), + // 服务器地址 + 'hostname' => Env::get('database.hostname', '127.0.0.1'), + // 数据库名 + 'database' => Env::get('database.database', 'peewee_test'), + // 用户名 + 'username' => Env::get('database.username', 'peewee_test'), + // 密码 + 'password' => Env::get('database.password', 'JEdE5r2FbFetWTde'), + // 端口 + 'hostport' => Env::get('database.hostport', ''), + // 连接dsn + 'dsn' => '', + // 数据库连接参数 + 'params' => [], + // 数据库编码默认采用 utf8mb4 + 'charset' => Env::get('database.charset', 'utf8mb4'), + // 数据库表前缀 + 'prefix' => Env::get('database.prefix', 'peewee_'), + // 数据库调试模式 + 'debug' => Env::get('database.debug', false), + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) + 'deploy' => 0, + // 数据库读写是否分离 主从式有效 + 'rw_separate' => false, + // 读写分离后 主服务器数量 + 'master_num' => 1, + // 指定从服务器序号 + 'slave_no' => '', + // 是否严格检查字段是否存在 + 'fields_strict' => true, + // 数据集返回类型 + 'resultset_type' => 'array', + // 自动写入时间戳字段 + 'auto_timestamp' => false, + // 时间字段取出后的默认时间格式,默认为Y-m-d H:i:s + 'datetime_format' => false, + // 是否需要进行SQL性能分析 + 'sql_explain' => false, +]; diff --git a/application/extra/addons.php b/application/extra/addons.php new file mode 100644 index 0000000..cf446d8 --- /dev/null +++ b/application/extra/addons.php @@ -0,0 +1,100 @@ + false, + 'hooks' => [ + 'app_init' => [ + 'alioss', + 'cms', + 'fastim', + 'qrcode', + 'shopro', + ], + 'module_init' => [ + 'alioss', + 'third', + ], + 'upload_config_init' => [ + 'alioss', + ], + 'upload_delete' => [ + 'alioss', + ], + 'sms_send' => [ + 'alisms', + ], + 'sms_notice' => [ + 'alisms', + ], + 'sms_check' => [ + 'alisms', + ], + 'upgrade' => [ + 'cms', + 'fastim', + 'shopro', + ], + 'view_filter' => [ + 'cms', + 'third', + ], + 'user_sidenav_after' => [ + 'cms', + ], + 'xunsearch_config_init' => [ + 'cms', + 'docs', + ], + 'xunsearch_index_reset' => [ + 'cms', + 'docs', + ], + 'app_dispatch' => [ + 'docs', + ], + 'epay_config_init' => [ + 'epay', + ], + 'addon_action_begin' => [ + 'epay', + ], + 'action_begin' => [ + 'epay', + 'third', + ], + 'config_init' => [ + 'nkeditor', + 'third', + ], + 'user_delete_successed' => [ + 'third', + ], + 'user_logout_successed' => [ + 'third', + ], + ], + 'route' => [ + '/$' => 'cms/index/index', + '/t/[:diyname]$' => 'cms/tag/index', + '/p/[:diyname]$' => 'cms/page/index', + '/s$' => 'cms/search/index', + '/d/[:diyname]$' => 'cms/diyform/index', + '/d/[:diyname]/post' => 'cms/diyform/post', + '/d/[:diyname]/[:id]' => 'cms/diyform/show', + '/special/[:diyname]' => 'cms/special/index', + '/u/[:id]' => 'cms/user/index', + '/[:diyname]$' => 'cms/channel/index', + '/[:catename]/[:id]$' => 'cms/archives/index', + '/docs/api' => 'docs/index/api', + '/docs/[:name]' => 'docs/index/index', + '/qrcode$' => 'qrcode/index/index', + '/qrcode/build$' => 'qrcode/index/build', + '/third$' => 'third/index/index', + '/third/connect/[:platform]' => 'third/index/connect', + '/third/callback/[:platform]' => 'third/index/callback', + '/third/bind/[:platform]' => 'third/index/bind', + '/third/unbind/[:platform]' => 'third/index/unbind', + ], + 'priority' => [], + 'domain' => '', +]; diff --git a/application/extra/chat.php b/application/extra/chat.php new file mode 100644 index 0000000..81d8160 --- /dev/null +++ b/application/extra/chat.php @@ -0,0 +1,22 @@ + [ + 'shop' => [ + 'room_id' => 'admin', + ], + ], + 'basic' => [ + 'allocate' => 'busy', + 'auto_customer_service' => '1', + 'last_customer_service' => '1', + ], + 'system' => [ + 'inside_host' => '127.0.0.1', + 'inside_port' => '9292', + 'port' => '2222', + 'ssl' => 'reverse_proxy', + 'ssl_cert' => '', + 'ssl_key' => '', + ], +]; \ No newline at end of file diff --git a/application/extra/queue.php b/application/extra/queue.php new file mode 100644 index 0000000..604fbb5 --- /dev/null +++ b/application/extra/queue.php @@ -0,0 +1,10 @@ + 'redis', // 队列驱动使用 redis 推荐, 可选 database + 'host' => '127.0.0.1', // redis 主机地址 + 'password' => '', // redis 密码 + 'port' => 6379, // redis 端口 + 'select' => 1, // redis db 库, 建议显示指定 1-15 的数字均可,如果缓存驱动是 redis,避免和缓存驱动 select 冲突 + 'timeout' => 0, // redis 超时时间 + 'persistent' => false, // redis 持续性,连接复用 +]; \ No newline at end of file diff --git a/application/extra/redis.php b/application/extra/redis.php new file mode 100644 index 0000000..64a1b29 --- /dev/null +++ b/application/extra/redis.php @@ -0,0 +1,10 @@ + '127.0.0.1', + 'port' => '6379', + 'select' => '1', + 'timeout' => '0', + 'persistent' => false, + 'password' => '', +]; \ No newline at end of file diff --git a/application/extra/site.php b/application/extra/site.php new file mode 100644 index 0000000..0c3d9ce --- /dev/null +++ b/application/extra/site.php @@ -0,0 +1,44 @@ + '比翼飞行网', + 'beian' => '浙ICP备2021002605号-3', + 'cdnurl' => '', + 'version' => '1.0.45', + 'timezone' => 'Asia/Shanghai', + 'forbiddenip' => '', + 'languages' => + array ( + 'backend' => 'zh-cn', + 'frontend' => 'zh-cn', + ), + 'fixedpage' => 'dashboard', + 'categorytype' => + array ( + 'default' => 'Default', + 'page' => 'Page', + 'article' => 'Article', + 'test' => 'Test', + ), + 'configgroup' => + array ( + 'basic' => 'Basic', + 'email' => 'Email', + 'dictionary' => 'Dictionary', + 'user' => 'User', + 'example' => 'Example', + ), + 'mail_type' => '1', + 'mail_smtp_host' => 'smtp.qq.com', + 'mail_smtp_port' => '465', + 'mail_smtp_user' => '10000', + 'mail_smtp_pass' => 'password', + 'mail_verify_type' => '2', + 'mail_from' => '10000@qq.com', + 'attachmentcategory' => + array ( + 'category1' => 'Category1', + 'category2' => 'Category2', + 'custom' => 'Custom', + ), +); diff --git a/application/extra/upload.php b/application/extra/upload.php new file mode 100644 index 0000000..17da522 --- /dev/null +++ b/application/extra/upload.php @@ -0,0 +1,45 @@ + 'ajax/upload', + /** + * CDN地址 + */ + 'cdnurl' => '', + /** + * 文件保存格式 + */ + 'savekey' => '/uploads/{year}{mon}{day}/{filemd5}{.suffix}', + /** + * 最大可上传大小 + */ + 'maxsize' => '20mb', + /** + * 可上传的文件类型 + */ + 'mimetype' => 'jpg,png,bmp,jpeg,gif,webp,zip,rar,wav,mp4,mp3,webm,docx,pdf,pem,crt,xlsx,xls,pem', + /** + * 是否支持批量上传 + */ + 'multiple' => false, + /** + * 是否支持分片上传 + */ + 'chunking' => false, + /** + * 默认分片大小 + */ + 'chunksize' => 2097152, + /** + * 完整URL模式 + */ + 'fullmode' => false, + /** + * 缩略图样式 + */ + 'thumbstyle' => '', +]; diff --git a/application/index/controller/Ajax.php b/application/index/controller/Ajax.php new file mode 100644 index 0000000..967f907 --- /dev/null +++ b/application/index/controller/Ajax.php @@ -0,0 +1,65 @@ +request->get(['callback' => 'define']); + $header = ['Content-Type' => 'application/javascript']; + if (!config('app_debug')) { + $offset = 30 * 60 * 60 * 24; // 缓存一个月 + $header['Cache-Control'] = 'public'; + $header['Pragma'] = 'cache'; + $header['Expires'] = gmdate("D, d M Y H:i:s", time() + $offset) . " GMT"; + } + + $controllername = input("controllername"); + $this->loadlang($controllername); + //强制输出JSON Object + return jsonp(Lang::get(), 200, $header, ['json_encode_param' => JSON_FORCE_OBJECT | JSON_UNESCAPED_UNICODE]); + } + + /** + * 生成后缀图标 + */ + public function icon() + { + $suffix = $this->request->request("suffix"); + $suffix = $suffix ? $suffix : "FILE"; + $data = build_suffix_image($suffix); + $header = ['Content-Type' => 'image/svg+xml']; + $offset = 30 * 60 * 60 * 24; // 缓存一个月 + $header['Cache-Control'] = 'public'; + $header['Pragma'] = 'cache'; + $header['Expires'] = gmdate("D, d M Y H:i:s", time() + $offset) . " GMT"; + $response = Response::create($data, '', 200, $header); + return $response; + } + + /** + * 上传文件 + */ + public function upload() + { + return action('api/common/upload'); + } + +} diff --git a/application/index/controller/Archives.php b/application/index/controller/Archives.php new file mode 100644 index 0000000..00ffb31 --- /dev/null +++ b/application/index/controller/Archives.php @@ -0,0 +1,980 @@ +model = new \app\admin\model\cms\Archives; + $this->MatchContestantModel = model('MatchContestant'); + $this->UserModel = model('User'); + $config = get_addon_config('cms'); + if ($config['archivesdatalimit'] != 'all') { + $this->dataLimit = $config['archivesdatalimit']; + } + + //复制/加入专题/修改标签均检测编辑权限 + if (in_array($this->request->action(), ['copy', 'special', 'tags', 'move', 'flag']) && !$this->auth->check('cms/archives/edit')) { + Hook::listen('admin_nopermission', $this); + $this->error(__('You have no permission'), ''); + } + + //是否超级管理员 + $this->isSuperAdmin = $this->auth->isSuperAdmin(); + $channelList = []; + $disabledIds = []; + $all = collection(Channel::order("weigh desc,id desc")->select())->toArray(); + + //允许的栏目 + $this->channelIds = $this->isSuperAdmin || !$config['channelallocate'] ? Channel::column('id') : ChannelAdmin::getAdminChanneIds(); + $parentChannelIds = Channel::where('id', 'in', $this->channelIds)->column('parent_id'); + $parentChannelIds = array_unique($parentChannelIds); + $parentChannelList = \think\Db::name('cms_channel')->where('id', 'in', $parentChannelIds)->where('parent_id', '<>', 0)->field('id,parent_id,name')->select(); + $tree = Tree::instance()->init($all, 'parent_id'); + foreach ($parentChannelList as $index => $channel) { + $parentChannelIds = array_merge($parentChannelIds, $tree->getParentsIds($channel['parent_id'], true)); + } + $this->channelIds = array_merge($parentChannelIds, $this->channelIds); + foreach ($all as $k => $v) { + $state = ['opened' => true]; + if ($v['type'] == 'link') { + $disabledIds[] = $v['id']; + } + if ($v['type'] == 'link') { + $state['checkbox_disabled'] = true; + } + if (!$this->isSuperAdmin) { + if (!in_array($v['id'], $parentChannelIds) && !in_array($v['id'], $this->channelIds)) { + unset($all[$k]); + continue; + } + } + $channelList[] = [ + 'id' => $v['id'], + 'parent' => $v['parent_id'] ? $v['parent_id'] : '#', + 'text' => __($v['name']), + 'type' => $v['type'], + 'state' => $state + ]; + } + $tree = Tree::instance()->init($all, 'parent_id'); + $channelOptions = $tree->getTree(0, "", '', $disabledIds); + $secondChannelOptions = $tree->getTree(0, "", '', $disabledIds); + $this->view->assign('channelOptions', $channelOptions); + $this->view->assign('secondChannelOptions', $secondChannelOptions); + $this->assignconfig('channelList', $channelList); + $this->assignconfig('spiderRecord', intval($config['spiderrecord'] ?? 0)); + + $this->assignconfig("flagList", $this->model->getFlagList()); + $this->view->assign("flagList", $this->model->getFlagList()); + $this->view->assign("statusList", $this->model->getStatusList()); + + $this->assignconfig('cms', ['archiveseditmode' => $config['archiveseditmode']]); + } + + /** + * 查看 + */ + public function index() + { + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + $this->relationSearch = true; + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + if (!$this->auth->isSuperAdmin()) { + $this->model->where('channel_id', 'in', $this->channelIds); + } + $total = $this->model + ->with('Channel') + ->where($where) + ->order($sort, $order) + ->count(); + if (!$this->auth->isSuperAdmin()) { + $this->model->where('channel_id', 'in', $this->channelIds); + } + $list = $this->model + ->with(['Channel']) + ->where($where) + ->order($sort, $order) + ->limit($offset, $limit) + ->select(); + + addtion($list, [ + [ + 'field' => 'channel_ids', + 'display' => 'channel_ids', + 'model' => Channel::class, + ], + ]); + \app\admin\model\cms\SpiderLog::render($list, 'archives'); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + + $modelList = \app\admin\model\cms\Modelx::all(); + $specialList = \app\admin\model\cms\Special::where('status', 'normal')->select(); + $this->view->assign('modelList', $modelList); + $this->view->assign('specialList', $specialList); + return $this->view->fetch(); + } + + /** + * 副表内容 + */ + public function content($model_id = null) + { + $model = \app\admin\model\cms\Modelx::get($model_id); + if (!$model) { + $this->error('未找到对应模型'); + } + $fieldsList = \app\admin\model\cms\Fields::where('source', 'model')->where('source_id', $model['id'])->where('type', '<>', 'text')->select(); + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + $fields = []; + foreach ($fieldsList as $index => $item) { + $fields[] = "addon." . $item['name']; + } + $filter = $this->request->request('filter'); + $op = $this->request->request('op'); + if ($filter && $op) { + $filterArr = json_decode($filter, true); + $opArr = json_decode($op, true); + foreach ($filterArr as $index => $item) { + if (in_array("addon." . $index, $fields)) { + $filterArr["addon." . $index] = $item; + $opArr["addon." . $index] = $opArr[$index]; + unset($filterArr[$index], $opArr[$index]); + } + } + $this->request->get(['filter' => json_encode($filterArr), 'op' => json_encode($opArr)]); + } + + $this->searchFields = "archives.id,archives.title"; + $this->relationSearch = true; + $table = $this->model->getTable(); + list($where, $sort, $order, $offset, $limit, $page, $alias) = $this->buildparams(); + $sort = 'archives.id'; + $isSuperAdmin = $this->isSuperAdmin; + $channelIds = $this->channelIds; + $customWhere = function ($query) use ($isSuperAdmin, $channelIds, $model_id) { + if (!$isSuperAdmin) { + $query->where('archives.channel_id', 'in', $channelIds); + } + if ($model_id) { + $query->where('archives.model_id', $model_id); + if ($model_id == 2){ + $res = $query->where('archives.channel_id', 36); + // var_dump($res);exit; + } + // if ($model_id == 6){ + + // $res = $query->where('archives.channel_id', 73); + // // var_dump($res);exit; + // } + } + }; + + + + $list = $this->model + ->alias($alias) + ->alias('archives') + ->join('cms_channel channel', 'channel.id=archives.channel_id', 'LEFT') + ->join($model['table'] . ' addon', 'addon.id=archives.id', 'LEFT') + ->field('archives.*,channel.name as channel_name,addon.id as aid' . ($fields ? ',' . implode(',', $fields) : '')) + ->where($customWhere) + ->whereNull('deletetime') + ->where($where) + ->order($sort, $order) + ->paginate($limit); + // var_dump($list);exit; + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + $fields = []; + foreach ($fieldsList as $index => $item) { + $fields[] = ['field' => $item['name'], 'title' => $item['title'], 'type' => $item['type'], 'content' => $item['content_list']]; + } + + $this->assignconfig('fields', $fields); + $this->view->assign('fieldsList', $fieldsList); + $this->view->assign('model', $model); + $this->assignconfig('model_id', $model_id); + $modelList = \app\admin\model\cms\Modelx::all(); + $this->view->assign('modelList', $modelList); + return $this->view->fetch(); + } + + /** + * 编辑 + * + * @param mixed $ids + * @return string + */ + 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->isSuperAdmin && !in_array($row['channel_id'], $this->channelIds)) { + $this->error(__('You have no permission')); + } + if ($this->request->isPost()) { + return parent::edit($ids); + } + $channel = Channel::get($row['channel_id']); + if (!$channel) { + $this->error(__('No specified channel found')); + } + $model = \app\admin\model\cms\Modelx::get($channel['model_id']); + if (!$model) { + $this->error(__('No specified model found')); + } + $addon = db($model['table'])->where('id', $row['id'])->find(); + if ($addon) { + $row->setData($addon); + } + + $disabledIds = []; + $all = collection(Channel::order("weigh desc,id desc")->select())->toArray(); + foreach ($all as $k => $v) { + if ($v['type'] == 'link' || $v['model_id'] != $channel['model_id']) { + $disabledIds[] = $v['id']; + } + } + $disabledIds = array_diff($disabledIds, [$row['channel_id']]); + $tree = Tree::instance()->init($all, 'parent_id'); + $channelOptions = $tree->getTree(0, "", $row['channel_id'], $disabledIds); + $secondChannelOptions = $tree->getTree(0, "", explode(',', $row['channel_ids']), $disabledIds); + $this->view->assign('channelOptions', $channelOptions); + $this->view->assign('secondChannelOptions', $secondChannelOptions); + $this->view->assign("row", $row); + return $this->view->fetch(); + } + + /** + * 删除 + * @param mixed $ids + */ + public function del($ids = "") + { + parent::del($ids); + } + + /** + * 销毁 + * @param string $ids + */ + public function destroy($ids = "") + { + \app\admin\model\cms\Archives::event('after_delete', function ($row) { + //删除副表 + $channel = Channel::get($row->channel_id); + if ($channel) { + $model = Modelx::get($channel['model_id']); + if ($model) { + db($model['table'])->where("id", $row['id'])->delete(); + } + } + }); + parent::destroy($ids); + } + + /** + * 还原 + * @param mixed $ids + */ + public function restore($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + if ($ids) { + $this->model->where($pk, 'in', $ids); + } + $config = get_addon_config('cms'); + $list = $this->model->onlyTrashed()->select(); + if ($list) { + $ids = []; + $refreshIds = []; + foreach ($list as $index => $item) { + if ($item['status'] == 'normal') { + User::score($config['score']['postarchives'], $item['user_id'], '发布文章'); + } + $ids[] = $item['id']; + $refreshIds = array_merge([$item['channel_id']], explode(',', $item['channel_ids'])); + $refreshIds = array_filter(array_unique($refreshIds)); + } + $this->model->where('id', 'in', $ids); + $this->model->restore('1=1'); + Channel::refreshItems($refreshIds); + $this->success(); + } + $this->error(__('No rows were updated')); + } + + /** + * 移动 + * @param string $ids + */ + public function move($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($ids) { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + $channel_id = $this->request->post('channel_id'); + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $this->model->where($pk, 'in', $ids); + $channel = Channel::get($channel_id); + if ($channel && $channel['type'] === 'list') { + $channelNums = \app\admin\model\cms\Archives:: + with('channel') + ->where('archives.' . $pk, 'in', $ids) + ->where('channel_id', '<>', $channel['id']) + ->field('channel_id,COUNT(*) AS nums') + ->group('channel_id') + ->select(); + $result = $this->model + ->where('model_id', '=', $channel['model_id']) + ->where('channel_id', '<>', $channel['id']) + ->update(['channel_id' => $channel_id]); + if ($result) { + $this->success(); + } else { + $this->error(__('No rows were updated')); + } + } else { + $this->error(__('No rows were updated')); + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + } + + /** + * 复制选择行 + * @param string $ids + */ + public function copy($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($ids) { + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $archivesList = $this->model->where('id', 'in', $ids)->select(); + foreach ($archivesList as $index => $item) { + try { + $model = Modelx::get($item['model_id']); + $addon = \think\Db::name($model['table'])->find($item['id']); + $data = $item->toArray(); + $data = array_merge($data, $addon ?? []); + $data['title'] = $data['title'] . "_copy"; + $data['status'] = 'hidden'; + unset($data['id']); + \app\admin\model\cms\Archives::create($data, true); + } catch (\Exception $e) { + // + } + } + $this->success(); + $this->error(__('Parameter %s can not be empty', 'ids')); + } + } + + /** + * 加入专题 + * @param string $ids + */ + public function special($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($ids) { + $special_id = $this->request->post('special_id'); + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $special = \app\admin\model\cms\Special::get($special_id); + if ($special) { + $archivesList = $this->model->where($pk, 'in', $ids)->select(); + foreach ($archivesList as $index => $item) { + $special_ids = explode(',', $item['special_ids']); + if (!in_array($special['id'], $special_ids)) { + $special_ids[] = $special['id']; + $item->save(['special_ids' => implode(',', array_unique(array_filter($special_ids)))]); + } + } + $this->success(); + } else { + $this->error(__('No rows were updated')); + } + } + $this->error(__('Please select at least one row')); + } + + /** + * 加入标签 + * @param string $ids + */ + public function tags($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($ids) { + $tags = $this->request->post('tags'); + $newTagsArr = array_filter(explode(',', $tags)); + if ($newTagsArr) { + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $archivesList = $this->model->where($pk, 'in', $ids)->select(); + foreach ($archivesList as $index => $item) { + $tagsArr = explode(',', $item['tags']); + $tagsArr = array_merge($tagsArr, $newTagsArr); + $item->save(['tags' => implode(',', array_unique(array_filter($tagsArr)))]); + } + $this->success(); + } else { + $this->error(__('标签数据不能为空')); + } + } + $this->error(__('Please select at least one row')); + } + + /** + * 修改标志 + * @param string $ids + */ + public function flag($ids = "") + { + if (!$this->request->isPost()) { + $this->error(__("Invalid parameters")); + } + if ($ids) { + $type = $this->request->post('type'); + $flag = $this->request->post('flag'); + $changeFlagArr = array_filter(explode(',', $flag)); + if ($changeFlagArr) { + $pk = $this->model->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->model->where($this->dataLimitField, 'in', $adminIds); + } + $archivesList = $this->model->where($pk, 'in', $ids)->select(); + foreach ($archivesList as $index => $item) { + $flagArr = explode(',', $item['flag']); + if ($type == 'add') { + $flagArr = array_merge($flagArr, $changeFlagArr); + } else { + $flagArr = array_diff($flagArr, $changeFlagArr); + } + $item->save(['flag' => implode(',', array_unique(array_filter($flagArr)))]); + } + $this->success(); + } else { + $this->error(__('标志数据不能为空')); + } + } + $this->error(__('Please select at least one row')); + } + + /** + * 获取栏目列表 + * @internal + */ + public function get_fields_html() + { + $this->view->engine->layout(false); + $channel_id = $this->request->post('channel_id'); + $archives_id = $this->request->post('archives_id'); + $channel = Channel::get($channel_id, 'model'); + if ($channel) { + $model_id = $channel['model_id']; + $values = []; + if ($archives_id) { + $values = db($channel['model']['table'])->where('id', $archives_id)->find(); + + //优先从栏目获取模型ID,再从文档获取 + $archives = \app\admin\model\cms\Archives::get($archives_id); + $model_id = $archives ? $archives['model_id'] : $model_id; + } + + $fields = \addons\cms\library\Service::getCustomFields('model', $model_id, $values); + + $model = Modelx::get($model_id); + + $setting = $model['setting']; + $publishfields = isset($setting['publishfields']) ? $setting['publishfields'] : []; + $titlelist = isset($setting['titlelist']) ? $setting['titlelist'] : []; + + $this->view->assign('channel', $channel); + $this->view->assign('fields', $fields); + $this->view->assign('values', $values); + $this->success('', null, ['html' => $this->view->fetch('cms/common/fields'), 'publishfields' => $publishfields, 'titlelist' => $titlelist]); + } else { + $this->error(__('Please select channel')); + } + $this->error(__('Parameter %s can not be empty', 'ids')); + } + + /** + * 检测元素是否可用 + * @internal + */ + public function check_element_available() + { + $id = $this->request->request('id'); + $name = $this->request->request('name'); + $value = $this->request->request('value'); + $name = substr($name, 4, -1); + if (!$name) { + $this->error(__('Parameter %s can not be empty', 'name')); + } + if ($id) { + $this->model->where('id', '<>', $id); + } + $exist = $this->model->where($name, $value)->find(); + if ($exist) { + $this->error(__('The data already exist')); + } else { + $this->success(); + } + } + + /** + * 搜索建议 + * @internal + */ + public function suggestion() + { + $config = get_addon_config('cms'); + $q = trim($this->request->request("q")); + $id = trim($this->request->request("id/d")); + $list = []; + if ($config['searchtype'] == 'xunsearch') { + $result = FulltextSearch::search($q, 1, 10); + } else { + $result = $this->model->where("title|keywords|description", "like", "%{$q}%")->where('id', '<>', $id)->limit(10)->order("id", "desc")->select(); + foreach ($result as $index => $item) { + $item['image'] = $item['image'] ? $item['image'] : '/assets/addons/cms/img/noimage.png'; + $list[] = ['id' => $item['id'], 'url' => $item['fullurl'], 'image' => cdnurl($item['image']), 'title' => $item['title'], 'create_date' => datetime($item['createtime']), 'status' => $item['status'], 'status_text' => $item['status_text'], 'deletetime' => $item['deletetime']]; + } + } + return json($list); + } + + /** + * Created by PhpStorm. + * Author:Soar + * Time:2023/7/12 11:54 + * @return string + * @throws \think\Exception + * @DESC 赛事页面飞手列表 + */ + public function league_allplayer() + { + return $this->view->fetch(); + } + public function league_wholeplayer() + { + return $this->view->fetch(); + } + public function leaguecontestant() + { + return $this->view->fetch(); + } + public function contestant() + { + return $this->view->fetch(); + } + + public function club_match_list() + { + return $this->view->fetch(); + } + + public function uploadfile() + { + return $this->view->fetch(); + } + + public function test() + { + return $this->view->fetch(); + } + + public function course() + { + return $this->view->fetch(); + } + + public function visualization() + { + return $this->view->fetch(); + } + + public function league(){ + return $this->view->fetch(); + } + + public function league_course(){ + return $this->view->fetch(); + } + + public function getleagueList() + { + $ClubMatchApply = new ClubMatchApply(); + $club = new Club(); + // $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + // 拦截部分信息修改 + if (!empty($this->request->get("op", '', 'trim'))) { + $op = json_decode($this->request->get("op", '', 'trim'), true); + $filter = json_decode($this->request->get("filter", '', 'trim'), true); + // var_dump($filter);exit; + if (!empty($filter['user.member_number'])) { + // 获取用户信息 + $players = $this->UserModel->where('member_number', $filter['user.member_number'])->find(); + // var_dump($players);exit; + if (!empty($players)) { + unset($op['user.member_number']); + unset($filter['user.member_number']); + $filter['players.member_id'] = $players->id; + $op['players.member_id'] = "LIKE"; + $this->request->get(["op" => json_encode($op)])["op"] = json_encode($op); + $this->request->get(["filter" => json_encode($filter)]); + + } + } + } + + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + // $res = $this->buildparams(); + // var_dump($res);exit; + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $matchId = trim($this->request->request("ids/d")); + + // $list = $ClubMatchApply->where($where) + // ->where('match_id', 'eq', $matchId) + // ->order($sort, $order) + // ->paginate($limit); + // var_dump($order);exit; + $club_list = $this->MatchContestantModel + // ->with('clubs') + ->alias('a') + // ->join('club club','a.club_id = club.id') + ->where($where) + ->where('match_id', 'eq', $matchId) + ->order($sort, $order) + ->group('a.club_id') + // ->fetchSql(true) + ->paginate($limit); + // var_dump($club_list);exit; + // var_dump($list->toArray()['data']);exit; + // $lists = $list->toArray()['data']; + foreach ($club_list as &$val){ + + + $val->club = $club->where('id',$val['club_id'])->find(); + // if(!empty($res)) $res = $res->toArray(); + // $val['clubs'] = $res; + } + // $a = $club_list->items(); + // foreach ($a as &$val){ + // $val = $val->toArray(); + // } + // var_dump($a);exit; + $result = array("total" => $club_list->total(), "rows" => $club_list->items()); + + return json($result); + } + + } + + public function getmatch_wholeList() + { + $ClubMatchApply = new ClubMatchApply(); + $club = new Club(); + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + // 拦截部分信息修改 + if (!empty($this->request->get("op", '', 'trim'))) { + $op = json_decode($this->request->get("op", '', 'trim'), true); + $filter = json_decode($this->request->get("filter", '', 'trim'), true); + // var_dump($filter);exit; + if (!empty($filter['user.member_number'])) { + // 获取用户信息 + $players = $this->UserModel->where('member_number', $filter['user.member_number'])->find(); + // var_dump($players);exit; + if (!empty($players)) { + unset($op['user.member_number']); + unset($filter['user.member_number']); + $filter['players.member_id'] = $players->id; + $op['players.member_id'] = "LIKE"; + $this->request->get(["op" => json_encode($op)])["op"] = json_encode($op); + $this->request->get(["filter" => json_encode($filter)]); + + } + } + } + + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $ids = trim($this->request->request("ids/d")); + // var_dump($ids);exit; + $matchId = $this->MatchContestantModel + ->field('match_id')->where('match_id', 'eq', $ids)->where('club_status',null)->find(); + // var_dump($matchId);exit; + // $list = $ClubMatchApply->where($where) + // ->where('match_id', 'eq', $matchId) + // ->order($sort, $order) + // ->paginate($limit); + $list = $this->MatchContestantModel + ->with('players') + ->where($where) + ->where('match_id', 'eq', $matchId['match_id']) + ->where('club_status',null) + ->order($sort, $order) + // ->distinct(true) + ->paginate($limit); + // var_dump($list->toArray()['data']);exit; + // $lists = $list->toArray()['data']; + foreach ($list as &$val){ + + if(empty($val->players->member_id)){ + + continue; + } + if(strpos($val->players->player_pic, 'aliyuncs') === false){ + // $val->players->player_pic = 'https://www.fpvone.cn'.$val->players->player_pic; + } + $val->user = $this->UserModel->find($val->players->member_id); + $val->club = $club->where('id',$val['club_id'])->find(); + } + $result = array("total" => $list->total(), "rows" => $list->items()); + // var_dump($result);exit; + return json($result); + } + + } + + public function getmatch_idsList() + { + $ClubMatchApply = new ClubMatchApply(); + $club = new Club(); + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + // 拦截部分信息修改 + if (!empty($this->request->get("op", '', 'trim'))) { + $op = json_decode($this->request->get("op", '', 'trim'), true); + $filter = json_decode($this->request->get("filter", '', 'trim'), true); + // var_dump($filter);exit; + if (!empty($filter['user.member_number'])) { + // 获取用户信息 + $players = $this->UserModel->where('member_number', $filter['user.member_number'])->find(); + // var_dump($players);exit; + if (!empty($players)) { + unset($op['user.member_number']); + unset($filter['user.member_number']); + $filter['players.member_id'] = $players->id; + $op['players.member_id'] = "LIKE"; + $this->request->get(["op" => json_encode($op)])["op"] = json_encode($op); + $this->request->get(["filter" => json_encode($filter)]); + + } + } + } + + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $ids = trim($this->request->request("ids/d")); + // var_dump($ids);exit; + $matchId = $this->MatchContestantModel + ->field('match_id,club_id')->where('id', 'eq', $ids)->find(); + // var_dump($matchId['club_id']);exit; + // $list = $ClubMatchApply->where($where) + // ->where('match_id', 'eq', $matchId) + // ->order($sort, $order) + // ->paginate($limit); + // var_dump($where);exit; + $list = $this->MatchContestantModel + ->with('player') + ->where($where) + ->where('match_id', 'eq', $matchId['match_id']) + ->where('club_id','eq',$matchId['club_id']) + ->where('club_status',null) + ->order($sort, $order) + // ->distinct(true) + ->paginate($limit); + // var_dump($list->toArray()['data']);exit; + // $lists = $list->toArray()['data']; + foreach ($list as &$val){ + + if(empty($val->players->member_id)){ + + continue; + } + $val->user = $this->UserModel->find($val->players->member_id); + $val->club = $club->where('id',$val['club_id'])->find(); + } + $result = array("total" => $list->total(), "rows" => $list->items()); + // var_dump($result);exit; + return json($result); + } + + } + + public function getplayersList() + { + $ClubMatchApply = new ClubMatchApply(); + $club = new Club(); + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + // 拦截部分信息修改 + if (!empty($this->request->get("op", '', 'trim'))) { + $op = json_decode($this->request->get("op", '', 'trim'), true); + $filter = json_decode($this->request->get("filter", '', 'trim'), true); + // var_dump($filter);exit; + if (!empty($filter['user.member_number'])) { + // 获取用户信息 + $players = $this->UserModel->where('member_number', $filter['user.member_number'])->find(); + // var_dump($players);exit; + if (!empty($players)) { + unset($op['user.member_number']); + unset($filter['user.member_number']); + $filter['players.member_id'] = $players->id; + $op['players.member_id'] = "LIKE"; + $this->request->get(["op" => json_encode($op)])["op"] = json_encode($op); + $this->request->get(["filter" => json_encode($filter)]); + + } + } + } + + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + // var_dump($where);exit; + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + $matchId = trim($this->request->request("ids/d")); + // var_dump($where);exit; + // $list = $ClubMatchApply->where($where) + // ->where('match_id', 'eq', $matchId) + // ->order($sort, $order) + // ->paginate($limit); + $list = $this->MatchContestantModel + ->with('players') + ->where($where) + ->where('match_id', 'eq', $matchId) + ->order($sort, $order) + // ->distinct(true) + ->paginate($limit); + // var_dump($list->toArray()['data']);exit; + // $lists = $list->toArray()['data']; + foreach ($list as &$val){ + + if(empty($val->players->member_id)){ + + continue; + } + // var_dump($val['club_id']);exit; + // $club->where('id',$val->club_id); + $val->user = $this->UserModel->find($val->players->member_id); + + $val->club = $club->where('id',$val['club_id'])->find(); + // if(!empty($res)) $res = $res->toArray(); + // $val['clubs'] = $res; + } + $result = array("total" => $list->total(), "rows" => $list->items()); + // var_dump($result);exit; + return json($result); + } + + } + +} diff --git a/application/index/controller/Broadcast.php b/application/index/controller/Broadcast.php new file mode 100644 index 0000000..1bb5213 --- /dev/null +++ b/application/index/controller/Broadcast.php @@ -0,0 +1,122 @@ +rankingService = new RankingService(); + $this->leagueRound = new Leagueround(); + $this->leagueService = new LeagueService(); + } + + public function leageue_course_double_fail(){ + $matchranking = new MatchRanking(); + $match_id = $this->request->param("match_id"); + $course = $this->request->param("course"); // 赛程 + $other_round = $this->request->param("other_round", ''); // 轮次 + // $row = $this->leagueService->getrank_course($match_id,$course,$other_round); + $row = $matchranking->field('id,match_id,player_id,player_name,name_short,channel,led_color,fly_num,times,grouping,achievement,other_round')->where('match_id',$match_id)->where('course',$course)->order('id')->select(); + + foreach($row as &$val){ + $group_name = $this->win_or_lose_group($val['other_round']); + $val['group_name'] = $group_name; + } + if (!empty($row)) { + $res['code'] = 1; + $res['data'] = $row; + // $res['round'] = $round; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['round'] = ''; + $res['msg'] = "获取失败"; + } + + return json($res); + } + + public function win_or_lose_group($other_round){ + switch ($other_round) { + case 1: $which_round = '胜者组';break; + case 2: $which_round = '胜者组';break; + case 3: $which_round = '败者组';break; + case 4: $which_round = '败者组';break; + case 5: $which_round = '败者组';break; + case 6: $which_round = '胜者组';break; + case 7: $which_round = '败者组';break; + case 8: $which_round = '败者组';break; + case 9: $which_round = '胜者组';break; + case 10: $which_round = '败者组';break; + case 11: $which_round = '败者组';break; + case 12: $which_round = '胜者组';break; + case 13: $which_round = '败者组';break; + + } + return $which_round; + } + + + + //队伍总积分排行榜 + public function club_allintegral(){ + $LeagueClubIntegral = new LeagueClubIntegral(); + $all_club = $LeagueClubIntegral->group('club_id')->select(); + $arr = []; + foreach ($all_club as $value){ + $arr[] = $LeagueClubIntegral->field('sum(sum_grade) as all_grade,club_id,club_name')->where('club_id',$value['club_id'])->select(); + + } + return json($arr); + } + + //队伍分站赛积分 + public function club_integral(){ + $LeagueClubIntegral = new LeagueClubIntegral(); + $match_id = $this->request->param('match_id'); + $res = $LeagueClubIntegral->where('match_id',$match_id)->select(); + // var_dump($all_club);exit; + return json($res); + } + + //飞手总积分排行 + public function player_allintegral(){ + $LeagueIntegral = new LeagueIntegral(); + $all_player = $LeagueIntegral->group('player_id')->select(); + $arr = []; + foreach ($all_player as $value){ + $arr[] = $LeagueIntegral->field('sum(grade) as all_grade,player_id,player_name')->where('player_id',$value['player_id'])->select(); + + } + return json($arr); + } + + //飞手分站赛积分 + public function player_integral(){ + $LeagueIntegral = new LeagueIntegral(); + $match_id = $this->request->param('match_id'); + $res = $LeagueIntegral->where('match_id',$match_id)->select(); + // var_dump($all_club);exit; + return json($res); + } + + +} \ No newline at end of file diff --git a/application/index/controller/Certificate b/application/index/controller/Certificate new file mode 100644 index 0000000..e69de29 diff --git a/application/index/controller/Certificate.php b/application/index/controller/Certificate.php new file mode 100644 index 0000000..653c015 --- /dev/null +++ b/application/index/controller/Certificate.php @@ -0,0 +1,41 @@ +request->param("certificate_number"); + + $certificateService = new CertificateService(); + + if ($certificateService->checkCertificate($certificate_number)) { + $certificate_info = $certificateService->getCertificateToNumber($certificate_number); + + $result['code'] = 200; + $result['data'] = $certificate_info; + $result['msg'] = "验证成功"; + + return json($result); + } + + $this->error("验证失败"); + } +} \ No newline at end of file diff --git a/application/index/controller/Club.php b/application/index/controller/Club.php new file mode 100644 index 0000000..3fb37aa --- /dev/null +++ b/application/index/controller/Club.php @@ -0,0 +1,379 @@ +view->assign('title', "飞手公会"); + $this->confirmed_club(); + return $this->view->fetch(); + } + + public function confirmed_club(){ + $club = new ClubModel(); + $user_id = $this->auth->id; + $club_info = $club->where("user_id", $user_id)->find(); + + if (empty($club_info)){ + $this->error(__("请先申请成为队伍!"), url('players/registeredflyers')) ; + } + + if ($club_info['status'] != 9){ + $this->error(__("您还不是正式队伍!"), url('user/index')); + } + } + + public function registerclub(){ + $this->view->assign('title', "认证队伍"); + //获取用户id + $this->view->engine->layout('layout/defaultsRegister'); + $user_id = $this->auth->id; + + if (empty($user_id)){ + $this->error("请先登录!",url('user/login')); + } + $club = new ClubModel(); + $club_info = $club + ->where('user_id', $user_id) + ->find(); + + if (empty($club_info)){ + // $this->view->assign('playerinfo', $club_info); + return $this->view->fetch(); + } + // var_dump($club_info);exit; + $this->view->assign('clubinfo', $club_info); + return $this->view->fetch(); + // return $this->fetch('registerclub'); + } + + //单个赛事下的所有报名飞手 + public function club_allplayer(){ + $user_id = $this->auth->id; + if (empty($user_id)){ + $this->error("请先登录!",url('user/login')); + } + $match_id = $this->request->param('match_id'); + $player = new Players(); + $club_model = new ClubModel(); + $MatchContestant = new MatchContestant(); + // var_dump($match_id);exit; + $club_info = $club_model->field('id,name')->where('user_id',$user_id)->where('status',9)->find(); + if(empty($club_info)){ + $this->error('请先注册俱乐部并通过认证'); + } + $allplayer = $MatchContestant->where('club_id',$club_info['id'])->where('match_id',$match_id)->select(); + // var_dump($club_info);exit; + // unset($allplayer[0]); + foreach ($allplayer as &$v){ + // $player_info = []; + $player_info = $player->where('id',$v['player_id'])->find(); + // if(!empty($player_info)) $player_info = $player_info->toArray(); + // var_dump($club_appleyres['club_player']);exit; + $v['player'] = $player_info; + } + $data['code'] = 1; + $data['msg'] = 'success'; + $data['data']['player'] = $allplayer; + $data['data']['club'] = $club_info; + return json($data); + } + + //俱乐部报名 + public function club_submit(){ + $user_id = $this->auth->id; + if (empty($user_id)){ + $this->error("请先登录!",url('user/login')); + } + $match_id = $this->request->param('match_id'); + $select_player = $this->request->param('select_player'); + $player = new Players(); + $club_model = new ClubModel(); + $club_match_apply = new ClubMatchApply(); + $clubinvate = new ClubInvate(); + // var_dump($select_player);exit; + $select_player = explode(",", $select_player); + // var_dump($select_player);exit; + $club_info = $club_model->where('user_id',$user_id)->where('status',9)->find(); + if(empty($club_info)){ + $this->error('请先注册俱乐部并通过认证'); + } + $club_appleyres = $club_match_apply->where('match_id',$match_id)->where('club_id',$club_info['id'])->find(); + if(empty($club_appleyres)){ + $club_match_apply->save([ + 'match_id' => $match_id, + 'club_id'=> $club_info['id'], + 'status' => 1, + 'club_player' => json_encode($select_player), + 'createtime' => time(), + 'updatetime' => time(), + ]); + }else{ + $club_match_apply->save([ + 'match_id' => $match_id, + 'club_id'=> $club_info['id'], + 'status' => 4, + 'club_player' => json_encode($select_player), + 'updatetime' => time(), + ],['id'=> $club_appleyres['id']]); + } + $this->success('提交成功'); + } + + public function index() + { + $this->view->assign('title', "身份认证"); + //获取用户id + $user_id = $this->auth->id; + + if (empty($user_id)){ + $this->error("请先登录!",url('user/login')); + } + $club_model = new ClubModel(); + + $club_info = $club_model + ->where('user_id', $user_id) + ->find(); + if(empty($club_info)){ + return $this->redirect('players/registeredflyers'); + } + // var_dump($club_info);exit; + $club_info['createtime'] = date('Y-m-d H:i:s', $club_info['createtime']); + $club_info['updatetime'] = date('Y-m-d H:i:s', $club_info['updatetime']); + $this->view->assign('clubinfo', $club_info); + + // $this->view->assign('clubinfo', ''); + return $this->view->fetch(); + } + + //飞手公会 + public function club_sociaty(){ + header('Access-Control-Allow-Origin: *'); + header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept"); + header('Access-Control-Allow-Methods: GET,POST'); + + $this->confirmed_club(); + $club = new ClubModel(); + $clubinvate = new ClubInvate(); + $player = new Players(); + $user_id = $this->auth->id; + $page = $this->request->param('page'); + $club_info = $club->where("user_id", $user_id)->find(); + $allplayer = $player->where('is_sociaty',1)->where('player_status',9)->select(); + $allplayer1 = $player->where('is_sociaty',1)->where('player_status',9)->page($page,10)->orderRaw('rand()')->select(); + foreach ($allplayer1 as &$val){ + $val['send_status'] = 0; + $res = $clubinvate->where('club_id',$club_info['id'])->where('player_id',$val['id'])->where('deletetime',null)->where('status',1)->find(); + if(!empty($res)) { + $val['send_status'] = 1; + $val['invate_id'] = $res['id']; + } + } + $data['total'] = count($allplayer); + $data['data'] = $allplayer1; + $data['user_data'] = $user_id; + return json($data); + } + + public function search_player(){ + $this->confirmed_club(); + $club = new ClubModel(); + $player = new Players(); + $clubinvate = new ClubInvate(); + $user_id = $this->auth->id; + $name = $this->request->param('name'); + $page = $this->request->param('page'); + // var_dump($name);exit; + $club_info = $club->where("user_id", $user_id)->find(); + $allplayer = $player->where('is_sociaty',1)->where('player_status',9)->where('real_name','like','%'.$name.'%')->select(); + $allplayer1 = $player->where('is_sociaty',1)->where('player_status',9)->where('real_name','like','%'.$name.'%')->page($page,10)->select(); + foreach ($allplayer1 as &$val){ + $val['send_status'] = 0; + $res = $clubinvate->where('club_id',$club_info['id'])->where('player_id',$val['id'])->where('deletetime',null)->where('status',1)->find(); + if(!empty($res)) { + $val['send_status'] = 1; + $val['invate_id'] = $res['id']; + } + + } + $data['total'] = count($allplayer); + $data['data'] = $allplayer1; + return json($data); + // $club = new ClubModel(); + // $club_info = $club->where("user_id", $user_id)->find(); + + // $clubinvate->where('club_id',$club_info['id'])->where('deletetime',null)->find(); + } + + + //队伍向飞手发送邀请 + public function invate_player(){ + $this->confirmed_club(); + $club = new ClubModel(); + $clubinvate = new ClubInvate(); + $player = new Players(); + $ClubThirdOauth = new ClubThirdOauth(); + $Wxmb = new Wxmb(); + $user = new User(); + $user_id = $this->auth->id; + $player_id = $this->request->param('id'); + $club_info = $club->where("user_id", $user_id)->find(); + $player_info = $player->where('id',$player_id)->find(); + $user_info = $user->where('id',$player_info['member_id'])->find(); + $res = $clubinvate->where('club_id',$club_info['id'])->where('player_id',$player_id)->where('status',6)->where('deletetime',null)->find(); + $res1 = $clubinvate->where('club_id',$club_info['id'])->where('player_id',$player_id)->where('status',1)->where('deletetime',null)->find(); + $res2 = $clubinvate->where('club_id',$club_info['id'])->where('player_id',$player_id)->where('status',5)->where('deletetime',null)->find(); + if(!empty($res2)) $this->error('该飞手已接受邀请等待平台审核'); + if(!empty($res)){ + $this->error('该飞手已成为某支队伍成员'); + } + if(!empty($res1)){ + $this->error('您已发送过邀请'); + } + $clubinvate->save([ + 'club_id' => $club_info['id'], + 'player_id' => $player_id, + 'club_name' => $club_info['name'], + 'player_name' => $player_info['real_name'], + 'status' => 1, + 'name_short' => $club_info['name_short'], + 'member_number' => $user_info['member_number'], + 'createtime' => time(), + 'updatetime' => time(), + ]); + + $oauth_res = $ClubThirdOauth->where('user_id',$player_info['member_id'])->find(); + if(!empty($oauth_res)){ + $oauth_res = $oauth_res->toArray(); + $push_arr = array('title'=>'您有一条新的队伍邀请通知','username'=> $club_info['name'],'type'=>'队伍邀请'); + $Wxmb->leaguepush($push_arr,$oauth_res['openid']); + } + $this->success('邀请成功','',$clubinvate->id); + } + + //撤销邀请 + public function invate_player_cancel(){ + $this->confirmed_club(); + $club = new ClubModel(); + $clubinvate = new ClubInvate(); + $player = new Players(); + $user_id = $this->auth->id; + $player_id = $this->request->param('id'); + $invate_id = $this->request->param('invate_id'); + $club_info = $club->where("user_id", $user_id)->find(); + // $player->where('id',$player_id)->find(); + $res = $clubinvate->where('club_id',$club_info['id'])->where('player_id',$player_id)->where('status',6)->where('deletetime',null)->find(); + $res1 = $clubinvate->where('club_id',$club_info['id'])->where('player_id',$player_id)->where('status',4)->where('deletetime',null)->find(); + $res2 = $clubinvate->where('club_id',$club_info['id'])->where('player_id',$player_id)->where('status',5)->where('deletetime',null)->find(); + if(!empty($res2)) $this->error('该飞手已接受邀请等待平台审核'); + if(!empty($res)){ + $this->error('该飞手已成为某支队伍成员'); + } + if(!empty($res1)){ + $this->error('您已撤销过邀请'); + } + $clubinvate->save([ + 'club_id' => $club_info['id'], + 'player_id' => $player_id, + 'status' => 4, + 'createtime' => time(), + 'updatetime' => time(), + 'deletetime' => time(), + ],['id'=> $invate_id]); + $this->success('撤销邀请成功'); + } + + + public function myplayer(){ + $this->view->assign('title', "管理飞手"); + $user_id = $this->auth->id; + $player = new Players(); + $club_model = new ClubModel(); + // var_dump($player_res['id']);exit; + $club_info = $club_model->where('user_id',$user_id)->where('status',9)->find(); + if(empty($club_info)){ + $this->error('请先注册俱乐部并通过认证'); + } + return $this->view->fetch(); + + } + + public function myplayer_data(){ + $user_id = $this->auth->id; + $page = $this->request->param('page'); + if(empty($page)) $this->error('参数异常'); + $player = new Players(); + $club_model = new ClubModel(); + $clubinvate = new ClubInvate(); + $club_info = $club_model->where('user_id',$user_id)->where('status',9)->find(); + // var_dump($user_id);exit; + if(empty($club_info)){ + $this->error('请先注册俱乐部并通过'); + } + // $res = $club_model->where('user_id',$user_id)->find(); + + $res = $clubinvate->where('club_id',$club_info['id'])->where('status in (1,3,5,6,7,8,9)')->where('deletetime',null)->select(); + $res1 = $clubinvate->where('club_id',$club_info['id'])->where('status in (1,3,5,6,7,8,9)')->where('deletetime',null)->page($page,10)->select(); + // var_dump($club_info['id']);exit; + $info = [];$i = 0; + foreach ($res1 as $v){ + $player_res = $player->where('id',$v['player_id'])->find(); + // $user_info = $this->model->where('id',$player_res['member_id'])->find(); + // if(!empty($player_res)) { + $info[$i]['player'] = $player_res; + $info[$i]['status'] = $v['status']; + $info[$i]['check_id'] = $v['id']; + $info[$i]['createtime'] = date('Y-m-d H:i:s', $v['createtime']); + $i++; + // } + } + $data['code'] = 1; + $data['msg'] = 'success'; + $data['total'] = count($res); + $data['data'] = $info; + return json($data); + } + + //上传协议 + public function send_protocol(){ + $club_model = new ClubModel(); + $clubinvate = new ClubInvate(); + $user_id = $this->auth->id; + $protocol = $this->request->post('protocol'); + $player_id = $this->request->post('player_id'); + if(empty($protocol)) $this->error('上传材料不能为空'); + $club_info = $club_model + ->where('user_id', $user_id) + ->where('status', 9) + ->find(); + if(empty($club_info)) $this->error('您无权限操作'); + $clubinvate->save([ + 'status' => 5, + 'protocol' => $protocol, + 'updatetime' => time() + ],['player_id'=> $player_id, 'club_id' => $club_info['id'],'deletetime' => null]); + $this->success('上传成功',url('club/myplayer')); + } + +} diff --git a/application/index/controller/Clubinvate.php b/application/index/controller/Clubinvate.php new file mode 100644 index 0000000..4c86af2 --- /dev/null +++ b/application/index/controller/Clubinvate.php @@ -0,0 +1,99 @@ +view->assign('clubinfo', ''); + return $this->view->fetch(); + } + + public function confirmed_player(){ + $player = new Players(); + $user_id = $this->auth->id; + $player_info = $player->where("member_id", $user_id)->find(); + + if (empty($player_info)){ + $this->error(__("请先申请成为飞手!"), url('players/registeredflyers')) ; + } + + if ($player_info['player_status'] != 9){ + $this->error(__("您还不是正式飞手!"), url('user/index')); + } + } + + public function confirmed_club(){ + $club = new Club(); + $user_id = $this->auth->id; + $club_info = $club->where("user_id", $user_id)->find(); + + if (empty($player_info)){ + $this->error(__("请先申请成为队伍!"), url('players/registeredflyers')) ; + } + + if ($player_info['player_status'] != 9){ + $this->error(__("您还不是正式队伍!"), url('user/index')); + } + } + + //飞手加入公会 + public function join_sociaty(){ + $this->confirmed_player(); + $user_id = $this->auth->id; + // var_dump($user_id);exit; + $player = new Players(); + $clubinvate = new ClubInvateModel(); + $player_info = $player->where("member_id", $user_id)->find(); + $res = $clubinvate->where('player_id',$player_info['id'])->where('status',6)->where('deletetime',null)->find(); + // $res1 = $clubinvate->where('club_id',$club_info['id'])->where('player_id',$player_id)->where('status',4)->where('deletetime',null)->find(); + $res2 = $clubinvate->where('player_id',$player_info['id'])->where('status',5)->where('deletetime',null)->find(); + $res3 = $clubinvate->where('player_id',$player_info['id'])->where('status',9)->where('deletetime',null)->find(); + if(!empty($res2)) $this->error('您已接受邀请等待平台审核'); + if(!empty($res3)) $this->error('您已接受邀请等待队伍上传协议'); + if(!empty($res)){ + $this->error('您已成为某支队伍成员'); + } + $player = new Players(); + $player->save([ + 'is_sociaty' => 1, + 'updated_at' => date('Y-m-d H:i:s',time()), + ],['member_id'=>$user_id]); + $this->success('加入成功',url('players/index')); + } + + //飞手退出公会 + public function exit_sociaty(){ + $this->confirmed_player(); + $user_id = $this->auth->id; + $clubinvate = new ClubInvateModel(); + $player = new Players(); + $player->save([ + 'is_sociaty' => '', + 'updated_at' => date('Y-m-d H:i:s',time()), + ],['member_id'=>$user_id]); + $this->success('退出成功',url('players/index')); + } + + +} diff --git a/application/index/controller/Combine.php b/application/index/controller/Combine.php new file mode 100644 index 0000000..b18edcf --- /dev/null +++ b/application/index/controller/Combine.php @@ -0,0 +1,30 @@ +where('id',1)->find()->toArray(); + var_dump($res);exit; + return $this->view->fetch(); + } + + + public function combine_save(){ + $user = auth_user(); + $request = Request::instance(); + $params = $request->param(); + var_dump($user);exit; + } +} diff --git a/application/index/controller/Complain.php b/application/index/controller/Complain.php new file mode 100644 index 0000000..56a3486 --- /dev/null +++ b/application/index/controller/Complain.php @@ -0,0 +1,62 @@ +view->fetch(); + } + + public function confirmed_player(){ + $user_id = $this->auth->id; + $player = new Players(); + $player_info = $player->where("member_id", $user_id)->find(); + + if (empty($player_info)){ + $this->error(__("请先申请成为飞手!"), url('players/registeredflyers')) ; + } + + if ($player_info['player_status'] != 9){ + $this->error(__("您还不是正式飞手!"), url('user/index')); + } + } + + //飞手申诉 + public function player_complain(){ + $this->confirmed_player(); + $user_id = $this->auth->id; + $player = new Players(); + $player_info = $player->where("member_id", $user_id)->find(); + $complain_text = $this->request->param('complain_text'); + $clubcomplain = new ClubComplain(); + $complain_res = $clubcomplain->where('player_id',$player_info['id'])->find(); + // if(!empty($complain_res)) $this->error('您已提交过申诉'); + if(empty($complain_text)) $this->error("申诉内容不能为空"); + $clubcomplain->save([ + 'player_name' =>$player_info['real_name'], + 'player_tel' => $player_info['phone'], + 'player_id' => $player_info['id'], + 'user_id' => $user_id, + 'com_content'=> $complain_text, + 'createtime' => time(), + ]); + $this->success('提交成功',url('players/index')); + } + +} diff --git a/application/index/controller/Index.php b/application/index/controller/Index.php new file mode 100644 index 0000000..c2092d0 --- /dev/null +++ b/application/index/controller/Index.php @@ -0,0 +1,214 @@ +qsq(1); + // var_dump($res);exit; + $arr = ['203.11','12.68','95.55']; + $maxString = min($arr); + // var_dump($maxString);exit; + $url = 'https://www.fpvone.cn/api/sms/send'; + + // POST数据 + $data = array('mobile' => '13626832346', 'event' => 'notice_diyform'); + + // 初始化cURL会话 + $ch = curl_init($url); + + // 设置cURL选项 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回结果而不是输出 + curl_setopt($ch, CURLOPT_POST, true); // 发送POST请求 + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); // POST数据 + + // 执行cURL会话 + $response = curl_exec($ch); + + // 关闭cURL会话 + curl_close($ch); + + // 打印结果 + // echo $response;exit; + // exit; + $OrderItem = new OrderItem(); + $Goods = new Goods(); + $res = $Goods->where('id',183)->find(); + // $res = $OrderItem->where('id',916)->find(); + $arr = explode(",", $res['category_ids']); + var_dump($arr);exit; + $virtualfiled = json_decode($res['virtualfiled']); + foreach ($virtualfiled as $val){ + var_dump($val);exit; + } + var_dump($oldarr);exit; +} + + public function bcd(){ + $CategoryModel = new CategoryModel(); + $category_ids = 377; + $cate_res = $CategoryModel->where('id',$category_ids)->find(); + var_dump($cate_res['vip_discount']);exit; + $vip = new VipModel(); + $config_vip = sheep_config('shop.vip'); + $order['user_id'] = 1044; + $order['createtime'] = 1729042450; + $user = User::where('id', $order['user_id'])->find(); + $vip_res = $vip->where('uid',$order['user_id'])->find(); + if(empty($vip_res)){ + $data = [ + 'uid' => $order['user_id'], + 'nickname' => $user->nickname, + 'starttime' => strtotime($order['createtime']), + 'endtime' => strtotime($order['createtime']) + ($config_vip['vip_range'])*24*3600, + ]; + $res = $vip->create($data); + }else{ + $data = [ + 'endtime' => $vip_res['endtime'] + ($config_vip['vip_range'])*24*3600, + ]; + $res = $vip->update($data,['uid'=>$order['user_id']]); + } + var_dump($res);exit; + $url = 'http://www.baidu.com'; + $headers = [ + 'Location' => $url, + 'Other-Token' => 'tsdfsdfsdf', + // 可以继续添加其他需要设置的HTTP头信息 + ]; + return redirect($url, [], 301)->header($headers); + } + + + public function abc($token = 'abc'){ + // var_dump(123);exit; +// $url = 'https://timer.fpvone.cn/timer/#/game'; +// $response = Response::create($url, 'redirect'); + +// // 设置请求头 +// $response->header([ +// 'Location' => $url, +// 'Other-Token' => $token // 可以设置其他需要的请求头 +// ]); + +// // 发送响应并终止脚本执行 +// $response->send(); +// exit; + $this->request->header('asdfsdf:ckjxvhkxjc'); + // header('abcd:adfsdfsd'); + // $returnURL = 'https://www.baidu.com'; + // header('location:'.$returnURL);exit; + $token = '34670c8a-d006-4e22-ac10-cb1f6e38b328'; + $config = [ + 'hashalgo' => 'ripemd160', + 'key' => 'nRU6uwTVHyD3i8AMch4Qf79tjOdYCPzX' + ]; + $res = hash_hmac($config['hashalgo'], $token, $config['key']); + + var_dump($res);exit;; + // file_put_contents("test.txt",'aaa'); + } + + public function index() + { + $url = 'https://ydool2017.oss-cn-shanghai.aliyuncs.com/%E6%AF%94%E7%BF%BC%E9%A3%9E%E8%A1%8C%E7%BD%91/uploads/20240311/06335f00b870cbc5650193c7cdff2301.jpg'; + $headers = get_headers($url); + // var_dump($headers);exit; + // 如果无法获取响应头或者响应头第0位不是HTTP/1.1 200 OK,则认为链接无效 + if (!is_array($headers) || strpos($headers[0], '200 OK') === false) { + // var_dump(123);exit; + return false; + } + return $this->view->fetch(); + } + + public function wechatlogin(){ + $code = $this->request->param("code"); + $match_id = $this->request->param("match_id"); + // var_dump($match_id);exit; + $wechat = new Wechat(); + $access_token = $wechat->getAccessTokens($code); + $userinfo = $wechat->getUserInfos($access_token); + + // var_dump($userinfo);exit; + $userwechat = new Userwechat(); + $res = $userwechat->where('openid','=',$userinfo['openid'])->find(); + // var_dump($res->toArray());exit; + //数据库为空 + if(empty($res)){ + $userwechat->data([ + 'openid'=> $userinfo['openid'], + 'wechat_name' => $userinfo['nickname'], + 'wechat_avatar' => $userinfo['headimgurl'] + ]); + $userwechat->save(); + }else{ + $res = $res->toArray(); + $userwechat->save([ + 'wechat_name' => $userinfo['nickname'], + 'wechat_avatar' => $userinfo['headimgurl'] + ],['openid' => $userinfo['openid']]); + } + Session::set('userinfo', $userinfo); + header('location:https://www.fpvone.cn/wurenjijingsusaicheng/'.$match_id); + // var_dump($res); + } + + public function test(){ + $str = '金老板'; + $strlen = mb_strlen($str,'UTF8'); + if($strlen == 2){ + $real_name=mb_substr($str,0,1,'UTF8').'*'; + } + if($strlen > 2){ + $tmp_str=mb_substr($str,0,$strlen-2,'UTF8').'*'; + $real_name=$tmp_str.mb_substr($str,-1,1,'UTF8'); + } + var_dump($real_name);exit; + } + + //获取插件配置信息 + public function getconfig(){ + $config = get_addon_config('third'); + // $config = Config::get('third.wechat'); + // print_r($config['wechat']);exit; + } +} diff --git a/application/index/controller/League.php b/application/index/controller/League.php new file mode 100644 index 0000000..e81a76d --- /dev/null +++ b/application/index/controller/League.php @@ -0,0 +1,405 @@ +rankingService = new RankingService(); + $this->leagueRound = new Leagueround(); + $this->leagueService = new LeagueService(); + $this->leagueClubIntegral = new LeagueClubIntegral(); + $this->leagueIntegral = new LeagueIntegral(); + } + + public function get_match_final(){ + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies + header('Access-Control-Expose-Headers: *'); //服务器 headers 白名单,可以让客户端进行访问 + header('Access-Control-Allow-Headers: *'); + $match_id = $this->request->param('match_id'); + $res = $this->leagueIntegral->field('number,player_id,player_name,gender,name_short,birthday')->where('match_id',$match_id)->order('number')->limit(64)->select(); + if(empty($res)){ + $res['code'] = 0; + $res['data'] = ''; + $res['msg'] = "最终成绩还未产生"; + // $this->error('最终成绩还未产生'); + }else{ + foreach ($res as &$v){ + if($v['birthday'] >= '2006-12-31'){ + $v['is_young'] = '是'; + }else{ + $v['is_young'] = '否'; + } + } + } + + return json($res); + } + + public function judge_league(){ + $Archives = new Archives(); + $match_id = $this->request->param('match_id'); + $arc_res = $Archives->where('id',$match_id)->find(); + if($arc_res['model_id'] == 6){ + $arr = 'league'; + return json($arr); + }elseif($arc_res['model_id'] == 2){ + $arr = 'saishi'; + return json($arr); + }else{ + $arr = 'error'; + return json($arr); + } + + } + + public function get_club_cert(){ + $Club = new Club(); + $Players= new Players(); + $User = new User(); + $certificateService = new CertificateService(); + $match_id = $this->request->param('match_id'); + $club_id = $this->request->param('club_id'); + $club_info = $Club->field('id,name_short,competition_slogan,user_id,province,city,district,logo')->where('id',$club_id)->find(); + + // var_dump($user_info['member_number']);exit; + $integral_club_res = $this->leagueClubIntegral->where(['match_id' => $match_id,'club_id'=>$club_id])->find(); + // var_dump($integral_res);exit; + if(empty($integral_club_res)) $this->error('暂未查询到您的成绩'); + + $club_info['number'] = $integral_club_res['number']; + $club_info['grade'] = $integral_club_res['sum_grade']; + // var_dump($player_info);exit; + $row['club'] = $club_info; + $addonproducts = db("cms_addonproducts"); + $row['endtime'] = $addonproducts->where("id", $match_id)->field("match_end_time, match_start_time, stime, etime, club_course_poster, is_open_course")->find(); + $row['certificate_number']['certificate_number'] = $certificateService->getCertificate( + [ + 'match_id' => $match_id, + 'club_id' => $club_id, + 'member_id' => $club_info['user_id'], + 'player_id' => 0, + ] + ); + if (!empty($row)) { + $res['code'] = 1; + $res['data'] = $row; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['msg'] = "数据为空"; + } + + return json($res); + } + + public function get_cert(){ + $Club = new Club(); + $Players= new Players(); + $User = new User(); + $certificateService = new CertificateService(); + $match_id = $this->request->param('match_id'); + $player_id = $this->request->param('player_id'); + $player_info = $Players->field('id,member_id,age,gender,real_name,player_pic,member_id')->where('id',$player_id)->find(); + + $user_info = $User->where('id',$player_info['member_id'])->find(); + // var_dump($user_info['member_number']);exit; + $integral_res = $this->leagueIntegral->where(['match_id' => $match_id,'player_id'=>$user_info['member_number']])->find(); + // var_dump($integral_res);exit; + if(empty($integral_res)) $this->error('暂未查询到您的成绩'); + $player_info['member_number'] = $user_info['member_number']; + $player_info['number'] = $integral_res['number']; + $player_info['grade'] = $integral_res['grade']; + // if(empty($integral_res)) $this->error('未查询到成绩'); + // var_dump($integral_res);exit; + $club_info = $Club->field('id,name_short')->where('id',$integral_res['club_id'])->find(); + + + // var_dump($player_info);exit; + $row['club'] = $club_info; + $row['player'] = $player_info; + $addonproducts = db("cms_addonproducts"); + $row['endtime'] = $addonproducts->where("id", $match_id)->field("match_end_time, match_start_time, stime, etime, course_poster, is_open_course")->find(); + $row['certificate_number']['certificate_number'] = $certificateService->getCertificate( + [ + 'match_id' => $match_id, + 'player_id' => $player_id, + 'member_id' => $player_info['member_id'], + 'club_id' => 0, + ] + ); + if (!empty($row)) { + $res['code'] = 1; + $res['data'] = $row; + $res['msg'] = "获取成功"; + } else { + $res['code'] = 0; + $res['data'] = []; + $res['msg'] = "数据为空"; + } + + return json($res); + } + + //队伍总积分排行榜 + public function club_allintegral(){ + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies + header('Access-Control-Expose-Headers: *'); //服务器 headers 白名单,可以让客户端进行访问 + header('Access-Control-Allow-Headers: *'); + $club = new Club(); + + + // 已参赛 + $all_club = $this->leagueClubIntegral->group('club_id')->select(); + $arr = $arrs = []; + foreach ($all_club as $value){ + $arr = $this->leagueClubIntegral->field('sum(sum_grade) as all_grade,club_id,club_name')->where('club_id',$value['club_id'])->select(); + $club_info = $club->field('logo,name_short')->where('id',$value['club_id'])->find(); + $arr['club'] = $club_info; + $arrs[] = $arr; + } + + // 未参赛的俱乐部 + $res = $club->where('status',9)->group('name_short')->where('id','NOT IN',function($query){ + $query->table('peewee_league_club_integral')->group('club_id')->field('club_id'); + })->select(); + $arr1 = $arr1s = []; + foreach ($res as $vvv){ + $arr1[0] = array('all_grade'=>0,'club_id'=>$vvv['id'],'club_name'=>$vvv['name']); + $arr1['club'] = array('logo'=>$vvv['logo'],'name_short'=>$vvv['name_short']); + $arr1s[] = $arr1; + } + + + $volume = []; + foreach ($arrs as $key => $row) + { + $volume[$key] = $row[0]['all_grade']; + // $edition[$key] = $row['edition']; + } + array_multisort($volume, SORT_DESC, $arrs); + + $new_arr = array_merge($arrs,$arr1s); + + $rank = 1; $i = 0;// 当前排名 + $all_grade = null; // 当前分数 + foreach ($new_arr as $k => &$value) { + if ($all_grade != $value[0]['all_grade']) { + $rank = $rank + $i; + $i = 0; + } + $value['integral_sort'] = $rank; + $all_grade = $value[0]['all_grade']; + $i++; + } + // var_dump(count($new_arr));exit; + + return json($new_arr); + } + + + + //队伍分站赛积分 + public function club_integral($match_id,$club_name=null){ + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies + header('Access-Control-Expose-Headers: *'); //服务器 headers 白名单,可以让客户端进行访问 + header('Access-Control-Allow-Headers: *'); + $club = new Club(); + //正常前端请求 + if(empty($club_name)){ + $match_id = $this->request->param('match_id'); + } + + $res = $this->leagueClubIntegral->where('match_id',$match_id)->order('sum_grade desc')->select(); + // var_dump($res);exit; + foreach ($res as &$val){ + $club_info = $club->field('logo,name_short')->where('id',$val['club_id'])->find(); + $val['club'] = $club_info; + } + + // 未参赛的俱乐部 + $res_not = $club->where('status',9)->group('name_short')->where('id','NOT IN',function($query) use($match_id){ + $query->table('peewee_league_club_integral')->where('match_id',$match_id)->group('club_id')->field('club_id'); + })->select(); + $arr1 = $arr1s = []; + foreach ($res_not as $vvv){ + $arr1 = array('sum_grade'=>0,'club_id'=>$vvv['id'],'club_name'=>$vvv['name']); + $arr1['club'] = array('logo'=>$vvv['logo'],'name_short'=>$vvv['name_short']); + $arr1s[] = $arr1; + } + + $new_arr = array_merge($res,$arr1s); + + $rank = 1; $i = 0;// 当前排名 + $all_grade = null; // 当前分数 + // var_dump($res);exit; + foreach ($new_arr as $k => &$value) { + // var_dump($value['sum_grade']);exit; + if ($all_grade != $value['sum_grade']) { + $rank = $rank + $i; + $i = 0; + } + $value['integral_sort'] = $rank; + $all_grade = $value['sum_grade']; + $i++; + } + //俱乐部详情分站赛排名 + if(!empty($club_name)){ + foreach ($new_arr as $zhi){ + if($zhi['name_short'] == $club_name){ + $new_arr = $zhi; + break; + } + } + return $new_arr; + } + // var_dump(count($new_arr));exit; + return json($new_arr); + } + + + //飞手总积分排行 + public function player_allintegral(){ + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies + header('Access-Control-Expose-Headers: *'); //服务器 headers 白名单,可以让客户端进行访问 + header('Access-Control-Allow-Headers: *'); + $club = new Club(); + $Players= new Players(); + $User = new User(); + $LeagueIntegral = new LeagueIntegral(); + $gender= $this->request->param('gender'); + $birthday = $this->request->param('birthday'); + $where = []; + if(!empty($gender)){ + $where['gender'] = '女'; + } + if(!empty($birthday)){ + $where['birthday'] = ['>','2006-12-31']; + } + $all_player = $LeagueIntegral->group('player_id')->where($where)->select(); + $arr = $arrs = []; + foreach ($all_player as $value){ + $arr = $LeagueIntegral->field('sum(grade) as all_grade,player_id,player_name')->where('player_id',$value['player_id'])->select(); + $user_info = $User->where('member_number',$value['player_id'])->find(); + $player_info = $Players->field('player_pic')->where('member_id',$user_info['id'])->find(); + $club_info = $club->field('name_short')->where('id',$value['club_id'])->find(); + $arr['player'] = $player_info; + $arr['club'] = $club_info; + $arrs[] = $arr; + } + $volume = []; + foreach ($arrs as $key => $row) + { + $volume[$key] = $row[0]['all_grade']; + // $edition[$key] = $row['edition']; + } + // var_dump($volume);exit; + array_multisort($volume, SORT_DESC, $arrs); + // $arr = $this->arraySort($arr,'all_grade'); + // var_dump($arr);exit; + $rank = 1; $i = 0;// 当前排名 + $all_grade = null; // 当前分数 + foreach ($arrs as $k => &$value) { + if ($all_grade != $value[0]['all_grade']) { + $rank = $rank + $i; + $i = 0; + } + $value['integral_sort'] = $rank; + $all_grade = $value[0]['all_grade']; + $i++; + } + return json($arrs); + } + + + + //飞手分站赛积分 + public function player_integral($match_id,$player_id=null){ + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies + header('Access-Control-Expose-Headers: *'); //服务器 headers 白名单,可以让客户端进行访问 + header('Access-Control-Allow-Headers: *'); + $club = new Club(); + $Players= new Players(); + $User = new User(); + $LeagueIntegral = new LeagueIntegral(); + if(empty($player_id)){ + $match_id = $this->request->param('match_id'); + } + + $gender= $this->request->param('gender'); + $birthday = $this->request->param('birthday'); + $where = []; + if(!empty($gender)){ + $where['gender'] = '女'; + } + if(!empty($birthday)){ + $where['birthday'] = ['>','2006-12-31']; + } + $res = $LeagueIntegral->where('match_id',$match_id)->where($where)->select(); + foreach ($res as &$val){ + $club_info = $club->field('name_short')->where('id',$val['club_id'])->find(); + $user_info = $User->where('member_number',$val['player_id'])->find(); + $player_info = $Players->field('player_pic')->where('member_id',$user_info['id'])->find(); + $val['club'] = $club_info; + $val['player'] = $player_info; + } + + $rank = 1; $i = 0;// 当前排名 + $all_grade = null; // 当前分数 + // var_dump($res);exit; + foreach ($res as $k => &$value) { + // var_dump($value['sum_grade']);exit; + if ($all_grade != $value['grade']) { + $rank = $rank + $i; + $i = 0; + } + $value['number'] = $rank; + $all_grade = $value['grade']; + $i++; + } + if(!empty($player_id)){ + foreach ($res as $zhi){ + if($zhi['player_id'] == $player_id){ + $res = $zhi; + break; + } + } + return $res; + } + // var_dump($all_club);exit; + return json($res); + } + + +} \ No newline at end of file diff --git a/application/index/controller/Leagueqr.php b/application/index/controller/Leagueqr.php new file mode 100644 index 0000000..a32d684 --- /dev/null +++ b/application/index/controller/Leagueqr.php @@ -0,0 +1,194 @@ +wechat = Wechat::officialAccountManage(); + } + + + public function getleagueQrcode() + { + + $event = $this->request->param('event'); +// var_dump($event);exit; + if (!in_array($event, ['bind'])) { + $this->error('参数错误'); + } + + $adminId = $this->auth->id; + var_dump($adminId);exit; + $thirdOauth = ClubThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'user_id' => $adminId + ])->find(); + + if ($thirdOauth) { + error_stop('已绑定微信账号', -2, $thirdOauth); + } + + // 二维码和缓存过期时间 + $expireTime = 1 * 60; + + // 事件唯一标识 + $eventId = Random::uuid(); + // var_dump($eventId);exit; + $cacheKey = "wechatAdmin.{$event}.{$eventId}"; + + cache($cacheKey, ['id' => 0], $expireTime); + + try { + $result = $this->wechat->qrcode->temporary($cacheKey, $expireTime); + // var_dump($result);exit; + $qrcode = $this->wechat->qrcode->url($result['ticket']); + // var_dump($qrcode);exit; + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + // return json($qrcode);exit; + // var_dump($qrcode);exit; + $this->success('', null, [ + 'url' => $qrcode, + 'eventId' => $eventId + ]); + } + + + // 获取公众号二维码 + public function getQrcode() + { + + $event = $this->request->param('event'); +// var_dump($event);exit; + if (!in_array($event, ['bind'])) { + $this->error('参数错误'); + } + + $adminId = $this->auth->id; + // var_dump($adminId);exit; + $thirdOauth = ThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'admin_id' => $adminId + ])->find(); + + if ($thirdOauth) { + error_stop('已绑定微信账号', -2, $thirdOauth); + } + + // 二维码和缓存过期时间 + $expireTime = 1 * 60; + + // 事件唯一标识 + $eventId = Random::uuid(); + // var_dump($eventId);exit; + $cacheKey = "wechatAdmin.{$event}.{$eventId}"; + + cache($cacheKey, ['id' => 0], $expireTime); + + try { + $result = $this->wechat->qrcode->temporary($cacheKey, $expireTime); + // var_dump($result);exit; + $qrcode = $this->wechat->qrcode->url($result['ticket']); + // var_dump($qrcode);exit; + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + // return json($qrcode);exit; + // var_dump($qrcode);exit; + $this->success('', null, [ + 'url' => $qrcode, + 'eventId' => $eventId + ]); + } + + // 检查扫码结果 + public function checkScan() + { + $event = $this->request->param('event'); + $eventId = $this->request->param('eventId'); + + if (!in_array($event, ['bind'])) { + error_stop('参数错误'); + } + + $cacheKey = "wechatAdmin.{$event}.{$eventId}"; + + $cacheValue = cache($cacheKey); + // var_dump($cacheValue);exit; + if (empty($cacheValue)) { + error_stop('二维码已过期, 请重新扫码'); + } + + if ($cacheValue['id'] === 0) { + error_stop('等待扫码', -1); + } + + if ($cacheValue['id'] !== 0) { + switch ($event) { + case 'bind': + $adminId = $this->auth->id; + + $thirdOauth = ThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'openid' => $cacheValue['id'], + ])->find(); + + if ($thirdOauth && $thirdOauth->admin_id !== 0) { + error_stop('该微信账号已被绑定'); + } + + if (!$thirdOauth) { + $thirdOauth = ThirdOauth::create([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'openid' => $cacheValue['id'], + 'admin_id' => $adminId + ]); + } else { + $thirdOauth->admin_id = $adminId; + $thirdOauth->save(); + } + break; + } + $this->success(); + } + } + + // 解绑 + public function unbind() + { + $adminId = $this->auth->id; + + $thirdOauth = ThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'admin_id' => $adminId + ])->find(); + + if ($thirdOauth) { + $thirdOauth->admin_id = 0; + $thirdOauth->save(); + } + $this->success(); + } + + +} diff --git a/application/index/controller/Live.php b/application/index/controller/Live.php new file mode 100644 index 0000000..632b0aa --- /dev/null +++ b/application/index/controller/Live.php @@ -0,0 +1,83 @@ +screemService = new ScreenService(); + } + + + public function live() + { + + $id = $this->request->param("id"); + if (empty($id)) { + $this->error("非法请求"); + } + $this->auth->setMatchId($id); + if (!$this->screemService->verifyScreen(['match_id' => $id, 'type' => "ranking"])) { + // 判断是否登陆 + if (!Session::has("screen_ranking{$id}")) { + $this->match_id = $id; + $this->error("请先登录!", url('live/login', ['match_id' => $id])); + } + } + + $screen_info = $this->screemService->getScreenInfo([ + 'match_id' => $id, + 'type' => "ranking" + ]); + $this->view->assign("match_id", $id); + $this->view->assign('screeninfo', $screen_info); + return $this->view->fetch(); + } + + public function login() + { + if ($this->request->isPost()) { + $data['look_pwd'] = $this->request->post("password"); + + if (empty($data['look_pwd'])) { + $this->error("密码不能为空!"); + } elseif (empty($this->request->param("match_id"))) { + $this->error("非法请求"); + } + + $data['match_id'] = $this->request->param("match_id"); + $data['type'] = 'ranking'; + + if ($this->screemService->verifyScreen($data)) { + Session::set("screen_ranking{$data['match_id']}", true); + $this->success("登陆成功!", url('live/live?id='.$data['match_id'])); + } else { + $this->error("密码错误!"); + } + } + $id = $this->request->param("match_id"); + if (empty($id)) { + $this->error("非法请求"); + } + return $this->view->fetch(); + } +} \ No newline at end of file diff --git a/application/index/controller/Match.php b/application/index/controller/Match.php new file mode 100644 index 0000000..325a12a --- /dev/null +++ b/application/index/controller/Match.php @@ -0,0 +1,203 @@ +auth; + + if (!Config::get('fastadmin.usercenter')) { + $this->error(__('User center already closed'), '/'); + } + + //监听注册登录退出的事件 + Hook::add('user_login_successed', function ($user) use ($auth) { + $expire = input('post.keeplogin') ? 30 * 86400 : 0; + Cookie::set('uid', $user->id, $expire); + Cookie::set('token', $auth->getToken(), $expire); + }); + Hook::add('user_register_successed', function ($user) use ($auth) { + Cookie::set('uid', $user->id); + Cookie::set('token', $auth->getToken()); + }); + Hook::add('user_delete_successed', function ($user) use ($auth) { + Cookie::delete('uid'); + Cookie::delete('token'); + }); + Hook::add('user_logout_successed', function ($user) use ($auth) { + Cookie::delete('uid'); + Cookie::delete('token'); + }); + + $this->matchService = new MatchService(); + } + + public function clubmatch(){ + $this->view->assign('title', "我的赛事"); + $club_match_apply = new ClubMatchApply(); + $MatchContestantModel = new MatchContestant(); + $archivesModel = new Archives(); + $club = new Club(); + + $club_info = $club->where('user_id',$this->auth->id)->find(); + if(empty($club_info)) $this->error('请先注册俱乐部'); + if($club_info['status'] != 9) $this->error('您还不是正式俱乐部'); + $club_match_list = $MatchContestantModel->where('club_id',$club_info['id'])->group('match_id')->paginate(10); + $page = $club_match_list->render(); + + // var_dump($club_match_list);exit; + $arr = []; $i = 0; + if(!empty($club_match_list)){ + foreach ($club_match_list as &$v){ + $match_info = $archivesModel->find($v->match_id); + $v['match'] = $match_info; + $v['createtime'] = date('Y-m-d H:i:s',$v['createtime']); + } + } + $this->assign("club_matchlist", $club_match_list); + $this->assign('page', $page); + // var_dump($club_match_list);exit; + return $this->view->fetch('club_match'); + } + + public function userMatch() + { + //获取用户赛事信息 + $MatchContestantModel = new MatchContestant(); + + $playersModel = new Players(); + + $player_info = $playersModel->where("member_id", $this->auth->id)->find(); + + if (empty($player_info)){ + $this->error(__("请先申请成为飞手!"), url('players/registeredflyers')) ; + } + + if ($player_info['player_status'] != 9){ + $this->error(__("您还不是正式飞手!"), url('user/index')); + } + + $user_match_list = $MatchContestantModel->where("player_id", $player_info->id)->select(); + + if (!empty($user_match_list)){ + $archivesModel = new Archives(); + foreach ($user_match_list as $key => $val){ + $match_info = $archivesModel->find($val->match_id); + if (!empty($match_info)) { + $user_match_list[$key]['match'] = $match_info; + } else { + unset($user_match_list[$key]); + } + //$user_match_list[$key]['match'] = $archivesModel->find($val->match_id); +// if ($this->auth->id == 855) { +// echo "
                                                                                                                  ";
                                                                                                                  +//                    $user_match_list = collection($user_match_list)->toArray();
                                                                                                                  +//                    print_r($user_match_list);exit;
                                                                                                                  +//                }
                                                                                                                  +            }
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $this->assign("matchlist", $user_match_list);
                                                                                                                  +
                                                                                                                  +        $this->view->assign('title', "我的赛事");
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function my_match_show()
                                                                                                                  +    {
                                                                                                                  +        $match_id = $this->request->param('id');
                                                                                                                  +
                                                                                                                  +        $MatchContestantModel = new MatchContestant();
                                                                                                                  +
                                                                                                                  +        $user_match_info = $MatchContestantModel->find($match_id);
                                                                                                                  +
                                                                                                                  +        if (!empty($user_match_info)){
                                                                                                                  +            $archivesModel = new Archives();
                                                                                                                  +            $user_match_info->match = $archivesModel->find($user_match_info->match_id);
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return $user_match_info;
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function getMatch()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $param['date'] = $this->request->param("date");
                                                                                                                  +        $param['page'] = $this->request->param("page", 1);
                                                                                                                  +        $param['limit'] = $this->request->param("limit", 10);
                                                                                                                  +
                                                                                                                  +        $row = $this->matchService->getAllMatch($param);
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function getmatchuser()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $match_id = $this->request->param("match_id");
                                                                                                                  +        $user_id = $this->request->param("user_id");
                                                                                                                  +
                                                                                                                  +        if (empty($match_id) || empty($user_id)) {
                                                                                                                  +            $this->error("非法请求!");
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $row = $this->matchService->getMatchUserInfo($match_id, $user_id);
                                                                                                                  +
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +}
                                                                                                                  \ No newline at end of file
                                                                                                                  diff --git a/application/index/controller/Players.php b/application/index/controller/Players.php
                                                                                                                  new file mode 100644
                                                                                                                  index 0000000..3b2d886
                                                                                                                  --- /dev/null
                                                                                                                  +++ b/application/index/controller/Players.php
                                                                                                                  @@ -0,0 +1,682 @@
                                                                                                                  +model = new \app\common\model\Players();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function sociaty(){
                                                                                                                  +        $clubinvate = new ClubInvate();
                                                                                                                  +        $player_info = $this->model->where("member_id", $this->auth->id)->find();
                                                                                                                  +
                                                                                                                  +        if (empty($player_info)){
                                                                                                                  +            $this->error(__("请先申请成为飞手!"), url('players/registeredflyers'))   ;
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        if ($player_info['player_status'] != 9){
                                                                                                                  +            $this->error(__("您还不是正式飞手!"), url('user/index'));
                                                                                                                  +        }        
                                                                                                                  +        $is_joinclub = 0;
                                                                                                                  +        $club_invateres = $clubinvate->where('player_id',$player_info['id'])->where('status',5)->where('deletetime',null)->find();
                                                                                                                  +        $club_invateres1 = $clubinvate->where('player_id',$player_info['id'])->where('status',9)->where('deletetime',null)->find();
                                                                                                                  +        $club_invateres2 = $clubinvate->where('player_id',$player_info['id'])->where('status',6)->where('deletetime',null)->find();
                                                                                                                  +        if(!empty($club_invateres) || !empty($club_invateres1) || !empty($club_invateres2)) $is_joinclub = 1;
                                                                                                                  +        
                                                                                                                  +        $this->view->assign('is_joinclub', $is_joinclub);
                                                                                                                  +        $this->view->assign('title', "飞手公会");
                                                                                                                  +        $this->view->assign('is_sociaty', $player_info['is_sociaty']);
                                                                                                                  +        $this->view->assign('playerinfo', $player_info);
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function index()
                                                                                                                  +    {
                                                                                                                  +        //获取用户id
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +        $club_playercheck = new ClubPlayercheck();
                                                                                                                  +
                                                                                                                  +        if (empty($user_id)){
                                                                                                                  +            $this->error("请先登录!",url('user/login'));
                                                                                                                  +        }
                                                                                                                  +        // var_dump($this->auth->member_type);exit;
                                                                                                                  +        $players_info = $this->model
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +            
                                                                                                                  +        if (empty($players_info) ){
                                                                                                                  +            $this->view->assign('playerinfo', $players_info);
                                                                                                                  +            return $this->redirect('players/registeredflyers');
                                                                                                                  +        }
                                                                                                                  +        
                                                                                                                  +        // $club_playercheckres = $club_playercheck->where('player_id',$players_info['id'])->where('status in (2,4,5)')->find();
                                                                                                                  +        // $club_playercheckres1 = $club_playercheck->where('player_id',$players_info['id'])->where('status',1)->find();
                                                                                                                  +        // if(!empty($club_playercheckres)) $this->view->assign('user_player_status', 2);
                                                                                                                  +        // if(!empty($club_playercheckres1)) $this->view->assign('user_player_status', 1);
                                                                                                                  +        
                                                                                                                  +        
                                                                                                                  +        if ($players_info->player_status == \app\common\model\Players::PLAYERS_MODIFY) {
                                                                                                                  +            $players_info->pass_centent =
                                                                                                                  +                db("player_check")->where(['after_status' => 1, 'player_id' => $players_info->id])->
                                                                                                                  +                order('id', 'DESC')->find();
                                                                                                                  +        }
                                                                                                                  +        if ($players_info->player_status == \app\common\model\Players::PLAYERS_NO_PASS) {
                                                                                                                  +            // 如果拒绝获取拒绝理由
                                                                                                                  +            $players_info->pass_centent =
                                                                                                                  +                db("player_check")->where(['after_status' => 2, 'player_id' => $players_info->id])->
                                                                                                                  +                order('id', 'DESC')->find();
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $this->view->assign('title', "身份认证");
                                                                                                                  +        $this->view->assign('players', $players_info);
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function authenticationflayers()
                                                                                                                  +    {
                                                                                                                  +        //获取用户id
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +
                                                                                                                  +        if (empty($user_id)){
                                                                                                                  +            $this->error("请先登录!",url('user/login'));
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $players_info = $this->model
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +
                                                                                                                  +        if (empty($players_info)){
                                                                                                                  +            $this->view->assign('playerinfo', $players_info);
                                                                                                                  +            return $this->redirect('players/registeredflyers');
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $this->view->assign('title', "身份认证");
                                                                                                                  +        $this->view->assign('players', $players_info);
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 重新申请
                                                                                                                  +     * @Author:Soar
                                                                                                                  +     * @Time:2023/10/8 15:17
                                                                                                                  +     * @param $ids
                                                                                                                  +     * @return void
                                                                                                                  +     */
                                                                                                                  +    public function reapply($ids = null)
                                                                                                                  +    {
                                                                                                                  +        $this->view->assign('title', "重新申请");
                                                                                                                  +
                                                                                                                  +        if ($this->request->isPost()) {
                                                                                                                  +            $id = $this->request->post("id");
                                                                                                                  +            $add_data['real_name'] = $this->request->post("real_name");
                                                                                                                  +            $add_data['gender'] = $this->request->post("gender");
                                                                                                                  +            $add_data['birthday'] = $this->request->post("birthday");
                                                                                                                  +            $add_data['age'] = $this->request->post("age");
                                                                                                                  +            $add_data['country'] = $this->request->post("country");
                                                                                                                  +            $add_data['province'] = $this->request->post("province");
                                                                                                                  +            $add_data['city'] = $this->request->post("city");
                                                                                                                  +            $add_data['district'] = $this->request->post("district");
                                                                                                                  +            $add_data['address'] = $this->request->post("address");
                                                                                                                  +            $add_data['player_pic'] = $this->request->post("player_pic");
                                                                                                                  +            $add_data['experience'] = $this->request->post("experience");
                                                                                                                  +            $add_data['guarder_name'] = $this->request->post("guarder_name");
                                                                                                                  +            $add_data['guarder_phone'] = $this->request->post("guarder_phone");
                                                                                                                  +            $add_data['guarder_card_number'] = $this->request->post("guarder_card_number");
                                                                                                                  +            $add_data['guarder_card_type'] = $this->request->post("guarder_card_type");
                                                                                                                  +            $add_data['guarder_card_front_view'] = $this->request->post("guarder_card_front_view");
                                                                                                                  +            $add_data['guarder_card_back_view'] = $this->request->post("guarder_card_back_view");
                                                                                                                  +            $add_data['card_type'] = $this->request->post("card_type");
                                                                                                                  +            $add_data['card_number'] = $this->request->post("card_number");
                                                                                                                  +            $add_data['card_front_view'] = $this->request->post("card_front_view");
                                                                                                                  +            $add_data['card_back_view'] = $this->request->post("card_back_view");
                                                                                                                  +            $add_data['card_view_same'] = $this->request->post("card_view_same");
                                                                                                                  +            $add_data['guarder_card_view_same'] = $this->request->post("guarder_card_view_same");
                                                                                                                  +            $add_data['asfc_user'] = $this->request->post("asfc_user");
                                                                                                                  +            $add_data['membership_id'] = $this->request->post("membership_id");
                                                                                                                  +            $add_data['flight_number'] = $this->request->post("flight_number");
                                                                                                                  +            $add_data['club_members'] = $this->request->post("club_members");
                                                                                                                  +            $add_data['club_name'] = $this->request->post("club_name");
                                                                                                                  +            $add_data['duties'] = $this->request->post("duties");
                                                                                                                  +            $add_data['counselor_name'] = $this->request->post("counselor_name");
                                                                                                                  +            $add_data['post_type'] = $this->request->post("post_type");
                                                                                                                  +
                                                                                                                  +            $this->registeredFlyer($id, $add_data);
                                                                                                                  +        }
                                                                                                                  +        //获取用户id
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +
                                                                                                                  +        if (empty($user_id)){
                                                                                                                  +            $this->error("请先登录!",url('user/login'));
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $player_info = $this->model
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +
                                                                                                                  +        $this->view->assign('playerinfo', $player_info);
                                                                                                                  +
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function edit($ids = null)
                                                                                                                  +    {
                                                                                                                  +        $this->view->engine->layout('layout/defaultsRegister');
                                                                                                                  +        $this->view->assign('title', "修改信息");
                                                                                                                  +
                                                                                                                  +        if ($this->request->isPost()) {
                                                                                                                  +            $id = $this->request->post("id");
                                                                                                                  +            $add_data['real_name'] = $this->request->post("real_name");
                                                                                                                  +            $add_data['gender'] = $this->request->post("gender");
                                                                                                                  +            $add_data['birthday'] = $this->request->post("birthday");
                                                                                                                  +            $add_data['age'] = $this->request->post("age");
                                                                                                                  +            $add_data['country'] = $this->request->post("country");
                                                                                                                  +            $add_data['province'] = $this->request->post("province");
                                                                                                                  +            $add_data['city'] = $this->request->post("city");
                                                                                                                  +            $add_data['district'] = $this->request->post("district");
                                                                                                                  +            $add_data['address'] = $this->request->post("address");
                                                                                                                  +            $add_data['player_pic'] = $this->request->post("player_pic");
                                                                                                                  +            $add_data['experience'] = $this->request->post("experience");
                                                                                                                  +            $add_data['guarder_name'] = $this->request->post("guarder_name");
                                                                                                                  +            $add_data['guarder_phone'] = $this->request->post("guarder_phone");
                                                                                                                  +            $add_data['guarder_card_number'] = $this->request->post("guarder_card_number");
                                                                                                                  +            $add_data['guarder_card_type'] = $this->request->post("guarder_card_type");
                                                                                                                  +            $add_data['guarder_card_front_view'] = $this->request->post("guarder_card_front_view");
                                                                                                                  +            $add_data['guarder_card_back_view'] = $this->request->post("guarder_card_back_view");
                                                                                                                  +            $add_data['card_type'] = $this->request->post("card_type");
                                                                                                                  +            $add_data['card_number'] = $this->request->post("card_number");
                                                                                                                  +            $add_data['card_front_view'] = $this->request->post("card_front_view");
                                                                                                                  +            $add_data['card_back_view'] = $this->request->post("card_back_view");
                                                                                                                  +            $add_data['card_view_same'] = $this->request->post("card_view_same");
                                                                                                                  +            $add_data['guarder_card_view_same'] = $this->request->post("guarder_card_view_same");
                                                                                                                  +            $add_data['asfc_user'] = $this->request->post("asfc_user");
                                                                                                                  +            $add_data['membership_id'] = $this->request->post("membership_id");
                                                                                                                  +            $add_data['flight_number'] = $this->request->post("flight_number");
                                                                                                                  +            $add_data['club_members'] = $this->request->post("club_members");
                                                                                                                  +            $add_data['club_name'] = $this->request->post("club_name");
                                                                                                                  +            $add_data['duties'] = $this->request->post("duties");
                                                                                                                  +            $add_data['counselor_name'] = $this->request->post("counselor_name");
                                                                                                                  +            $add_data['post_type'] = $this->request->post("post_type");
                                                                                                                  +            $add_data['eng_name'] = $this->request->post("eng_name");
                                                                                                                  +            $add_data['fai_number'] = $this->request->post("fai_number");
                                                                                                                  +
                                                                                                                  +            $this->registeredFlyer($id, $add_data);
                                                                                                                  +        }
                                                                                                                  +        //获取用户id
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +
                                                                                                                  +        if (empty($user_id)){
                                                                                                                  +            $this->error("请先登录!",url('user/login'));
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $player_info = $this->model
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +
                                                                                                                  +        $this->view->assign('playerinfo', $player_info);
                                                                                                                  +
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function registeredflyers($ids = null)
                                                                                                                  +    {
                                                                                                                  +        $this->view->engine->layout('layout/defaultsRegister');
                                                                                                                  +        $this->view->assign('title', "注册飞手");
                                                                                                                  +
                                                                                                                  +         //获取用户id
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +
                                                                                                                  +        if (empty($user_id)){
                                                                                                                  +            $this->error("请先登录!",url('user/login'));
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $player_info = $this->model
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +        
                                                                                                                  +        if($player_info && $player_info->player_status == 9){
                                                                                                                  +            return $this->redirect('players/index');
                                                                                                                  +        }
                                                                                                                  +        
                                                                                                                  +        $this->view->assign('playerinfo', $player_info);
                                                                                                                  +        
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * @Author Soar
                                                                                                                  +     * @DESC 注册飞手
                                                                                                                  +     * @return json
                                                                                                                  +     */
                                                                                                                  +    public function registeredFlyer($id, $add_data)
                                                                                                                  +    {
                                                                                                                  +        // var_dump(123);exit;
                                                                                                                  +        $post_type = $add_data['post_type'];
                                                                                                                  +        unset($add_data['post_type']);
                                                                                                                  +
                                                                                                                  +        $validate = [
                                                                                                                  +            ["real_name", "require", '姓名不可为空!'],
                                                                                                                  +            ["gender", "require", '请选择性别!'],
                                                                                                                  +            ["birthday", "require", '出生日期不可为空!'],
                                                                                                                  +            ["country", "require", '国籍不可为空!'],
                                                                                                                  +            ["address", "require", '详细地址不可为空!'],
                                                                                                                  +            ["player_pic", "require", '请上传证件照!'],
                                                                                                                  +            ['card_front_view', 'require', "请上传正面证件照片"],
                                                                                                                  +            // ['card_back_view', 'require', "请上传{$add_data['card_type']}证件照片"],
                                                                                                                  +            ['card_number', 'require', "请输入{$add_data['card_type']}证件号码"]
                                                                                                                  +        ];
                                                                                                                  +
                                                                                                                  +        if ($add_data['country'] == "中国 China"){
                                                                                                                  +            array_push(
                                                                                                                  +                $validate,
                                                                                                                  +                ['province', 'require', "请选择省份"],
                                                                                                                  +                ['city', 'require', "请选择市"],
                                                                                                                  +                ['district', 'require', "请选择区/县"]
                                                                                                                  +            );
                                                                                                                  +        }
                                                                                                                  +        // 如果是 ASFC 获取会员编号
                                                                                                                  +        if ($add_data['asfc_user'] == \app\common\model\Players::ASFC_VIP) {
                                                                                                                  +            array_push(
                                                                                                                  +                $validate,
                                                                                                                  +                ['membership_id', 'require', '请填写 ASFC会员 证号!']
                                                                                                                  +            );
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +        if ($add_data['club_members'] == \app\common\model\Players::CLUP_USER) {
                                                                                                                  +            array_push(
                                                                                                                  +                $validate,
                                                                                                                  +                ['club_name', 'require', '请填写俱乐部名称!'],
                                                                                                                  +                ['duties', 'require', '请填写所在俱乐部职务!'],
                                                                                                                  +            );
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        if ($add_data["country"] == "中国 China" && $add_data['age'] < \app\common\model\Players::ADULT) {
                                                                                                                  +            //监护人
                                                                                                                  +            array_push(
                                                                                                                  +                $validate,
                                                                                                                  +                ['guarder_name', 'require', '请输入监护人姓名!'],
                                                                                                                  +                ['guarder_phone', 'require', '请输入监护人手机号'],
                                                                                                                  +                ['guarder_card_type', 'require', '请选择监护人证件类型'],
                                                                                                                  +                ['guarder_card_number', 'require', "请输入监护人{$add_data['guarder_card_type']}证件号码"],
                                                                                                                  +               ['guarder_card_front_view', 'require', "请上传监护人正面证件照片"],
                                                                                                                  +//                ['guarder_card_back_view', 'require', "请上传监护人{$add_data['guarder_card_type']}证件照片"]
                                                                                                                  +            );
                                                                                                                  +
                                                                                                                  +            //护照特殊判断
                                                                                                                  +            if ($add_data['guarder_card_type'] == \app\common\model\Players::CARD_TYPE[0]){
                                                                                                                  +                array_push(
                                                                                                                  +                    $validate,
                                                                                                                  +                    ['guarder_card_view_same', 'require', "请选择人像页类型"]
                                                                                                                  +                );
                                                                                                                  +            }
                                                                                                                  +            //排除护照单页的情况,背面必传
                                                                                                                  +            if (!($add_data['guarder_card_type'] == \app\common\model\Players::CARD_TYPE[1] && isset($add_data["guarder_card_view_same"]) && $add_data["guarder_card_view_same"] == 2)) {
                                                                                                                  +
                                                                                                                  +                if ($add_data['guarder_card_type'] == \app\common\model\Players::CARD_TYPE[1]){
                                                                                                                  +                    array_push(
                                                                                                                  +                        $validate,
                                                                                                                  +                        ['guarder_card_back_view', 'require', "请上传监护人签名页照片"]
                                                                                                                  +                    );
                                                                                                                  +                }
                                                                                                                  +
                                                                                                                  +                array_push(
                                                                                                                  +                    $validate,
                                                                                                                  +                    ['card_back_view', 'require', "请上传反面证件照片"],
                                                                                                                  +                    ['guarder_card_back_view', 'require', "请上传监护人反面证件照片"]
                                                                                                                  +                );
                                                                                                                  +
                                                                                                                  +            }
                                                                                                                  +
                                                                                                                  +            //比较麻烦的判断
                                                                                                                  +            if ($add_data["guarder_card_type"] == \app\common\model\Players::CARD_TYPE[1] && $add_data["guarder_card_view_same"] == 2) {
                                                                                                                  +                $add_data["guarder_card_back_view"] = null;
                                                                                                                  +            } elseif ($add_data["guarder_card_type"] != "护照") {
                                                                                                                  +                $add_data["guarder_card_view_same"] = 1;
                                                                                                                  +            }
                                                                                                                  +        }else{
                                                                                                                  +            array_push(
                                                                                                                  +                $validate,
                                                                                                                  +                ['card_type', 'require', '请选择证件类型!']
                                                                                                                  +            );
                                                                                                                  +
                                                                                                                  +            //护照特殊判断
                                                                                                                  +            if ($add_data['card_type'] == \app\common\model\Players::CARD_TYPE[0]){
                                                                                                                  +                array_push(
                                                                                                                  +                    $validate,
                                                                                                                  +                    ['card_view_same', 'require', "请选择人像页类型"]
                                                                                                                  +                );
                                                                                                                  +            }
                                                                                                                  +
                                                                                                                  +            //排除护照单页的情况,背面必传
                                                                                                                  +            if (!($add_data['card_type'] == \app\common\model\Players::CARD_TYPE[1] && isset($add_data["card_view_same"]) && $add_data["card_view_same"] == 2)) {
                                                                                                                  +
                                                                                                                  +                if ($add_data['card_type'] == \app\common\model\Players::CARD_TYPE[1]){
                                                                                                                  +                    array_push(
                                                                                                                  +                        $validate,
                                                                                                                  +                        ['card_back_view', 'require', "请上传{$add_data['guarder_card_type']}签名页照片"]
                                                                                                                  +                    );
                                                                                                                  +                }
                                                                                                                  +                array_push(
                                                                                                                  +                    $validate,
                                                                                                                  +                    ['card_back_view', 'require', "请上传反面证件照片"]
                                                                                                                  +                );
                                                                                                                  +            }
                                                                                                                  +
                                                                                                                  +            //比较麻烦的判断
                                                                                                                  +            if ($add_data["card_type"] == "护照" && $add_data["card_view_same"] == 2) {
                                                                                                                  +                $add_data["card_back_view"] = null;
                                                                                                                  +            } elseif ($add_data["card_type"] != "护照") {
                                                                                                                  +                $add_data["card_view_same"] = 1;
                                                                                                                  +            }
                                                                                                                  +        }
                                                                                                                  +        $validate = new \think\Validate($validate);
                                                                                                                  +
                                                                                                                  +        if (!$validate->check($add_data)){
                                                                                                                  +            $this->error($validate->getError());
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $playersModel = new \app\common\model\Players();
                                                                                                                  +        $playersinfo = $playersModel->getPlayers($this->auth->id);
                                                                                                                  +
                                                                                                                  +        try {
                                                                                                                  +            $add_data['member_id'] = $this->auth->id;
                                                                                                                  +            $add_data['club_id'] = 0;
                                                                                                                  +            $add_data['phone'] = $this->auth->mobile;
                                                                                                                  +            $add_data['id'] = $id;
                                                                                                                  +            $add_data['player_status'] = \app\common\model\Players::PLAYERS_REAPPLY;
                                                                                                                  +            $add_data['updated_at'] = date("Y-m-d H:i:s", time());
                                                                                                                  +            $playersinfo->save($add_data);
                                                                                                                  +
                                                                                                                  +        }catch (Exception $exception){
                                                                                                                  +            $this->error($exception->getMessage());
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        if (!empty($post_type) && $post_type == "json") {
                                                                                                                  +            return json(['code' => 1, 'data' => [], 'msg' => "提交成功"]);
                                                                                                                  +        }
                                                                                                                  +        //模板消息通知
                                                                                                                  +        $renzheng = new Renzheng();
                                                                                                                  +        $res = $renzheng->where('id',1)->find();
                                                                                                                  +        if(!empty($res)) $res = $res->toArray();
                                                                                                                  +        if($res['status'] == 1){
                                                                                                                  +            $wxmb = new Wxmb();
                                                                                                                  +            $wxmb->renzhengpush($add_data,$res['openid']);
                                                                                                                  +        }
                                                                                                                  +        $this->success("提交成功!", url('players/index'));
                                                                                                                  +
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    
                                                                                                                  +    //飞手加入俱乐部
                                                                                                                  +    public function joinclub(){
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +        // $club_model = new Club();
                                                                                                                  +        // $club_playercheck = new ClubPlayercheck();
                                                                                                                  +        $player_info = $this->model
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +        if(empty($player_info)){
                                                                                                                  +            $this->error('请先成为飞手');
                                                                                                                  +        }
                                                                                                                  +        $this->view->assign('title', "查找队伍");
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +    
                                                                                                                  +    public function confirmed_player(){
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +        $player_info = $this->model->where("member_id", $user_id)->find();
                                                                                                                  +
                                                                                                                  +        if (empty($player_info)){
                                                                                                                  +            $this->error(__("请先申请成为飞手!"), url('players/registeredflyers'))   ;
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        if ($player_info['player_status'] != 9){
                                                                                                                  +            $this->error(__("您还不是正式飞手!"), url('user/index'));
                                                                                                                  +        }   
                                                                                                                  +    }
                                                                                                                  +    //我的俱乐部
                                                                                                                  +    public function myclub(){
                                                                                                                  +         $this->view->assign('title', "我的队伍");
                                                                                                                  +        $this->confirmed_player();
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +        $club_model = new Club();
                                                                                                                  +        $club_playercheck = new ClubPlayercheck();
                                                                                                                  +        $clubinvate = new ClubInvate();
                                                                                                                  +        $player_info = $this->model
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +        $clubinvate_res = $clubinvate->where('player_id',$player_info['id'])->where('status',6)->where('deletetime',null)->find();
                                                                                                                  +        if(!empty($clubinvate_res)) {
                                                                                                                  +            $club_info = $club_model->where('id',$clubinvate_res['club_id'])->find();
                                                                                                                  +            $this->view->assign('club_info', $club_info);
                                                                                                                  +            $this->view->assign('player_status', 2);  //未加入公会已参加俱乐部 
                                                                                                                  +            
                                                                                                                  +        }else{
                                                                                                                  +            $invate_res1 = $clubinvate->where('player_id',$player_info['id'])->where('status',9)->where('deletetime',null)->find();
                                                                                                                  +            $invate_res2 = $clubinvate->where('player_id',$player_info['id'])->where('status',8)->where('deletetime',null)->find();
                                                                                                                  +            if(!empty($invate_res1) || !empty($invate_res2)){
                                                                                                                  +                if(!empty($invate_res1)){
                                                                                                                  +                $invate_res1 = $invate_res1->toArray();
                                                                                                                  +                $club_info1 = $club_model->where('id',$invate_res1['club_id'])->find();
                                                                                                                  +                $this->view->assign('club_name', $club_info1['name']);  
                                                                                                                  +                $this->view->assign('player_status', 4);  //待俱乐部上传协议
                                                                                                                  +                }
                                                                                                                  +                if(!empty($invate_res2)){
                                                                                                                  +                $invate_res2 = $invate_res2->toArray();
                                                                                                                  +                $club_info2 = $club_model->where('id',$invate_res2['club_id'])->find();
                                                                                                                  +                $this->view->assign('club_name', $club_info2['name']);  
                                                                                                                  +                $this->view->assign('player_status', 5);  //俱乐部重新上传协议
                                                                                                                  +                }
                                                                                                                  +
                                                                                                                  +            }else{
                                                                                                                  +                $invate_res = $clubinvate->where('player_id',$player_info['id'])->where('status',5)->where('deletetime',null)->find();
                                                                                                                  +                if(!empty($invate_res)){
                                                                                                                  +                    $this->view->assign('player_status', 3);  //待平台审核
                                                                                                                  +                }else{
                                                                                                                  +                    if($player_info['is_sociaty'] == 1){  
                                                                                                                  +                    // var_dump(123);exit;
                                                                                                                  +    
                                                                                                                  +                        $this->view->assign('player_status', 1);  //加入公会未参加俱乐部
                                                                                                                  +                        //
                                                                                                                  +                    }elseif($player_info['is_sociaty'] == ''){ 
                                                                                                                  +                        $this->view->assign('player_status', 0);  //未加入公会未参加俱乐部 
                                                                                                                  +                    }
                                                                                                                  +                }
                                                                                                                  +            }
                                                                                                                  +            
                                                                                                                  +
                                                                                                                  +        }
                                                                                                                  +        // $res = $club_playercheck->where('player_id',$player_info['id'])->where('status',2)->find();
                                                                                                                  +        // if(empty($res)) $this->error('请先加入俱乐部');
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +        
                                                                                                                  +    }
                                                                                                                  +    
                                                                                                                  +    public function myinvate(){
                                                                                                                  +        $this->confirmed_player();
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +        $club_model = new Club();
                                                                                                                  +        $club_playercheck = new ClubPlayercheck();
                                                                                                                  +        $clubinvate = new ClubInvate();
                                                                                                                  +        $page = $this->request->param('page');
                                                                                                                  +        $player_info = $this->model
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +        $invate_res = $clubinvate->where('player_id',$player_info['id'])->where('deletetime',null)->where('grass',null)->select();
                                                                                                                  +        $invate_res1 = $clubinvate->where('player_id',$player_info['id'])->where('deletetime',null)->where('grass',null)->page($page,10)->select();
                                                                                                                  +        foreach ($invate_res1 as &$val){
                                                                                                                  +            $club_res = $club_model->where('id',$val['club_id'])->find();
                                                                                                                  +            $val['club_name'] = $club_res['name'];
                                                                                                                  +            $val['updatetime'] = date('Y-m-d H:i:s',$val['updatetime']);
                                                                                                                  +        }        
                                                                                                                  +        $data['total'] = count($invate_res);
                                                                                                                  +        $data['data'] = $invate_res1;
                                                                                                                  +        return json($data);  
                                                                                                                  +    }
                                                                                                                  +    
                                                                                                                  +    public function agree_invate(){
                                                                                                                  +        $this->confirmed_player();
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +        $club_model = new Club();
                                                                                                                  +        $club_playercheck = new ClubPlayercheck();
                                                                                                                  +        $clubinvate = new ClubInvate();
                                                                                                                  +        $ClubThirdOauth = new ClubThirdOauth(); 
                                                                                                                  +        $Wxmb = new Wxmb();        
                                                                                                                  +        $invate_id = $this->request->param('invate_id');
                                                                                                                  +        // var_dump($invate_id);exit;
                                                                                                                  +        $player_info = $this->model
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();    
                                                                                                                  +            // var_dump($invate_id);exit;
                                                                                                                  +        $exist_invate = $clubinvate->where('id',$invate_id)->where('player_id',$player_info['id'])->find();
                                                                                                                  +        if(empty($exist_invate)) $this->error('异常请求',url('players/index'));
                                                                                                                  +        $jiaru_res = $clubinvate->where('player_id',$player_info['id'])->where('status',6)->where('deletetime',null)->select();
                                                                                                                  +        if(!empty($jiaru_res)) $this->error('您已加入俱乐部',url('players/index'));
                                                                                                                  +        $invate_res = $clubinvate->where('player_id',$player_info['id'])->where('deletetime',null)->select();
                                                                                                                  +        // 启动事务
                                                                                                                  +        // Db::startTrans();
                                                                                                                  +        // try{
                                                                                                                  +            // var_dump($invate_id);exit;
                                                                                                                  +            foreach ($invate_res as $val){
                                                                                                                  +                
                                                                                                                  +                if($val['id'] != $invate_id){
                                                                                                                  +                    // var_dump($val['id']);exit;
                                                                                                                  +                $clubinvate->save([
                                                                                                                  +                    'deletetime' => time(),
                                                                                                                  +                    // 'updatetime' => time(),
                                                                                                                  +                    ],['id' => $val['id']]);
                                                                                                                  +                }
                                                                                                                  +            }
                                                                                                                  +            // var_dump($invate_res);exit;
                                                                                                                  +            $res = $clubinvate->save([
                                                                                                                  +                'status' => 5,
                                                                                                                  +                // 'status' => 9,
                                                                                                                  +                // 'createtime' => time(),
                                                                                                                  +                'updatetime' => time(),
                                                                                                                  +                ],['id' => $invate_id]);
                                                                                                                  +                // var_dump($res);exit;
                                                                                                                  +                
                                                                                                                  +            $res1 = $this->model->save([
                                                                                                                  +                'is_sociaty' => null,
                                                                                                                  +                'updated_at' => date('Y-m-d H:i:s',time()),
                                                                                                                  +                ],['id'=>$player_info['id']]);
                                                                                                                  +                // var_dump($res1);exit;
                                                                                                                  +            
                                                                                                                  +        // } catch (\Exception $e) {
                                                                                                                  +        //     // 回滚事务
                                                                                                                  +        //     // var_dump($e);exit;
                                                                                                                  +        //     Db::rollback();
                                                                                                                  +        // }
                                                                                                                  +        $club_info = $club_model->where('id',$exist_invate['club_id'])->find();
                                                                                                                  +        if(!empty($club_info)){
                                                                                                                  +            $oauth_res = $ClubThirdOauth->where('user_id',$club_info['user_id'])->find();
                                                                                                                  +            if(!empty($oauth_res)){
                                                                                                                  +                $oauth_res = $oauth_res->toArray();
                                                                                                                  +                $push_arr = array('title'=>'飞手已同意邀请','username'=> $player_info['real_name'],'type'=>'飞手确认');
                                                                                                                  +                $Wxmb->leaguepush($push_arr,$oauth_res['openid']);
                                                                                                                  +            }                
                                                                                                                  +        }
                                                                                                                  +        
                                                                                                                  +         $this->success('同意成功',url('players/index'));       
                                                                                                                  +    }
                                                                                                                  +    
                                                                                                                  +    public function reject_invate(){
                                                                                                                  +        $this->confirmed_player();
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +        $club_model = new Club();
                                                                                                                  +        $club_playercheck = new ClubPlayercheck();
                                                                                                                  +        $clubinvate = new ClubInvate();      
                                                                                                                  +        $ClubThirdOauth = new ClubThirdOauth(); 
                                                                                                                  +        $Wxmb = new Wxmb();              
                                                                                                                  +        $invate_id = $this->request->param('invate_id');     
                                                                                                                  +        $player_info = $this->model
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();      
                                                                                                                  +        $exist_invate = $clubinvate->where('id',$invate_id)->where('player_id',$player_info['id'])->find();
                                                                                                                  +        if(empty($exist_invate)) $this->error('异常请求',url('players/index'));            
                                                                                                                  +        $jiaru_res = $clubinvate->where('player_id',$player_info['id'])->where('status',6)->where('deletetime',null)->select();
                                                                                                                  +        if(!empty($jiaru_res)) $this->error('您已加入俱乐部');        
                                                                                                                  +        $clubinvate->save([
                                                                                                                  +            'status' => 3,
                                                                                                                  +            // 'createtime' => time(),
                                                                                                                  +            'grass' => 1,
                                                                                                                  +            ],['id' => $invate_id]);
                                                                                                                  +            
                                                                                                                  +        //模板消息通知
                                                                                                                  +        $club_info = $club_model->where('id',$exist_invate['club_id'])->find();
                                                                                                                  +        if(!empty($club_info)){
                                                                                                                  +            $oauth_res = $ClubThirdOauth->where('user_id',$club_info['user_id'])->find();
                                                                                                                  +            if(!empty($oauth_res)){
                                                                                                                  +                $oauth_res = $oauth_res->toArray();
                                                                                                                  +                $push_arr = array('title'=>'飞手已拒绝邀请','username'=> $player_info['real_name'],'type'=>'飞手确认');
                                                                                                                  +                $Wxmb->leaguepush($push_arr,$oauth_res['openid']);
                                                                                                                  +            }                
                                                                                                                  +        }                  
                                                                                                                  +        $this->success('拒绝成功');     
                                                                                                                  +    }
                                                                                                                  +    
                                                                                                                  +    public function all_club(){
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
                                                                                                                  +        header('Access-Control-Allow-Methods: GET,POST');        
                                                                                                                  +        $this->confirmed_player();
                                                                                                                  +        $club_model = new Club();
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +        $page = $this->request->param('page');
                                                                                                                  +        $res = $club_model->field('id,user_id,name,province,city,district')->where('status',9)->page($page,10)->orderRaw('rand()')->select();
                                                                                                                  +        $res1 = $club_model->field('id,user_id,name,province,city,district')->where('status',9)->select();
                                                                                                                  +        $data = array('code' => 1, 'total'=>count($res1),'data'=>$res,'user_data'=>$user_id);
                                                                                                                  +        return json($data);
                                                                                                                  +        // var_dump($res);exit;
                                                                                                                  +    }
                                                                                                                  +    
                                                                                                                  +    public function search_club(){
                                                                                                                  +        $this->confirmed_player();
                                                                                                                  +        $club_model = new Club();
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +        $keyword = $this->request->param('keyword');
                                                                                                                  +        $page = $this->request->param('page');
                                                                                                                  +        $res = $club_model->field('id,user_id,name,province,city,district')->where('status',9)->where('name','like','%'.$keyword.'%')->select();
                                                                                                                  +        $res1 = $club_model->field('id,user_id,name,province,city,district')->where('status',9)->where('name','like','%'.$keyword.'%')->page($page,10)->select();
                                                                                                                  +        $data = array('code' => 1, 'total'=>count($res),'data'=>$res ,'user_data'=>$user_id);
                                                                                                                  +        return json($data);
                                                                                                                  +    }    
                                                                                                                  +    
                                                                                                                  +}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  diff --git a/application/index/controller/Ranking.php b/application/index/controller/Ranking.php
                                                                                                                  new file mode 100644
                                                                                                                  index 0000000..9798316
                                                                                                                  --- /dev/null
                                                                                                                  +++ b/application/index/controller/Ranking.php
                                                                                                                  @@ -0,0 +1,465 @@
                                                                                                                  +request->param('course');
                                                                                                                  +        $match_id = $this->request->param('match_id');
                                                                                                                  +
                                                                                                                  +        $rankingService = new RankingService();
                                                                                                                  +        $collection['thirty_two'] = $rankingService->getRank(32, $match_id);
                                                                                                                  +        $collection['sixteen'] = $rankingService->getRank(16, $match_id);
                                                                                                                  +        $collection['eight'] = $rankingService->getRank(8, $match_id);
                                                                                                                  +        $collection['four'] = $rankingService->getRank(4, $match_id);
                                                                                                                  +        //$collection = $rankingService->getRank($course);
                                                                                                                  +
                                                                                                                  +        if (!empty($collection)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $collection;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 获取所有选手名单
                                                                                                                  +     * @Author:Soar
                                                                                                                  +     * @Time:2023/11/23 9:41
                                                                                                                  +     * @return \think\response\Json
                                                                                                                  +     */
                                                                                                                  +    public function getRankingAll()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        // $course = $this->request->param('course');
                                                                                                                  +        $match_id = $this->request->param('match_id');
                                                                                                                  +        $rankingService = new RankingService();
                                                                                                                  +
                                                                                                                  +        $collection['thirty_two'] = $rankingService->getRoundAllPlayer(32, $match_id);
                                                                                                                  +        $collection['sixteen'] = $rankingService->getRoundAllPlayer(16, $match_id);
                                                                                                                  +        $collection['eight'] = $rankingService->getRoundAllPlayer(8, $match_id);
                                                                                                                  +        $collection['four'] = $rankingService->getRoundAllPlayer(4, $match_id);
                                                                                                                  +        $collection['finals'] = $rankingService->getRoundAllPlayer(1, $match_id);
                                                                                                                  +
                                                                                                                  +        if (!empty($collection)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $collection;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * @Created by PhpStorm.
                                                                                                                  +     * @Author:Soar
                                                                                                                  +     * @Time:2023/11/23 9:41
                                                                                                                  +     * @return \think\response\Json
                                                                                                                  +     */
                                                                                                                  +    #[Deprecated]
                                                                                                                  +    public function getRankToSemiFinals()
                                                                                                                  +    {
                                                                                                                  +        $match_id = $this->request->param("match_id");
                                                                                                                  +
                                                                                                                  +        if (empty($match_id)) {
                                                                                                                  +            $this->error("赛事id不可为空!");
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $rankingService = new RankingService();
                                                                                                                  +        $row = $rankingService->getSemiFinals();
                                                                                                                  +
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 获取分组选手信息以及成绩列表
                                                                                                                  +     * @Author:Soar
                                                                                                                  +     * @Time:2023/11/20 16:29
                                                                                                                  +     * @return \think\response\Json
                                                                                                                  +     * @throws \think\db\exception\DataNotFoundException
                                                                                                                  +     * @throws \think\db\exception\ModelNotFoundException
                                                                                                                  +     * @throws \think\exception\DbException
                                                                                                                  +     */
                                                                                                                  +    #//[Deprecated]
                                                                                                                  +    public function getRankForGroup()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $post['group'] = $this->request->param("group", "A");
                                                                                                                  +        $post['match_id'] = $this->request->param("match_id", 0);
                                                                                                                  +        $post['course'] = $this->request->param("course", 32);
                                                                                                                  +
                                                                                                                  +        if ($post['match_id'] != 0) {
                                                                                                                  +            $rankingService = new RankingService();
                                                                                                                  +            $row = $rankingService->getRankGroup($post);
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 根据赛事ID 获取决赛的所有成绩
                                                                                                                  +     * @Author:Soar
                                                                                                                  +     * @Time:2023/11/23 11:09
                                                                                                                  +     * @return \think\response\Json
                                                                                                                  +     */
                                                                                                                  +    public function getFinalsList()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $post['match_id'] = $this->request->param("match_id", 0);
                                                                                                                  +        $post['name'] = $this->request->param("name", 0);
                                                                                                                  +
                                                                                                                  +        if ($post['match_id'] != 0) {
                                                                                                                  +            $rankingService = new RankingService();
                                                                                                                  +            $row = $rankingService->getRankFinals($post['match_id'],$post['name']);
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 查询单个飞手的赛事成绩
                                                                                                                  +     * @Author:Soar
                                                                                                                  +     * @Time:2023/11/23 11:07
                                                                                                                  +     * @return \think\response\Json
                                                                                                                  +     */
                                                                                                                  +    public function getPlayerRanking()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $post['match_id'] = $this->request->param("match_id");
                                                                                                                  +        $post['player_id'] = $this->request->param("player_id");
                                                                                                                  +
                                                                                                                  +        if (!empty($post['match_id']) && $post['player_id']) {
                                                                                                                  +            $rankingService = new RankingService();
                                                                                                                  +            $row = $rankingService->getPlayerRanking($post['match_id'], $post['player_id']);
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 根据选手id、赛事id、赛程 获取所有轮次信息
                                                                                                                  +     * @Author:Soar
                                                                                                                  +     * @Time:2023/12/12 16:53
                                                                                                                  +     * @return \think\response\Json
                                                                                                                  +     */
                                                                                                                  +    public function getroundPlayer()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $match_id = $this->request->param("match_id");;
                                                                                                                  +        $course = $this->request->param("course");;
                                                                                                                  +        $player_id = $this->request->param("player_id");;
                                                                                                                  +
                                                                                                                  +        if (empty($course) || empty($match_id) || empty($player_id)) {
                                                                                                                  +            $this->error("非法请求!");
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $rankingService = new RankingService();
                                                                                                                  +        $row = $rankingService->getRoundPlayer($match_id, $course, $player_id);
                                                                                                                  +
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 前台报名页面,获取所有飞手成绩
                                                                                                                  +     * @Author:Soar
                                                                                                                  +     * @Time:2023/12/18 11:15
                                                                                                                  +     * @return void
                                                                                                                  +     */
                                                                                                                  +    public function getIndexRanking()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $match_id = $this->request->param("match_id");;
                                                                                                                  +
                                                                                                                  +        if (empty($match_id)) {
                                                                                                                  +            $this->error("非法请求!");
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $rankingService = new RankingService();
                                                                                                                  +        $row['thirty_two'] = $rankingService->getIndexRanking($match_id, 32);
                                                                                                                  +        $row['sixteen'] = $rankingService->getIndexRanking($match_id, 16);
                                                                                                                  +        $row['eight'] = $rankingService->getIndexRanking($match_id, 8);
                                                                                                                  +        $row['four'] = $rankingService->getIndexRanking($match_id, 4);
                                                                                                                  +
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +        $this->success();
                                                                                                                  +
                                                                                                                  +    }
                                                                                                                  +    
                                                                                                                  +    public function getIndexRankingScore()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $match_id = $this->request->param("match_id");;
                                                                                                                  +
                                                                                                                  +        if (empty($match_id)) {
                                                                                                                  +            $this->error("非法请求!");
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $rankingService = new RankingService();
                                                                                                                  +        $row['thirty_two'] = $rankingService->getIndexRankingScore($match_id, 32);
                                                                                                                  +        $row['sixteen'] = $rankingService->getIndexRankingScore($match_id, 16);
                                                                                                                  +        $row['eight'] = $rankingService->getIndexRankingScore($match_id, 8);
                                                                                                                  +        $row['four'] = $rankingService->getIndexRankingScore($match_id, 4);
                                                                                                                  +
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +        $this->success();
                                                                                                                  +
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 详情页查询单个飞手最优成绩
                                                                                                                  +     * @Author:Soar
                                                                                                                  +     * @Time:2023/12/21 15:52
                                                                                                                  +     * @return \think\response\Json
                                                                                                                  +     */
                                                                                                                  +    public function getIndexPlayerRanking()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $post['match_id'] = $this->request->param("match_id");
                                                                                                                  +        $post['player_id'] = $this->request->param("player_id");
                                                                                                                  +
                                                                                                                  +        if (!empty($post['match_id']) && $post['player_id']) {
                                                                                                                  +            $rankingService = new RankingService();
                                                                                                                  +            $row = $rankingService->getIndexPlayerRanking($post['match_id'], $post['player_id']);
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function getCertificate()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $post['match_id'] = $this->request->param("match_id");
                                                                                                                  +        $post['player_id'] = $this->request->param("player_id");
                                                                                                                  +        if (!empty($post['match_id']) && $post['player_id']) {
                                                                                                                  +
                                                                                                                  +            $rankingService = new RankingService();
                                                                                                                  +            $certificateService = new CertificateService();
                                                                                                                  +            $players = new \app\common\model\Players();
                                                                                                                  +            $player_info = $players->alias("a")
                                                                                                                  +                ->join('user users', 'users.id = a.member_id', 'LEFT')
                                                                                                                  +                ->where("a.id", $post['player_id'])
                                                                                                                  +                ->find();
                                                                                                                  +            if (empty($players)) {
                                                                                                                  +                $this->error("数据错误");
                                                                                                                  +            }
                                                                                                                  +            $player_id = $player_info->member_number;
                                                                                                                  +
                                                                                                                  +            $row['thirty_two'] = $rankingService->getOneRanking($post['match_id'], $player_id, 32);
                                                                                                                  +            if (empty($row['thirty_two'])) {
                                                                                                                  +                $this->error("数据错误或者比赛暂未开始!");
                                                                                                                  +            }
                                                                                                                  +            $row['sixteen'] = $rankingService->getOneRanking($post['match_id'], $player_id, 16);
                                                                                                                  +            $row['eight'] = $rankingService->getOneRanking($post['match_id'], $player_id, 8);
                                                                                                                  +            $row['four'] = $rankingService->getOneRanking($post['match_id'], $player_id, 4);
                                                                                                                  +            $row['finals'] = $rankingService->getFinalsIntegral($post['match_id'], $player_id);
                                                                                                                  +            $row['player_info'] = $rankingService->getPlayerInfo($player_id);
                                                                                                                  +            $row['bast'] = $rankingService->getPlayerBast($post['match_id'], $player_id, $row['player_info']);
                                                                                                                  +
                                                                                                                  +            $addonproduct = db("cms_addonproduct");
                                                                                                                  +            $row['endtime'] = $addonproduct->where("id", $post['match_id'])->field("match_end_time, match_start_time, stime, etime, course_poster, is_open_course")->find();
                                                                                                                  +            // var_dump($row);exit;
                                                                                                                  +            if (!empty($row['endtime']['course_poster'])) {
                                                                                                                  +                $row['endtime']['course_poster'] = config("upload.cdnurl") ? config("upload.cdnurl").$row['endtime']['course_poster'] : config("upload.uploadurl").$row['endtime']['course_poster'];
                                                                                                                  +            }
                                                                                                                  +
                                                                                                                  +            if ($row['bast']['sort'] != 0) {
                                                                                                                  +                $row['certificate_number']['certificate_number'] = $certificateService->getCertificate(
                                                                                                                  +                    [
                                                                                                                  +                        'match_id' => $post['match_id'],
                                                                                                                  +                        'player_id' => $post['player_id'],
                                                                                                                  +                        'member_id' => $row['player_info']['member_id'],
                                                                                                                  +                        'club_id' => 0,
                                                                                                                  +                    ]
                                                                                                                  +                );
                                                                                                                  +            } else {
                                                                                                                  +                $row['certificate_number']['certificate_number'] = null;
                                                                                                                  +            }
                                                                                                                  +        }
                                                                                                                  +        if (!empty($row)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $row;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +}
                                                                                                                  \ No newline at end of file
                                                                                                                  diff --git a/application/index/controller/Schedule.php b/application/index/controller/Schedule.php
                                                                                                                  new file mode 100644
                                                                                                                  index 0000000..47ec872
                                                                                                                  --- /dev/null
                                                                                                                  +++ b/application/index/controller/Schedule.php
                                                                                                                  @@ -0,0 +1,54 @@
                                                                                                                  +scheduleService = new ScheduleService();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function getSchedule()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');        
                                                                                                                  +        $param['match_id'] = $this->request->param('match_id');
                                                                                                                  +
                                                                                                                  +        if (!$param['match_id']) {
                                                                                                                  +            $this->error(__('赛事id不可为空!'));
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $matchSchedules = $this->scheduleService->getByWhere($param);
                                                                                                                  +
                                                                                                                  +        if (@$matchSchedules) {
                                                                                                                  +            $result['code'] = 1;
                                                                                                                  +            $result['data'] = $matchSchedules;
                                                                                                                  +            $result['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $result['code'] = 1;
                                                                                                                  +            $result['data'] = $matchSchedules;
                                                                                                                  +            $result['msg'] = "获取失败";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($result);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +}
                                                                                                                  \ No newline at end of file
                                                                                                                  diff --git a/application/index/controller/Screen.php b/application/index/controller/Screen.php
                                                                                                                  new file mode 100644
                                                                                                                  index 0000000..99bf7f1
                                                                                                                  --- /dev/null
                                                                                                                  +++ b/application/index/controller/Screen.php
                                                                                                                  @@ -0,0 +1,76 @@
                                                                                                                  +screenService = new ScreenService();
                                                                                                                  +
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function getScreenList()
                                                                                                                  +    {
                                                                                                                  +        // 解决跨域请求接口问题
                                                                                                                  +        header('Access-Control-Allow-Origin: *');
                                                                                                                  +        header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies
                                                                                                                  +        header('Access-Control-Expose-Headers: *');  //服务器 headers 白名单,可以让客户端进行访问
                                                                                                                  +        header('Access-Control-Allow-Headers: *');
                                                                                                                  +
                                                                                                                  +        $match_id = $this->request->param('match_id');
                                                                                                                  +        if (empty($match_id)) {
                                                                                                                  +            $this->error("赛事id不可为空!");
                                                                                                                  +        }
                                                                                                                  +        $this->screenService->match_id = $match_id;
                                                                                                                  +        $screenList = $this->screenService->screenList($match_id);
                                                                                                                  +
                                                                                                                  +        if (!empty($screenList)) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = $screenList;
                                                                                                                  +            $res['msg'] = "获取成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "数据为空";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function verify_password()
                                                                                                                  +    {
                                                                                                                  +        $param['look_pwd'] = $this->request->param("password", "123456");
                                                                                                                  +        $param['match_id'] = $this->request->param("match_id", 0);
                                                                                                                  +
                                                                                                                  +        $res = $this->screenService->verifyScreen($param);
                                                                                                                  +
                                                                                                                  +        if ($res) {
                                                                                                                  +            $res['code'] = 1;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "验证成功";
                                                                                                                  +        } else {
                                                                                                                  +            $res['code'] = 0;
                                                                                                                  +            $res['data'] = [];
                                                                                                                  +            $res['msg'] = "验证失败";
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        return json($res);
                                                                                                                  +    }
                                                                                                                  +}
                                                                                                                  \ No newline at end of file
                                                                                                                  diff --git a/application/index/controller/Third.php b/application/index/controller/Third.php
                                                                                                                  new file mode 100644
                                                                                                                  index 0000000..685ff05
                                                                                                                  --- /dev/null
                                                                                                                  +++ b/application/index/controller/Third.php
                                                                                                                  @@ -0,0 +1,149 @@
                                                                                                                  +app = new Application($config);
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 准备绑定
                                                                                                                  +     */
                                                                                                                  +    public function prepare()
                                                                                                                  +    {
                                                                                                                  +        $platform = $this->request->request('platform', '');
                                                                                                                  +        if (!in_array($platform, ['wechat', 'weibo', 'qq'])) {
                                                                                                                  +            $this->error("未找到指定平台");
                                                                                                                  +        }
                                                                                                                  +        $url = $this->request->get('url', '/', 'trim');
                                                                                                                  +        if ($this->auth->id) {
                                                                                                                  +            $this->redirect(url("index/third/bind") . "?" . http_build_query(['platform' => $platform, 'url' => $url]));
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        // 授权成功后的回调
                                                                                                                  +        $userinfo = Session::get("{$platform}-userinfo");
                                                                                                                  +
                                                                                                                  +        if (!$userinfo) {
                                                                                                                  +            $this->error("操作失败,请返回重试");
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $lang = $this->request->langset();
                                                                                                                  +        $lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn';
                                                                                                                  +        Lang::load([
                                                                                                                  +            APP_PATH . 'index' . DS . 'lang' . DS . $lang . DS . 'user' . EXT,
                                                                                                                  +        ]);
                                                                                                                  +
                                                                                                                  +        $this->view->assign('userinfo', $userinfo['userinfo']);
                                                                                                                  +        $this->view->assign('platform', $platform);
                                                                                                                  +        $this->view->assign('url', $url);
                                                                                                                  +        $this->view->assign('bindurl', url("index/third/bind") . '?' . http_build_query(['platform' => $platform, 'url' => $url]));
                                                                                                                  +        $this->view->assign('captchaType', config('fastadmin.user_register_captcha'));
                                                                                                                  +        $this->view->assign('title', "账号绑定");
                                                                                                                  +
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 绑定账号
                                                                                                                  +     */
                                                                                                                  +    public function bind()
                                                                                                                  +    {
                                                                                                                  +        $platform = $this->request->request('platform', '');
                                                                                                                  +        if (!in_array($platform, ['wechat', 'weibo', 'qq'])) {
                                                                                                                  +            $this->error("未找到指定平台");
                                                                                                                  +        }
                                                                                                                  +        $url = $this->request->get('url', $this->request->server('HTTP_REFERER', '', 'trim'), 'trim');
                                                                                                                  +        if (!$platform) {
                                                                                                                  +            $this->error("参数不正确");
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        $apptype = $platform == 'wechat' ? Service::getApptype() : '';
                                                                                                                  +
                                                                                                                  +        // 授权成功后的回调
                                                                                                                  +        $userinfo = Session::get("{$platform}-userinfo");
                                                                                                                  +        if (!$userinfo) {
                                                                                                                  +            $this->redirect(addon_url('third/index/connect', [':platform' => $platform]) . '?url=' . urlencode($url));
                                                                                                                  +        }
                                                                                                                  +        $third = \addons\third\model\Third::where('user_id', $this->auth->id)
                                                                                                                  +            ->where('platform', $platform)
                                                                                                                  +            ->where(function ($query) use ($platform, $apptype) {
                                                                                                                  +                if ($platform == 'wechat') {
                                                                                                                  +                    if (in_array($apptype, ['', 'mp'])) {
                                                                                                                  +                        $query->where('apptype', 'in', ['', 'mp']);
                                                                                                                  +                    } else {
                                                                                                                  +                        $query->where('apptype', $apptype);
                                                                                                                  +                    }
                                                                                                                  +                }
                                                                                                                  +            })
                                                                                                                  +            ->find();
                                                                                                                  +        if ($third) {
                                                                                                                  +            $this->error("已绑定账号,请勿重复绑定");
                                                                                                                  +        }
                                                                                                                  +        $time = time();
                                                                                                                  +        $values = [
                                                                                                                  +            'platform'      => $platform,
                                                                                                                  +            'apptype'       => $apptype,
                                                                                                                  +            'user_id'       => $this->auth->id,
                                                                                                                  +            'openid'        => $userinfo['openid'],
                                                                                                                  +            'openname'      => $userinfo['userinfo']['nickname'] ?? '',
                                                                                                                  +            'access_token'  => $userinfo['access_token'],
                                                                                                                  +            'refresh_token' => $userinfo['refresh_token'],
                                                                                                                  +            'expires_in'    => $userinfo['expires_in'],
                                                                                                                  +            'logintime'     => $time,
                                                                                                                  +            'expiretime'    => $time + $userinfo['expires_in'],
                                                                                                                  +            'wechat_avatar'    => $userinfo['userinfo']['avatar'] ?? '',
                                                                                                                  +        ];
                                                                                                                  +        $third = \addons\third\model\Third::create($values);
                                                                                                                  +        if ($third) {
                                                                                                                  +            $this->success("账号绑定成功", $url);
                                                                                                                  +        } else {
                                                                                                                  +            $this->error("账号绑定失败,请重试", $url);
                                                                                                                  +        }
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 解绑账号
                                                                                                                  +     */
                                                                                                                  +    public function unbind()
                                                                                                                  +    {
                                                                                                                  +        $platform = $this->request->request('platform', '');
                                                                                                                  +        if (!in_array($platform, ['wechat', 'weibo', 'qq'])) {
                                                                                                                  +            $this->error("未找到指定平台");
                                                                                                                  +        }
                                                                                                                  +        $apptype = $platform == 'wechat' ? Service::getApptype() : '';
                                                                                                                  +
                                                                                                                  +        $third = \addons\third\model\Third::where('user_id', $this->auth->id)
                                                                                                                  +            ->where('platform', $platform)
                                                                                                                  +            ->where(function ($query) use ($platform, $apptype) {
                                                                                                                  +                if ($platform == 'wechat') {
                                                                                                                  +                    $query->where('apptype', $apptype);
                                                                                                                  +                }
                                                                                                                  +            })
                                                                                                                  +            ->find();
                                                                                                                  +        if (!$third) {
                                                                                                                  +            $this->error("未找到指定的账号绑定信息");
                                                                                                                  +        }
                                                                                                                  +        Session::delete("{$platform}-userinfo");
                                                                                                                  +        $third->delete();
                                                                                                                  +        $this->success("账号解绑成功");
                                                                                                                  +    }
                                                                                                                  +}
                                                                                                                  diff --git a/application/index/controller/Tiktok.php b/application/index/controller/Tiktok.php
                                                                                                                  new file mode 100644
                                                                                                                  index 0000000..df8810a
                                                                                                                  --- /dev/null
                                                                                                                  +++ b/application/index/controller/Tiktok.php
                                                                                                                  @@ -0,0 +1,42 @@
                                                                                                                  +view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +    public function archive_list(){
                                                                                                                  +        $page = $this->request->param("page");
                                                                                                                  +        // var_dump($page);exit;
                                                                                                                  +        if(empty($page)) $page = 1;
                                                                                                                  +        $archive = new Archives();
                                                                                                                  +        $total = $archive->where('channel_id','=',57)
                                                                                                                  +            ->select();
                                                                                                                  +        $res = $archive->where('channel_id','=',57)
                                                                                                                  +            ->limit(10)
                                                                                                                  +            ->page($page)
                                                                                                                  +            ->select();
                                                                                                                  +            // var_dump($total);exit;
                                                                                                                  +        $data = array('total'=>count($total),'list'=>$res);
                                                                                                                  +        return json($data);
                                                                                                                  +        //     var_dump(count($total));
                                                                                                                  +        // var_dump(json_encode($res));
                                                                                                                  +    }
                                                                                                                  +    
                                                                                                                  +    public function archive_detail(){
                                                                                                                  +        
                                                                                                                  +    }
                                                                                                                  +}
                                                                                                                  diff --git a/application/index/controller/User.php b/application/index/controller/User.php
                                                                                                                  new file mode 100644
                                                                                                                  index 0000000..a2eb780
                                                                                                                  --- /dev/null
                                                                                                                  +++ b/application/index/controller/User.php
                                                                                                                  @@ -0,0 +1,1094 @@
                                                                                                                  +auth;
                                                                                                                  +
                                                                                                                  +        if (!Config::get('fastadmin.usercenter')) {
                                                                                                                  +            $this->error(__('User center already closed'), '/');
                                                                                                                  +        }
                                                                                                                  +
                                                                                                                  +        //监听注册登录退出的事件
                                                                                                                  +        Hook::add('user_login_successed', function ($user) use ($auth) {
                                                                                                                  +            $expire = input('post.keeplogin') ? 30 * 86400 : 0;
                                                                                                                  +            Cookie::set('uid', $user->id, $expire);
                                                                                                                  +            Cookie::set('token', $auth->getToken(), $expire);
                                                                                                                  +        });
                                                                                                                  +        Hook::add('user_register_successed', function ($user) use ($auth) {
                                                                                                                  +            Cookie::set('uid', $user->id);
                                                                                                                  +            Cookie::set('token', $auth->getToken());
                                                                                                                  +        });
                                                                                                                  +        Hook::add('user_delete_successed', function ($user) use ($auth) {
                                                                                                                  +            Cookie::delete('uid');
                                                                                                                  +            Cookie::delete('token');
                                                                                                                  +        });
                                                                                                                  +        Hook::add('user_logout_successed', function ($user) use ($auth) {
                                                                                                                  +            Cookie::delete('uid');
                                                                                                                  +            Cookie::delete('token');
                                                                                                                  +        });
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    public function judge_status(){
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +        if (empty($user_id)){
                                                                                                                  +            $this->error("请先登录!");
                                                                                                                  +        }
                                                                                                                  +        $playersModel = new Players();
                                                                                                                  +        $club = new ClubModel(); 
                                                                                                                  +        $clubinvate = new ClubInvate();
                                                                                                                  +        
                                                                                                                  +        $club_info = $club
                                                                                                                  +            ->where('user_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +            
                                                                                                                  +        $player_info = $playersModel
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +        // $this->view->assign('user_type', 0);
                                                                                                                  +        $data['user_type'] = 0;
                                                                                                                  +        if(!empty($club_info)){
                                                                                                                  +            $data['user_type'] = 2;
                                                                                                                  +        }
                                                                                                                  +        
                                                                                                                  +        // $this->view->assign('user_player_status', 0);
                                                                                                                  +        $data['user_player_status'] = 0;
                                                                                                                  +        if(!empty($player_info)) {
                                                                                                                  +            // $this->view->assign('user_type', 1);
                                                                                                                  +            $data['user_type'] = 1;
                                                                                                                  +            $club_invateres = $clubinvate->where('player_id',$player_info['id'])->where('status',6)->where('deletetime',null)->find();
                                                                                                                  +            // $club_invateres2 = $clubinvate->where('player_id',$player_info['id'])->where('status',9)->where('deletetime',null)->find();
                                                                                                                  +            $club_invateres1 = $clubinvate->where('player_id',$player_info['id'])->where('status',5)->where('deletetime',null)->find();
                                                                                                                  +            if(!empty($club_invateres)) $data['user_player_status'] = 2;
                                                                                                                  +            if(!empty($club_invateres1)) $data['user_player_status'] = 3;
                                                                                                                  +            // if(!empty($club_invateres2)) $data['user_player_status'] = 4;
                                                                                                                  +        }
                                                                                                                  +        $datas['code'] = 1;
                                                                                                                  +        $datas['msg'] = 'success';
                                                                                                                  +        $datas['data'] = $data;
                                                                                                                  +        return json($datas);
                                                                                                                  +        
                                                                                                                  +        
                                                                                                                  +    }
                                                                                                                  +    
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 会员中心
                                                                                                                  +     */
                                                                                                                  +    public function index()
                                                                                                                  +    {
                                                                                                                  +        $this->view->assign('title', __('User center'));
                                                                                                                  +        $user_id = $this->auth->id;
                                                                                                                  +
                                                                                                                  +        if (empty($user_id)){
                                                                                                                  +            $this->error("请先登录!",url('user/login'));
                                                                                                                  +        }
                                                                                                                  +        $playersModel = new Players();
                                                                                                                  +        $club = new ClubModel(); 
                                                                                                                  +        $clubinvate = new ClubInvate();
                                                                                                                  +        $ClubThirdOauth = new ClubThirdOauth();         
                                                                                                                  +        
                                                                                                                  +        $club_info = $club
                                                                                                                  +            ->where('user_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +            
                                                                                                                  +        $player_info = $playersModel
                                                                                                                  +            ->where('member_id', $user_id)
                                                                                                                  +            ->find();
                                                                                                                  +        $this->view->assign('user_type', 0);
                                                                                                                  +        if(!empty($club_info)){
                                                                                                                  +            $this->view->assign('club_info', $club_info);
                                                                                                                  +            $this->view->assign('user_type', 2);
                                                                                                                  +        }
                                                                                                                  +        
                                                                                                                  +        $this->view->assign('user_player_status', 0);
                                                                                                                  +        $count_match = 0;
                                                                                                                  +        if(!empty($player_info)) {
                                                                                                                  +            $this->view->assign('user_type', 1);
                                                                                                                  +            
                                                                                                                  +            // $club_invateres = $clubinvate->where('player_id',$player_info['id'])->where('status',6)->where('deletetime',null)->find();
                                                                                                                  +            // // $club_invateres1 = $clubinvate->where('player_id',$player_info['id'])->where('status',1)->where('deletetime',null)->find();
                                                                                                                  +            // $club_invateres1 = $clubinvate->where('player_id',$player_info['id'])->where('status',5)->where('deletetime',null)->find();
                                                                                                                  +            // // var_dump($club_invateres1);exit;
                                                                                                                  +            // if(!empty($club_invateres)) $this->view->assign('user_player_status', 2);
                                                                                                                  +            // if(!empty($club_invateres1)) $this->view->assign('user_player_status', 3);
                                                                                                                  +            
                                                                                                                  +            // 如果是飞手则获取报名成功的赛事总计
                                                                                                                  +            $matchModel = new MatchContestant();
                                                                                                                  +            $count_match = $matchModel->where('player_id', $player_info['id'])->where('status', 2)->count("*");
                                                                                                                  +            
                                                                                                                  +            if($player_info['is_sociaty'] == '' ) {
                                                                                                                  +                if($player_info['first_show'] == ''){
                                                                                                                  +                    $playersModel->save([
                                                                                                                  +                        'first_show'=>1,
                                                                                                                  +                        'updated_at' => date('Y-m-d H:i:s',time()),
                                                                                                                  +                        ],['id'=>$player_info['id']]);
                                                                                                                  +                return $this->redirect('players/sociaty');        
                                                                                                                  +                }
                                                                                                                  +            }
                                                                                                                  +        }
                                                                                                                  +        
                                                                                                                  +        $userInfo = \app\admin\model\User::get($user_id);
                                                                                                                  +        $oauth_res = $ClubThirdOauth->where('user_id',$user_id)->find();
                                                                                                                  +        if(!empty($oauth_res)) {
                                                                                                                  +            $this->view->assign('is_notice', 1);
                                                                                                                  +        }else{
                                                                                                                  +            $this->view->assign('is_notice', 0);
                                                                                                                  +        }
                                                                                                                  +        $this->view->assign('count_match', $count_match);
                                                                                                                  +        $this->view->assign('playerinfo', $player_info);
                                                                                                                  +        $this->view->assign('userinfo', $userInfo);
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 注册会员
                                                                                                                  +     */
                                                                                                                  +    public function register()
                                                                                                                  +    {
                                                                                                                  +        $this->layout = 'default';
                                                                                                                  +        $this->view->engine->layout('layout/' . $this->layout);
                                                                                                                  +        $url = $this->request->request('url', '', 'trim');
                                                                                                                  +        if ($this->auth->id) {
                                                                                                                  +            $this->success(__('You\'ve logged in, do not login again'), $url ? $url : url('user/index'));
                                                                                                                  +        }
                                                                                                                  +        if ($this->request->isPost()) {
                                                                                                                  +            $username = $this->request->post('username');
                                                                                                                  +            $password = $this->request->post('password');
                                                                                                                  +            $email = $this->request->post('email');
                                                                                                                  +            $mobile = $this->request->post('mobile', '');
                                                                                                                  +            $captcha = $this->request->post('captcha');
                                                                                                                  +            $dialCode = $this->request->post("dialCode");
                                                                                                                  +            // var_dump($dialCode);exit;
                                                                                                                  +            $token = $this->request->post('__token__');
                                                                                                                  +            $rule = [
                                                                                                                  +                'username'  => 'require|length:3,30',
                                                                                                                  +                'password'  => 'require|length:6,30',
                                                                                                                  +                // 'mobile'    => 'regex:/^1\d{10}$/',
                                                                                                                  +                '__token__' => 'require|token',
                                                                                                                  +            ];
                                                                                                                  +
                                                                                                                  +            $msg = [
                                                                                                                  +                'username.require' => 'Username can not be empty',
                                                                                                                  +                'username.length'  => 'Username must be 3 to 30 characters',
                                                                                                                  +                'password.require' => 'Password can not be empty',
                                                                                                                  +                'password.length'  => 'Password must be 6 to 30 characters',
                                                                                                                  +                // 'mobile'           => 'Mobile is incorrect',
                                                                                                                  +            ];
                                                                                                                  +            $data = [
                                                                                                                  +                'username'  => $username,
                                                                                                                  +                'password'  => $password,
                                                                                                                  +                'email'     => $email,
                                                                                                                  +                'mobile'    => $mobile,
                                                                                                                  +                '__token__' => $token,
                                                                                                                  +            ];
                                                                                                                  +            //验证码
                                                                                                                  +            $captchaResult = true;
                                                                                                                  +            $captchaType = config("fastadmin.user_register_captcha");
                                                                                                                  +            
                                                                                                                  +            $check_mobile = $mobile;
                                                                                                                  +            $event = 'register';
                                                                                                                  +            //中国大陆手机号
                                                                                                                  +            if($dialCode == "+86"){
                                                                                                                  +                if (!$mobile || !\think\Validate::regex($mobile, "^1\d{10}$")) {
                                                                                                                  +                    $this->error(__('手机号不正确'));
                                                                                                                  +                }
                                                                                                                  +            }elseif(!empty($dialCode) && $dialCode != "+86"){
                                                                                                                  +                $check_mobile = $dialCode.$mobile;
                                                                                                                  +                $event = 'inter_'.$event;
                                                                                                                  +            }
                                                                                                                  +            
                                                                                                                  +            if ($captchaType) {
                                                                                                                  +                if ($captchaType == 'mobile') {
                                                                                                                  +                    $captchaResult = Sms::check($check_mobile, $captcha, $event);
                                                                                                                  +                } elseif ($captchaType == 'email') {
                                                                                                                  +                    $captchaResult = Ems::check($email, $captcha, 'register');
                                                                                                                  +                } elseif ($captchaType == 'wechat') {
                                                                                                                  +                    $captchaResult = WechatCaptcha::check($captcha, 'register');
                                                                                                                  +                } elseif ($captchaType == 'text') {
                                                                                                                  +                    $captchaResult = \think\Validate::is($captcha, 'captcha');
                                                                                                                  +                }
                                                                                                                  +            }
                                                                                                                  +            if (!$captchaResult) {
                                                                                                                  +                $this->error(__('Captcha is incorrect'));
                                                                                                                  +            }
                                                                                                                  +            $validate = new Validate($rule, $msg);
                                                                                                                  +            $result = $validate->check($data);
                                                                                                                  +            if (!$result) {
                                                                                                                  +                $this->error(__($validate->getError()), null, ['token' => $this->request->token()]);
                                                                                                                  +            }
                                                                                                                  +            if ($this->auth->register($username, $password, $email, $mobile,array('dial_code'=>$dialCode))) {
                                                                                                                  +                $this->success(__('Sign up successful'), $url ? $url : url('user/index'));
                                                                                                                  +            } else {
                                                                                                                  +                $this->error($this->auth->getError(), null, ['token' => $this->request->token()]);
                                                                                                                  +            }
                                                                                                                  +        }
                                                                                                                  +        //判断来源
                                                                                                                  +        $referer = $this->request->server('HTTP_REFERER');
                                                                                                                  +        if (!$url && (strtolower(parse_url($referer, PHP_URL_HOST)) == strtolower($this->request->host()))
                                                                                                                  +            && !preg_match("/(user\/login|user\/register|user\/logout)/i", $referer)) {
                                                                                                                  +            $url = $referer;
                                                                                                                  +        }
                                                                                                                  +        $this->view->assign('captchaType', config('fastadmin.user_register_captcha'));
                                                                                                                  +        $this->view->assign('url', $url);
                                                                                                                  +        $this->view->assign('title', __('Register'));
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 会员登录
                                                                                                                  +     */
                                                                                                                  +    public function login()
                                                                                                                  +    {
                                                                                                                  +        $this->layout = 'default';
                                                                                                                  +        $this->view->engine->layout('layout/' . $this->layout);
                                                                                                                  +        
                                                                                                                  +        $url = $this->request->request('url', '', 'trim');
                                                                                                                  +        if ($this->auth->id) {
                                                                                                                  +            $this->success(__('You\'ve logged in, do not login again'), $url ? $url : url('user/index'));
                                                                                                                  +        }
                                                                                                                  +        if ($this->request->isPost()) {
                                                                                                                  +            $account = $this->request->post('account');
                                                                                                                  +            $password = $this->request->post('password');
                                                                                                                  +            $keeplogin = (int)$this->request->post('keeplogin');
                                                                                                                  +            $token = $this->request->post('__token__');
                                                                                                                  +            $returnURL = $this->request->post('returnURL','');
                                                                                                                  +            $istype = $this->request->post('istype');
                                                                                                                  +            $mobile = $this->request->post('mobile');
                                                                                                                  +            $dialCode = $this->request->post('dialCode');
                                                                                                                  +            $code = '';
                                                                                                                  +            //手机类型登录
                                                                                                                  +            if($istype == '1'){
                                                                                                                  +                if($dialCode == '+86'){
                                                                                                                  +                    $dialCode = null;
                                                                                                                  +                }
                                                                                                                  +                $UserModel = new UserModel();
                                                                                                                  +                $user_res = $UserModel->where('mobile',$mobile)->where('dial_code',$dialCode)->find();
                                                                                                                  +                if (empty($user_res)) {
                                                                                                                  +                    $this->error('Account is incorrect');
                                                                                                                  +                    return false;
                                                                                                                  +                }
                                                                                                                  +                $account = $mobile;
                                                                                                                  +                $code = 1;
                                                                                                                  +            }
                                                                                                                  +            $rule = [
                                                                                                                  +                'account'   => 'require|length:3,50',
                                                                                                                  +                'password'  => 'require|length:6,30',
                                                                                                                  +                '__token__' => 'require|token',
                                                                                                                  +            ];
                                                                                                                  +
                                                                                                                  +            $msg = [
                                                                                                                  +                'account.require'  => 'Account can not be empty',
                                                                                                                  +                'account.length'   => 'Account must be 3 to 50 characters',
                                                                                                                  +                'password.require' => 'Password can not be empty',
                                                                                                                  +                'password.length'  => 'Password must be 6 to 30 characters',
                                                                                                                  +            ];
                                                                                                                  +            $data = [
                                                                                                                  +                'account'   => $account,
                                                                                                                  +                'password'  => $password,
                                                                                                                  +                '__token__' => $token,
                                                                                                                  +            ];
                                                                                                                  +            $validate = new Validate($rule, $msg);
                                                                                                                  +            $result = $validate->check($data);
                                                                                                                  +            if (!$result) {
                                                                                                                  +                $this->error(__($validate->getError()), null, ['token' => $this->request->token()]);
                                                                                                                  +                return false;
                                                                                                                  +            }
                                                                                                                  +            if ($this->auth->login($account, $password,$code)) {
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +                
                                                                                                                  +                $this->success(__('Logged in successful'), $url ? $url : url('user/index'));
                                                                                                                  +            } else {
                                                                                                                  +                $this->error($this->auth->getError(), null, ['token' => $this->request->token()]);
                                                                                                                  +            }
                                                                                                                  +
                                                                                                                  +        }
                                                                                                                  +        //判断来源
                                                                                                                  +        $referer = $this->request->server('HTTP_REFERER');
                                                                                                                  +        if (!$url && (strtolower(parse_url($referer, PHP_URL_HOST)) == strtolower($this->request->host()))
                                                                                                                  +            && !preg_match("/(user\/login|user\/register|user\/logout)/i", $referer)) {
                                                                                                                  +            $url = $referer;
                                                                                                                  +        }
                                                                                                                  +        $this->view->assign('url', $url);
                                                                                                                  +        $this->view->assign('title', __('Login'));
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +    //dra登录跳转
                                                                                                                  +    public function logins()
                                                                                                                  +    {
                                                                                                                  +        $this->layout = 'default';
                                                                                                                  +        $this->view->engine->layout('layout/' . $this->layout);
                                                                                                                  +        
                                                                                                                  +        $url = $this->request->request('url', '', 'trim');
                                                                                                                  +        if ($this->auth->id) {
                                                                                                                  +            $this->success(__('You\'ve logged in, do not login again'),'https://www.fpvone.cn/index/user/index.html','',1);
                                                                                                                  +        }
                                                                                                                  +        if ($this->request->isPost()) {
                                                                                                                  +            $account = $this->request->post('account');
                                                                                                                  +            $password = $this->request->post('password');
                                                                                                                  +            $keeplogin = (int)$this->request->post('keeplogin');
                                                                                                                  +            $token = $this->request->post('__token__');
                                                                                                                  +            $rule = [
                                                                                                                  +                'account'   => 'require|length:3,50',
                                                                                                                  +                'password'  => 'require|length:6,30',
                                                                                                                  +                // '__token__' => 'require|token',
                                                                                                                  +            ];
                                                                                                                  +
                                                                                                                  +            $msg = [
                                                                                                                  +                'account.require'  => 'Account can not be empty',
                                                                                                                  +                'account.length'   => 'Account must be 3 to 50 characters',
                                                                                                                  +                'password.require' => 'Password can not be empty',
                                                                                                                  +                'password.length'  => 'Password must be 6 to 30 characters',
                                                                                                                  +            ];
                                                                                                                  +            $data = [
                                                                                                                  +                'account'   => $account,
                                                                                                                  +                'password'  => $password,
                                                                                                                  +                // '__token__' => $token,
                                                                                                                  +            ];
                                                                                                                  +            $validate = new Validate($rule, $msg);
                                                                                                                  +            $result = $validate->check($data);
                                                                                                                  +            if (!$result) {
                                                                                                                  +                $this->error(__($validate->getError()), 'https://www.fpvone.cn/index/user/index.html');
                                                                                                                  +                return false;
                                                                                                                  +            }
                                                                                                                  +            if ($this->auth->login($account, $password)) {
                                                                                                                  +                $this->success(__('Logged in successful'), 'https://www.fpvone.cn/index/user/index.html','',1);
                                                                                                                  +            } else {
                                                                                                                  +                $this->error($this->auth->getError(), 'https://www.fpvone.cn/index/user/index.html');
                                                                                                                  +            }
                                                                                                                  +        }
                                                                                                                  +        //判断来源
                                                                                                                  +        $referer = $this->request->server('HTTP_REFERER');
                                                                                                                  +        if (!$url && (strtolower(parse_url($referer, PHP_URL_HOST)) == strtolower($this->request->host()))
                                                                                                                  +            && !preg_match("/(user\/login|user\/register|user\/logout)/i", $referer)) {
                                                                                                                  +            $url = $referer;
                                                                                                                  +        }
                                                                                                                  +        $this->view->assign('url', $url);
                                                                                                                  +        $this->view->assign('title', __('Login'));
                                                                                                                  +        return $this->view->fetch();
                                                                                                                  +    }
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +    /**
                                                                                                                  +     * 退出登录
                                                                                                                  +     */
                                                                                                                  +    public function logout()
                                                                                                                  +    {
                                                                                                                  +        if ($this->request->isPost()) {
                                                                                                                  +            $this->token();
                                                                                                                  +            //退出本站
                                                                                                                  +            $this->auth->logout();
                                                                                                                  +            $this->success(__('Logout successful'), url('user/index'));
                                                                                                                  +        }
                                                                                                                  +        $html = "
                                                                                                                  " . token() . "
                                                                                                                  "; + $html .= ""; + + return $html; + } + + /** + * 个人信息 + */ + public function profile() + { + $this->view->assign('title', __('Profile')); + return $this->view->fetch(); + } + + /** + * 修改密码 + */ + public function changepwd() + { + $this->layout = 'default'; + $this->view->engine->layout('layout/' . $this->layout); + + if ($this->request->isPost()) { + $oldpassword = $this->request->post("oldpassword"); + $newpassword = $this->request->post("newpassword"); + $renewpassword = $this->request->post("renewpassword"); + $token = $this->request->post('__token__'); + $rule = [ + 'oldpassword' => 'require|regex:\S{6,30}', + 'newpassword' => 'require|regex:\S{6,30}', + 'renewpassword' => 'require|regex:\S{6,30}|confirm:newpassword', + '__token__' => 'token', + ]; + + $msg = [ + 'renewpassword.confirm' => __('Password and confirm password don\'t match') + ]; + $data = [ + 'oldpassword' => $oldpassword, + 'newpassword' => $newpassword, + 'renewpassword' => $renewpassword, + '__token__' => $token, + ]; + $field = [ + 'oldpassword' => __('Old password'), + 'newpassword' => __('New password'), + 'renewpassword' => __('Renew password') + ]; + $validate = new Validate($rule, $msg, $field); + $result = $validate->check($data); + if (!$result) { + $this->error(__($validate->getError()), null, ['token' => $this->request->token()]); + return false; + } + + $ret = $this->auth->changepwd($newpassword, $oldpassword); + if ($ret) { + $this->success(__('Reset password successful'), url('user/login')); + } else { + $this->error($this->auth->getError(), null, ['token' => $this->request->token()]); + } + } + $this->view->assign('title', __('Change password')); + return $this->view->fetch(); + } + + public function attachment() + { + //设置过滤方法 + $this->request->filter(['strip_tags']); + if ($this->request->isAjax()) { + $mimetypeQuery = []; + $where = []; + $filter = $this->request->request('filter'); + $filterArr = (array)json_decode($filter, true); + if (isset($filterArr['mimetype']) && preg_match("/(\/|\,|\*)/", $filterArr['mimetype'])) { + $this->request->get(['filter' => json_encode(array_diff_key($filterArr, ['mimetype' => '']))]); + $mimetypeQuery = function ($query) use ($filterArr) { + $mimetypeArr = array_filter(explode(',', $filterArr['mimetype'])); + foreach ($mimetypeArr as $index => $item) { + $query->whereOr('mimetype', 'like', '%' . str_replace("/*", "/", $item) . '%'); + } + }; + } elseif (isset($filterArr['mimetype'])) { + $where['mimetype'] = ['like', '%' . $filterArr['mimetype'] . '%']; + } + + if (isset($filterArr['filename'])) { + $where['filename'] = ['like', '%' . $filterArr['filename'] . '%']; + } + + if (isset($filterArr['createtime'])) { + $timeArr = explode(' - ', $filterArr['createtime']); + $where['createtime'] = ['between', [strtotime($timeArr[0]), strtotime($timeArr[1])]]; + } + $search = $this->request->get('search'); + if ($search) { + $where['filename'] = ['like', '%' . $search . '%']; + } + + $model = new Attachment(); + $offset = $this->request->get("offset", 0); + $limit = $this->request->get("limit", 0); + $total = $model + ->where($where) + ->where($mimetypeQuery) + ->where('user_id', $this->auth->id) + ->order("id", "DESC") + ->count(); + + $list = $model + ->where($where) + ->where($mimetypeQuery) + ->where('user_id', $this->auth->id) + ->order("id", "DESC") + ->limit($offset, $limit) + ->select(); + $cdnurl = preg_replace("/\/(\w+)\.php$/i", '', $this->request->root()); + foreach ($list as $k => &$v) { + $v['fullurl'] = ($v['storage'] == 'local' ? $cdnurl : $this->view->config['upload']['cdnurl']) . $v['url']; + } + unset($v); + $result = array("total" => $total, "rows" => $list); + + return json($result); + } + $mimetype = $this->request->get('mimetype', ''); + $mimetype = substr($mimetype, -1) === '/' ? $mimetype . '*' : $mimetype; + $this->view->assign('mimetype', $mimetype); + $this->view->assign("mimetypeList", \app\common\model\Attachment::getMimetypeList()); + return $this->view->fetch(); + } + + public function authenticationflayers() + { + //获取用户id + $user_id = $this->auth->id; + if (empty($user_id)){ + $this->error("请先登录!",url('user/login')); + } + + $Players = new Players(); + $players_info = $Players + ->where('member_id', $user_id) + ->find(); + if (empty($players_info)){ + return $this->view->fetch('identitylist'); + } + + $this->view->assign('title', "身份认证"); + $this->view->assign('players', $players_info); + return $this->view->fetch(); + } + + + public function identitylist() + { + return $this->view->fetch(); + } + + /** + * @DESC 更新用户信息 + */ + + public function updateuser() + { + $update_data['nickname'] = $this->request->post("nick_name"); + $update_data['wechat'] = $this->request->post("vx"); + $update_data['qq'] = $this->request->post("qq"); + $update_data['avatar'] = $this->request->post("avater"); + $player_data['fai_number'] = $this->request->post("fai_number"); + $player_data['updated_at'] = date("Y-m-d H:i:s", time()); + + // $update_data['type'] = $this->request->post("type"); + // var_dump($update_data);exit; + try { + $playersModel = new Players(); + $userModel = new \app\common\model\User(); + $userModel->update($update_data, ['id' => $this->auth->id]); + // if(!empty($player_data['fai_number'])){ + // $userinfo = $userModel->where('id',$this->auth->id)->find(); + $playersModel->update($player_data, ['member_id' => $this->auth->id]); + + // } + }catch (Exception $exception){ + $this->error($exception->getMessage()); + } + + if($this->request->post("type") == 'douyin'){ + return json(array('code'=>1,'message'=>'success')); + } + + $this->success("保存成功 / Save successfully"); + } + + /** + * @Author Soar + * @DESC 注册飞手 + * @return json + */ + public function registeredFlyer(Request $request) + { + $user_id = $this->auth->id; + $club = new ClubModel(); + $club_res =$club->where('user_id',$user_id)->find(); + if(!empty($club_res)) $this->error('该账号已注册过俱乐部身份'); + // var_dump($user_id);exit; + $id = $this->request->post("id"); + $add_data['real_name'] = $this->request->post("real_name"); + $add_data['gender'] = $this->request->post("gender"); + $add_data['birthday'] = $this->request->post("birthday"); + $add_data['age'] = $this->request->post("age"); + $add_data['country'] = $this->request->post("country"); + $add_data['province'] = $this->request->post("province"); + $add_data['city'] = $this->request->post("city"); + $add_data['district'] = $this->request->post("district"); + $add_data['address'] = $this->request->post("address"); + $add_data['player_pic'] = $this->request->post("player_pic"); + $add_data['experience'] = $this->request->post("experience"); + $add_data['guarder_name'] = $this->request->post("guarder_name"); + $add_data['guarder_phone'] = $this->request->post("guarder_phone"); + $add_data['guarder_card_number'] = $this->request->post("guarder_card_number"); + $add_data['guarder_card_type'] = $this->request->post("guarder_card_type"); + $add_data['guarder_card_front_view'] = $this->request->post("guarder_card_front_view"); + $add_data['guarder_card_back_view'] = $this->request->post("guarder_card_back_view"); + $add_data['card_type'] = $this->request->post("card_type"); + $add_data['card_number'] = $this->request->post("card_number"); + $add_data['card_front_view'] = $this->request->post("card_front_view"); + $add_data['card_back_view'] = $this->request->post("card_back_view"); + $add_data['card_view_same'] = $this->request->post("card_view_same"); + $add_data['guarder_card_view_same'] = $this->request->post("guarder_card_view_same"); + $add_data['asfc_user'] = $this->request->post("asfc_user"); + $add_data['membership_id'] = $this->request->post("membership_id"); + $add_data['flight_number'] = $this->request->post("flight_number"); + $add_data['club_members'] = $this->request->post("club_members"); + $add_data['club_name'] = $this->request->post("club_name"); + $add_data['duties'] = $this->request->post("duties"); + $post_type = $this->request->post("post_type"); + $add_data['counselor_name'] = $this->request->post("counselor_name"); + $add_data['eng_name'] = $this->request->post("eng_name"); + $add_data['fai_number'] = $this->request->post("fai_number"); + + $validate = [ + ["real_name", "require", '姓名不可为空!'], + ["gender", "require", '请选择性别!'], + ["birthday", "require", '出生日期不可为空!'], + ["country", "require", '国籍不可为空!'], + ["address", "require", '详细地址不可为空!'], + ["player_pic", "require", '请上传证件照!'], + ['card_front_view', 'require', "请上传正面证件照片"], + ['card_number', 'require', "请输入{$add_data['card_type']}证件号码"] + ]; + + if ($add_data['country'] == "中国 China"){ + array_push( + $validate, + ['province', 'require', "请选择省份"], + ['city', 'require', "请选择市"], + ['district', 'require', "请选择区/县"] + ); + } + // 如果是 ASFC 获取会员编号 + if ($add_data['asfc_user'] == Players::ASFC_VIP) { + array_push( + $validate, + ['membership_id', 'require', '请填写 ASFC会员 证号!'] + ); + } + + + if ($add_data['club_members'] == Players::CLUP_USER) { + array_push( + $validate, + ['club_name', 'require', '请填写俱乐部名称!'], + ['duties', 'require', '请填写所在俱乐部职务!'], + ); + } + + if ($add_data['age'] < Players::ADULT) { + //监护人 + array_push( + $validate, + ['guarder_name', 'require', '请输入监护人姓名!'], + ['guarder_phone', 'require', '请输入监护人手机号'], + ['guarder_card_type', 'require', '请选择监护人证件类型'], + ['guarder_card_number', 'require', "请输入监护人{$add_data['guarder_card_type']}证件号码"], + ['guarder_card_front_view', 'require', "请上传监护人正面证件照片"], +// ['guarder_card_back_view', 'require', "请上传监护人{$add_data['guarder_card_type']}证件照片"] + ); + + //护照特殊判断 + if ($add_data['guarder_card_type'] == Players::CARD_TYPE[0]){ + array_push( + $validate, + ['guarder_card_view_same', 'require', "请选择人像页类型"] + ); + } + //排除护照单页的情况,背面必传 + if (!($add_data['guarder_card_type'] == Players::CARD_TYPE[1] && isset($add_data["guarder_card_view_same"]) && $add_data["guarder_card_view_same"] == 2)) { + + if ($add_data['guarder_card_type'] == Players::CARD_TYPE[1]){ + array_push( + $validate, + ['guarder_card_back_view', 'require', "请上传监护人证件签名页照片"] + ); + } + + array_push( + $validate, + ['card_back_view', 'require', "请上传反面证件照片"], + ['guarder_card_back_view', 'require', "请上传监护人反面证件照片"] + ); + + } + + //比较麻烦的判断 + if ($add_data["guarder_card_type"] == Players::CARD_TYPE[1] && $add_data["guarder_card_view_same"] == 2) { + $add_data["guarder_card_back_view"] = null; + } elseif ($add_data["guarder_card_type"] != "护照") { + $add_data["guarder_card_view_same"] = 1; + } + }else{ + array_push( + $validate, + ['card_type', 'require', '请选择证件类型!'] + ); + + //护照特殊判断 + if ($add_data['card_type'] == Players::CARD_TYPE[0]){ + array_push( + $validate, + ['card_view_same', 'require', "请选择人像页类型"] + ); + } + + //排除护照单页的情况,背面必传 + if (!($add_data['card_type'] == Players::CARD_TYPE[1] && isset($add_data["card_view_same"]) && $add_data["card_view_same"] == 2)) { + + if ($add_data['card_type'] == Players::CARD_TYPE[1]){ + array_push( + $validate, + ['card_back_view', 'require', "请上传证件签名页照片"] + ); + } + + array_push( + $validate, + ['card_back_view', 'require', "请上传反面证件照片"] + ); + } + + //比较麻烦的判断 + if ($add_data["card_type"] == "护照" && $add_data["card_view_same"] == 2) { + $add_data["card_back_view"] = null; + } elseif ($add_data["card_type"] != "护照") { + $add_data["card_view_same"] = 1; + } + } + $validate = new \think\Validate($validate); + + if (!$validate->check($add_data)){ + $this->error($validate->getError()); + } + + $playersModel = new Players(); + $playersinfo = $playersModel->getPlayers($this->auth->id); + + try { + $add_data['member_id'] = $this->auth->id; + $add_data['club_id'] = 0; + $add_data['phone'] = $this->auth->mobile; + $add_data['player_status'] = Players::PLAYERS_APPLYING; + $add_data['updated_at'] = date("Y-m-d H:i:s", time()); + if ($id){ + $add_data['id'] = $id; + $add_data['player_status'] = Players::PLAYERS_REAPPLY; + }else{ + $add_data['created_at'] = date("Y-m-d H:i:s", time()); + + } + + if($id){ + $playersinfo->save($add_data); + }else{ + // 判断该手机号和身份证是否存在 + + $playersModel->insert($add_data); + } + + }catch (Exception $exception){ + $this->error($exception->getMessage()); + } + + if ($id){ + if (!empty($post_type) && $post_type == "json") { + return json(['code' => 1, 'date' => [], 'msg' => '重新申请成功']); + } + $this->success("重新申请成功!", url('players/index')); + } + + if (!empty($post_type) && $post_type == "json") { + return json(['code' => 1, 'date' => [], 'msg' => '注册成功']); + } + + //模板消息通知 + $renzheng = new Renzheng(); + $res = $renzheng->where('id',1)->find(); + if(!empty($res)) $res = $res->toArray(); + if($res['status'] == 1){ + $wxmb = new Wxmb(); + $wxmb->renzhengpush($add_data,$res['openid']); + } + + $this->success("注册成功/successfully registered", url('players/index')); + + } + + //注册俱乐部 + public function register_club(){ + //俱乐部认证开关 + $renzheng = new Renzheng(); + $res = $renzheng->where('id',2)->find(); + if(!empty($res)) $res = $res->toArray(); + if($res['status'] == 0){ + $this->error('2024年中国无人机竞速联赛参赛主体认证已截止。咨询电话:13136225305'); + } + // $this->error('2024年中国无人机竞速联赛参赛主体预注册已截止,请等待正式注册通知。咨询电话:13136225305'); + // header('Access-Control-Allow-Origin:*'); + // header('Access-Control-Allow-Methods:*'); + // header('Access-Control-Allow-Headers:x-requested-with,content-type'); + $user_id = $this->auth->id; + if (empty($user_id)){ + $this->error("请先登录!",url('user/login')); + } + $club = new ClubModel(); + $players = new Players(); + $player_res = $players->where('member_id',$user_id)->find(); + if(!empty($player_res)) $this->error('该账号已注册过飞手身份'); + + $id = $this->request->post("id"); + $add_data['name'] = $this->request->post("name"); + $add_data['name_short'] = $this->request->post("name_short"); + $add_data['user_id'] = $user_id; + $add_data['competitors'] = $this->request->post("competitors"); + $add_data['competitors_type'] = $this->request->post("competitors_type"); //主体类型 + // $add_data['type'] = 1; + $add_data['business_num'] = $this->request->post("business_num"); + $add_data['business_license'] = $this->request->post("business_license"); + $add_data['card_front_view'] = $this->request->post("card_front_view"); //法人身份证 + $add_data['card_back_view'] = $this->request->post("card_back_view"); + $add_data['is_asfc'] = $this->request->post("is_asfc"); //是否中国航空团体会员 0否 1是 + $add_data['asfc_num'] = $this->request->post("asfc_num"); //证书编号 + $add_data['asfc_cert'] = $this->request->post("asfc_cert"); //证书 + $add_data['province'] = $this->request->post("province"); + $add_data['city'] = $this->request->post("city"); + $add_data['district'] = $this->request->post("district"); + $add_data['address'] = $this->request->post("address"); + $add_data['logo'] = $this->request->post("logo"); + $add_data['flag'] = $this->request->post("flag"); + $add_data['competition_slogan'] = $this->request->post("competition_slogan"); + + $add_data['legal_name'] = $this->request->post("legal_name"); //法人 + $add_data['legal_tel'] = $this->request->post("legal_tel"); + $add_data['legal_card'] = $this->request->post("legal_card"); + // var_dump($add_data['leader_tel']);exit; + $add_data['leader'] = $this->request->post("leader"); //领队 + $add_data['leader_tel'] = intval($this->request->post("leader_tel")); + // var_dump($add_data['leader_tel']);exit; + $add_data['deputy_leader1'] = $this->request->post("deputy_leader1"); //副领队1 + $add_data['deputy_leader1_tel'] = $this->request->post("deputy_leader1_tel"); + $add_data['deputy_leader2'] = $this->request->post("deputy_leader2"); //副领队2 + $add_data['deputy_leader2_tel'] = $this->request->post("deputy_leader2_tel"); + $add_data['head_coach'] = $this->request->post("head_coach"); //主教练 + $add_data['head_coach_tel'] = $this->request->post("head_coach_tel"); + $add_data['coach1'] = $this->request->post("coach1"); + $add_data['coach1_tel'] = $this->request->post("coach1_tel"); + $add_data['coach2'] = $this->request->post("coach2"); + $add_data['coach2_tel'] = $this->request->post("coach2_tel"); + $add_data['coach3'] = $this->request->post("coach3"); + $add_data['coach3_tel'] = $this->request->post("coach3_tel"); + $add_data['coach4'] = $this->request->post("coach4"); + $add_data['coach4_tel'] = $this->request->post("coach4_tel"); + $add_data['player_coach_stuff'] = $this->request->post("player_coach_stuff"); //运动员教练名单 + $add_data['job_situ'] = $this->request->post("job_situ"); //工作情况 + $add_data['promise_stuff'] = $this->request->post("promise_stuff"); //参赛承诺 + + $add_data['is_media'] = $this->request->post("is_media"); //是否有媒体宣传账号 + $add_data['media_platform'] = $this->request->post("media_platform"); //媒体平台 + $add_data['media_name'] = $this->request->post("media_name"); // + $add_data['club_clothes1'] = $this->request->post("club_clothes1"); // + $add_data['club_clothes2'] = $this->request->post("club_clothes2"); // + $add_data['club_clothes3'] = $this->request->post("club_clothes3"); // + $add_data['club_clothes4'] = $this->request->post("club_clothes4"); // + // $add_data['hk_card'] = $this->request->post("hk_card"); // + + $add_data['status'] = 1; + $add_data['createtime'] = time(); + $add_data['updatetime'] = time(); + + $validate = [ + ["name", "require", '俱乐部名称不可为空!'], + ["competitors", "require", '赛事主体不可为空!'], + ["business_license", "require", '请上传营业执照!'], + ["logo", "require", '请上传俱乐部队徽!'], + ["flag", "require", '请上传俱乐部队旗!'], + ['province', 'require', "请选择省份"], + ['city', 'require', "请选择市"], + ['district', 'require', "请选择区/县"], + ["address", "require", '详细地址不可为空!'], + // ["player_pic", "require", '请上传证件照!'], + ['card_front_view', 'require', "请上传法人正面证件照片"], + ['card_back_view', 'require', "请上传法人反面证件照片"], + ['legal_name', 'require', "法人名称不可为空"], + ['legal_tel', 'require', "法人电话不可为空"], + ['legal_card', 'require', "法人身份证不可为空"], + // ['gym_nature', 'require', "场地性质不可为空"], + // ['respon_tel', 'require', "负责人电话不可为空"], + ['deputy_leader1_tel', 'require', "副领队1电话不可为空"], + ['deputy_leader2', 'require', "副领队2名称不可为空"], + ['deputy_leader2_tel', 'require', "副领队2电话不可为空"], + ['head_coach', 'require', "主教练名称不可为空"], + ['head_coach_tel', 'require', "主教练电话不可为空"], + ['coach1', 'require', "教练员1名称不可为空"], + ['coach1_tel', 'require', "教练员1电话不可为空"], + ]; + + $validate = new \think\Validate($validate); + + if (!$validate->check($add_data)){ + $this->error($validate->getError()); + } + + $club_res = $club->where('competitors',$add_data['competitors'])->where('status',9)->find(); + if(!empty($club_res)) $this->error('参赛主体已注册'); + + $club->insert($add_data); + + //微信通知 + $renzheng = new Renzheng(); + $res = $renzheng->where('id',1)->find(); + if(!empty($res)) $res = $res->toArray(); + if($res['status'] == 1){ + $wxmb = new Wxmb(); + $wxmb->clubpush($add_data,$res['openid']); + } + + $this->success("提交成功", url('club/index')); + } + + //重新申请俱乐部 + public function club_resubmit(){ + $user_id = $this->auth->id; + if (empty($user_id)){ + $this->error("请先登录!",url('user/login')); + } + $club = new ClubModel(); + $players = new Players(); + + $player_res = $players->where('member_id',$user_id)->find(); + if(!empty($player_res)) $this->error('该账号已注册过飞手身份'); + + $id = $this->request->post("id"); + if(empty($id)) $this->error("缺少参数!"); + $club_res = $club->where('id',$id)->where('user_id',$user_id)->find(); + if(empty($club_res)) $this->error("没有权限!"); + + $add_data['name'] = $this->request->post("name"); + $add_data['name_short'] = $this->request->post("name_short"); + $add_data['user_id'] = $user_id; + $add_data['competitors'] = $this->request->post("competitors"); + $add_data['competitors_type'] = $this->request->post("competitors_type"); //主体类型 + // $add_data['type'] = 1; + $add_data['business_num'] = $this->request->post("business_num"); //营业执照编号 + // var_dump($add_data);exit; + $add_data['business_license'] = $this->request->post("business_license"); + $add_data['card_front_view'] = $this->request->post("card_front_view"); + $add_data['card_back_view'] = $this->request->post("card_back_view"); + $add_data['is_asfc'] = $this->request->post("is_asfc"); //是否中国航空团体会员 0否 1是 + $add_data['asfc_num'] = $this->request->post("asfc_num"); //证书编号 + $add_data['asfc_cert'] = $this->request->post("asfc_cert"); + $add_data['province'] = $this->request->post("province"); + $add_data['city'] = $this->request->post("city"); + $add_data['district'] = $this->request->post("district"); + $add_data['address'] = $this->request->post("address"); + $add_data['logo'] = $this->request->post("logo"); + $add_data['flag'] = $this->request->post("flag"); + $add_data['competition_slogan'] = $this->request->post("competition_slogan"); + + $add_data['legal_name'] = $this->request->post("legal_name"); //法人 + $add_data['legal_tel'] = $this->request->post("legal_tel"); + $add_data['legal_card'] = $this->request->post("legal_card"); + // var_dump($add_data['leader_tel']);exit; + $add_data['leader'] = $this->request->post("leader"); //领队 + $add_data['leader_tel'] = intval($this->request->post("leader_tel")); + // var_dump($add_data['leader_tel']);exit; + $add_data['deputy_leader1'] = $this->request->post("deputy_leader1"); //副领队1 + $add_data['deputy_leader1_tel'] = $this->request->post("deputy_leader1_tel"); + $add_data['deputy_leader2'] = $this->request->post("deputy_leader2"); //副领队2 + $add_data['deputy_leader2_tel'] = $this->request->post("deputy_leader2_tel"); + $add_data['head_coach'] = $this->request->post("head_coach"); //主教练 + $add_data['head_coach_tel'] = $this->request->post("head_coach_tel"); + $add_data['coach1'] = $this->request->post("coach1"); + $add_data['coach1_tel'] = $this->request->post("coach1_tel"); + $add_data['coach2'] = $this->request->post("coach2"); + $add_data['coach2_tel'] = $this->request->post("coach2_tel"); + $add_data['coach3'] = $this->request->post("coach3"); + $add_data['coach3_tel'] = $this->request->post("coach3_tel"); + $add_data['coach4'] = $this->request->post("coach4"); + $add_data['coach4_tel'] = $this->request->post("coach4_tel"); + $add_data['player_coach_stuff'] = $this->request->post("player_coach_stuff"); //运动员教练名单 + $add_data['job_situ'] = $this->request->post("job_situ"); //工作情况 + $add_data['promise_stuff'] = $this->request->post("promise_stuff"); //参赛承诺 + + $add_data['is_media'] = $this->request->post("is_media"); //是否有媒体宣传账号 + $add_data['media_platform'] = $this->request->post("media_platform"); //媒体平台 + $add_data['media_name'] = $this->request->post("media_name"); // + $add_data['club_clothes1'] = $this->request->post("club_clothes1"); // + $add_data['club_clothes2'] = $this->request->post("club_clothes2"); // + $add_data['club_clothes3'] = $this->request->post("club_clothes3"); // + $add_data['club_clothes4'] = $this->request->post("club_clothes4"); // + // $add_data['hk_card'] = $this->request->post("hk_card"); // + + $add_data['status'] = 4; + $add_data['updatetime'] = time(); + + + $validate = [ + ["name", "require", '俱乐部名称不可为空!'], + ["competitors", "require", '赛事主体不可为空!'], + ["business_license", "require", '请上传营业执照!'], + ["logo", "require", '请上传俱乐部队徽!'], + ["flag", "require", '请上传俱乐部队旗!'], + ['province', 'require', "请选择省份"], + ['city', 'require', "请选择市"], + ['district', 'require', "请选择区/县"], + ["address", "require", '详细地址不可为空!'], + // ["player_pic", "require", '请上传证件照!'], + ['card_front_view', 'require', "请上传法人正面证件照片"], + ['card_back_view', 'require', "请上传法人反面证件照片"], + ['leader', 'require', "领队名称不可为空"], + ['leader_tel', 'require', "领队电话不可为空"], + ['deputy_leader1', 'require', "副领队1名称不可为空"], + ['deputy_leader1_tel', 'require', "副领队1电话不可为空"], + ['deputy_leader2', 'require', "副领队2名称不可为空"], + ['deputy_leader2_tel', 'require', "副领队2电话不可为空"], + ['head_coach', 'require', "主教练名称不可为空"], + ['head_coach_tel', 'require', "主教练电话不可为空"], + ['coach1', 'require', "教练员1名称不可为空"], + ['coach1_tel', 'require', "教练员1电话不可为空"], + ]; + + $validate = new \think\Validate($validate); + + if (!$validate->check($add_data)){ + $this->error($validate->getError()); + } + + // $club_res = $club->where('competitors',$add_data['competitors'])->where('status',9)->find(); + // if(!empty($club_res)) $this->error('参赛主体已注册'); + + $club->save($add_data,['id'=>$id]); + + //微信通知 + $renzheng = new Renzheng(); + $res = $renzheng->where('id',1)->find(); + if(!empty($res)) $res = $res->toArray(); + if($res['status'] == 1){ + $wxmb = new Wxmb(); + $wxmb->clubpush($add_data,$res['openid']); + } + $this->success("重新申请成功!", url('club/index')); + + } +} diff --git a/application/index/controller/Wrj.php b/application/index/controller/Wrj.php new file mode 100644 index 0000000..2f9989e --- /dev/null +++ b/application/index/controller/Wrj.php @@ -0,0 +1,87 @@ +screemService = new ScreenService(); + } + + // public function index(){ + // return $this->view->fetch(); + // } + + public function live() + { + + $id = $this->request->param("match_id"); + if (empty($id)) { + $this->error("非法请求"); + } + $this->auth->setMatchId($id); + if (!$this->screemService->verifyScreen(['match_id' => $id, 'type' => "screen"])) { + // 判断是否登陆 + if (!Session::has("screen_ranking{$id}")) { + $this->match_id = $id; + $this->error("请先登录!", url('wrj/login', ['match_id' => $id])); + } + } + + $screen_info = $this->screemService->getScreenInfo([ + 'match_id' => $id, + 'type' => "screen" + ]); + $this->view->assign("match_id", $id); + $this->view->assign('screeninfo', $screen_info); + return $this->view->fetch('index'); + } + + public function login() + { + if ($this->request->isPost()) { + $data['look_pwd'] = $this->request->post("password"); + + if (empty($data['look_pwd'])) { + $this->error("密码不能为空!"); + } elseif (empty($this->request->param("match_id"))) { + $this->error("非法请求"); + } + + $data['match_id'] = $this->request->param("match_id"); + $data['type'] = 'screen'; + + if ($this->screemService->verifyScreen($data)) { + Session::set("screen_ranking{$data['match_id']}", true); + $this->success("登陆成功!", 'https://www.fpvone.cn/index/wrj/live?match_id='.$data['match_id']); + // header('location:https://www.fpvone.cn/index/wrj/live?match_id='.$data['match_id']); + } else { + $this->error("密码错误!"); + } + } + $id = $this->request->param("match_id"); + if (empty($id)) { + $this->error("非法请求"); + } + return $this->view->fetch(); + } +} \ No newline at end of file diff --git a/application/index/controller/Wxmb.php b/application/index/controller/Wxmb.php new file mode 100644 index 0000000..17cc98b --- /dev/null +++ b/application/index/controller/Wxmb.php @@ -0,0 +1,270 @@ +leaguepush(); + // $this->leaguepush(array('title'=>'俱乐部邀请通知','username'=>'张三','type'=>'类型1'),'otmyT6mZXtSbPY0g22jSoSiRUc6o'); + // $this->weishoupushs(array('order_sn'=>'2342342342','order_amount'=>11,'paid_time'=>date('Y-m-d H:i:s'))); + } + + public function leaguepush($add_data,$openid) + { + $appid = 'wxae874d12ca3f56b8';//公众号的appid + $secret = '0984255faae9d7c0c8a9926aa396d22b'; + // 获取token + $token = $this->getAccToken($appid,$secret); + // var_dump($token);exit; + // file_put_contents(__DIR__."tztoken.txt",json_encode($token)); + $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=".$token; + $data = [ + "touser"=>$openid, + "appid"=>"wxae874d12ca3f56b8",//公众号 + "template_id"=>"GQwDq5V_xM3wRfTOAHOfFrOLwKahJwRRCp4KWBla3FM",//模板id + "url"=>"https://www.fpvone.cn/index/user/login.html", + "data"=>[ + 'thing6' => array( + 'value'=> $add_data['title'], + ), + 'thing3'=>array( + 'value'=> $add_data['username'], + ), + 'thing19'=>array( + 'value'=> $add_data['type'], + ), + ], + + ]; + + return $this->httpUtil($url,json_encode($data),'POST'); //发送请求 + } + + public function getAccToken($appid,$secret){ + $token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$appid}&secret={$secret}"; + $token_res = $this->httpUtil($token_url); + $token_res = json_decode($token_res, true); + $token = $token_res['access_token']; + return $token; + } + + public function httpUtil($url, $data = '', $method = 'GET'){ + try { + // file_put_contents(__DIR__."tzaaa.txt",$url); + $curl = curl_init(); // 启动一个CURL会话 + curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址 + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查 + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在 + // curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器 + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转 + curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer + // file_put_contents(__DIR__."tzeee.txt",json_encode($_SERVER['HTTP_USER_AGENT'])); + if ($method == 'POST') { + curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求 + if ($data != '') { + curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包 + } + } + // file_put_contents(__DIR__."tzddd.txt",000); + curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环 + curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容 + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回 + + $tmpInfo = curl_exec($curl); // 执行操作 + // file_put_contents(__DIR__."tzfff.txt",000); + curl_close($curl); // 关闭CURL会话 + return $tmpInfo; // 返回数据 + } catch (Exception $e) { + // file_put_contents(__DIR__."tzbbb.txt",json_encode($e->getMessage())); + return $e->getMessage(); + } + } + + public function weishoupushs($order) + { +// file_put_contents(__DIR__."tztem.txt",json_encode($order['order_sn'])); + $appid = 'wxae874d12ca3f56b8';//公众号的appid + $secret = '0984255faae9d7c0c8a9926aa396d22b'; + // 获取token + $token = $this->getAccToken($appid,$secret); + // file_put_contents(__DIR__."tztoken.txt",json_encode($token)); + //$url = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=".$token; + $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=".$token; + + $thirdOauth = new ThirdOauth(); + $admin = $thirdOauth->where('admin_id',7)->find(); + + if(!empty($admin)) {$admin = $admin->toArray();} + else{ + $admin['openid'] = 'otmyT6mZXtSbPY0g22jSoSiRUc6o'; + } + // var_dump($admin['openid']);exit; + $data = [ + "touser"=>$admin['openid'], + "appid"=>"wxae874d12ca3f56b8",//公众号 + //"template_id"=>"nVfy1Tz1EXQ3T2YcGkwgicAjrD0M3N1Ca-CHIJCwjQY",//模板id + "template_id"=>"t-UC5eLA8R5f7NHKnH39oLxYlOHS5mvu6BeaNUgJx_8",//模板id + "url"=>"https://shop.fpvone.cn", + // "miniprogram"=>[ + // "appid"=>"wx18aa***",//小程序 + // //"pagepath"=>"/pagesIndex/house/billingDetails?type=3&house_user_id=" . $house_user_id . "&house_code_id=" . $house_code_id//pagesIndex/house/billingDetails 未收账单路径 index?foo=bar + // "pagepath"=>"/pagesIndex/house/billingDetails?type=3&id=" . $data['house_user_id'] . "&ids=" . $data['house_code_id']//pagesIndex/house/billingDetails 未收账单路径 index?foo=bar + // ], + "data"=>[ + 'character_string1' => array( + 'value'=>$order['order_sn'], + ), + 'amount3'=>array( + 'value'=>$order['order_amount'], + ), + 'time9'=>array( + 'value'=>$order['paid_time'], + // 'value'=>date('Y-m-d H:i:s'), + ), + ], + + ]; + // file_put_contents(__DIR__."tztem1.txt",json_encode($data)); + return $this->httpUtil($url,json_encode($data),'POST'); //发送请求 + } + + public function tuikuanpush($order) + { + $appid = 'wxae874d12ca3f56b8';//公众号的appid + $secret = '0984255faae9d7c0c8a9926aa396d22b'; + // 获取token + $token = $this->getAccToken($appid,$secret); + // var_dump($order['order_sn']);exit; + // file_put_contents(__DIR__."tztoken.txt",json_encode($token)); + $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=".$token; + + $thirdOauth = new ThirdOauth(); + $admin = $thirdOauth->where('admin_id',7)->find(); + + if(!empty($admin)) {$admin = $admin->toArray();} + else{ + $admin['openid'] = 'otmyT6mZXtSbPY0g22jSoSiRUc6o'; + } + // var_dump($admin);exit; + $data = [ + "touser"=>$admin['openid'], + "appid"=>"wxae874d12ca3f56b8",//公众号 + //"template_id"=>"nVfy1Tz1EXQ3T2YcGkwgicAjrD0M3N1Ca-CHIJCwjQY",//模板id + "template_id"=>"dz0Dg4k1O3tjPQEnGyoOP_wSbKDIQr0tlaVULac9NiQ",//模板id + "url"=>"https://shop.fpvone.cn", + "data"=>[ + 'character_string12' => array( + 'value'=>$order['order_sn'], + ), + 'amount4'=>array( + 'value'=>$order['order_amount'], + ), + 'time3'=>array( + 'value'=>$order['updatetime'], + // 'value'=>date('Y-m-d H:i:s'), + ), + ], + + ]; + return $this->httpUtil($url,json_encode($data),'POST'); //发送请求 + } + + public function renzhengpush($add_data,$openid) + { + $appid = 'wxae874d12ca3f56b8';//公众号的appid + $secret = '0984255faae9d7c0c8a9926aa396d22b'; + // 获取token + $token = $this->getAccToken($appid,$secret); + // var_dump($token);exit; + // file_put_contents(__DIR__."tztoken.txt",json_encode($token)); + $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=".$token; + + // $thirdOauth = new ThirdOauth(); + // $admin = $thirdOauth->where('admin_id',7)->find(); + + // if(!empty($admin)) {$admin = $admin->toArray();} + // else{ + // $admin['openid'] = 'otmyT6mZXtSbPY0g22jSoSiRUc6o'; + // } + // var_dump($admin);exit; + $data = [ + "touser"=>$openid, + "appid"=>"wxae874d12ca3f56b8",//公众号 + //"template_id"=>"nVfy1Tz1EXQ3T2YcGkwgicAjrD0M3N1Ca-CHIJCwjQY",//模板id + "template_id"=>"u3sFFtaYZRiqq2S1h6ODsRl30_n0cJnftizBe1nOFVA",//模板id + // "url"=>"https://shop.fpvone.cn", + "data"=>[ + 'thing1' => array( + 'value'=>$add_data['real_name'], + ), + // 'phone_number2'=>array( + // 'value'=>456, + // ), + 'time4'=>array( + 'value'=>date('Y-m-d H:i:s'), + // 'value'=>date('Y-m-d H:i:s'), + ), + ], + + ]; + + return $this->httpUtil($url,json_encode($data),'POST'); //发送请求 + } + + public function clubpush($add_data,$openid) + { + $appid = 'wxae874d12ca3f56b8';//公众号的appid + $secret = '0984255faae9d7c0c8a9926aa396d22b'; + // 获取token + $token = $this->getAccToken($appid,$secret); + // var_dump($token);exit; + // file_put_contents(__DIR__."tztoken.txt",json_encode($token)); + $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=".$token; + + // $thirdOauth = new ThirdOauth(); + // $admin = $thirdOauth->where('admin_id',7)->find(); + + // if(!empty($admin)) {$admin = $admin->toArray();} + // else{ + // $admin['openid'] = 'otmyT6mZXtSbPY0g22jSoSiRUc6o'; + // } + // var_dump($admin);exit; + $data = [ + "touser"=>$openid, + "appid"=>"wxae874d12ca3f56b8",//公众号 + //"template_id"=>"nVfy1Tz1EXQ3T2YcGkwgicAjrD0M3N1Ca-CHIJCwjQY",//模板id + "template_id"=>"u3sFFtaYZRiqq2S1h6ODsRl30_n0cJnftizBe1nOFVA",//模板id + // "url"=>"https://shop.fpvone.cn", + "data"=>[ + 'thing1' => array( + 'value'=> $add_data['name_short'], + ), + // 'phone_number2'=>array( + // 'value'=>456, + // ), + 'time4'=>array( + 'value'=>date('Y-m-d H:i:s'), + // 'value'=>date('Y-m-d H:i:s'), + ), + ], + + ]; + + return $this->httpUtil($url,json_encode($data),'POST'); //发送请求 + } + + + +} diff --git a/application/index/controller/Zhinan.php b/application/index/controller/Zhinan.php new file mode 100644 index 0000000..5e1d415 --- /dev/null +++ b/application/index/controller/Zhinan.php @@ -0,0 +1,94 @@ +param(); + $archives = new Archives(); + if(empty($params['str']) || empty($params['limit']) || !is_numeric($params['limit']) || empty($params['page']) || !is_numeric($params['page'])){ + $message = [ + 'code' => 0, + 'message' => '请求异常', + ]; + return json($message); + } + $data = []; + $res = $archives->where('channel_id',59)->where('status','normal')->where('title','like','%'.$params['str'].'%')->select(); + $res1 = $archives->where('channel_id',59)->where('status','normal')->where('title','like','%'.$params['str'].'%')->page($params['page'],$params['limit'])->select(); + $data['code'] = 1; + $data['total'] = count($res); + $data['data'] = $res1; + return json($data); + } + + public function list(){ + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies + header('Access-Control-Expose-Headers: *'); //服务器 headers 白名单,可以让客户端进行访问 + header('Access-Control-Allow-Headers: *'); + $archives = new Archives(); + $request = Request::instance(); + $params = $request->param(); + if(empty($params['limit']) || !is_numeric($params['limit']) || empty($params['page']) || !is_numeric($params['page'])){ + $message = [ + 'code' => 0, + 'message' => '请求异常', + ]; + return json($message); + } + $data = []; + $res = $archives->where('channel_id',59)->where('status','normal')->select(); + $res1 = $archives->where('channel_id',59)->where('status','normal')->page($params['page'],$params['limit'])->select(); + $data['code'] = 1; + $data['total'] = count($res); + $data['data'] = $res1; + return json($data); + // var_dump($res);exit; + } + + public function detail(){ + // $user = auth_user(); + // var_dump($user);exit; + header('Access-Control-Allow-Origin: *'); + header('Access-Control-Allow-Credentials: true'); // 设置是否允许发送 cookies + header('Access-Control-Expose-Headers: *'); //服务器 headers 白名单,可以让客户端进行访问 + header('Access-Control-Allow-Headers: *'); + $request = Request::instance(); + $params = $request->param(); + if(empty($params['id'])){ + $message = [ + 'code' => 0, + 'message' => '请求异常', + ]; + return json($message); + } + $archives = new Archives(); + $addonnews = new Addonnews(); + $res = $addonnews->where('id',$params['id'])->find(); + $res2 = $archives->where('id',$params['id'])->find(); + $res['title'] = $res2['title']; + $data['code'] = 1; + $data['data'] = $res; + return json($data); + } +} diff --git a/application/index/controller/cms/Archives.php b/application/index/controller/cms/Archives.php new file mode 100644 index 0000000..d222c66 --- /dev/null +++ b/application/index/controller/cms/Archives.php @@ -0,0 +1,325 @@ +request->get('id'); + $id = $config['archiveshashids'] ? IntCode::decode($id) : $id; + $archives = $id ? \app\admin\model\cms\Archives::get($id) : null; + + if ($archives) { + $channel = Channel::get($archives['channel_id']); + if (!$channel) { + $this->error(__('未找到指定栏目')); + } + $model = \addons\cms\model\Modelx::get($channel['model_id']); + if (!$model) { + $this->error(__('未找到指定模型')); + } + if ($archives['user_id'] != $this->auth->id) { + $this->error("无法进行越权操作!"); + } + } else { + $model = null; + $model_id = $this->request->request('model_id'); + // 如果有model_id则调用指定模型 + if ($model_id) { + $model = Modelx::get($model_id); + } + } + + // 如果来源于提交 + if ($this->request->isPost()) { + $this->token(); + if (isset($config['limitscore']['postarchives']) && $this->auth->score < $config['limitscore']['postarchives']) { + $this->error("积分必须大于{$config['limitscore']['postarchives']}才可以发布文章"); + } + + $row = $this->request->post('row/a', '', 'trim,xss_clean'); + + $rule = [ + 'title|标题' => 'require|length:3,100', + 'channel_id|栏目' => 'require|integer', + ]; + + $msg = [ + 'title.require' => '标题不能为空', + 'title.length' => '标题长度限制在3~100个字符', + 'channel_id' => '栏目不能为空', + 'content.require' => '内容不能为空', + ]; + $validate = new Validate($rule, $msg); + $result = $validate->check($row); + if (!$result) { + $this->error($validate->getError()); + } + + $channelIds = isset($row['channel_ids']) ? (is_array($row['channel_ids']) ? $row['channel_ids'] : explode(',', $row['channel_ids'])) : []; + $channelIds = array_merge([$row['channel_id']], $channelIds); + $channelIds = array_filter($channelIds); + $count = Channel::where('id', 'in', $channelIds)->where('iscontribute', 0)->count(); + if ($count > 0) { + $this->error("栏目不允许投稿"); + } + + //审核状态 + $status = 'normal'; + if ($config['isarchivesaudit'] == 1) { + $status = 'hidden'; + } elseif ($config['isarchivesaudit'] == 0) { + $status = 'normal'; + } else { + $textArr = array_map(function ($item) { + return is_array($item) ? json_encode($item, JSON_UNESCAPED_UNICODE) : $item; + }, $row); + if (!Service::isContentLegal(implode(' ', $textArr))) { + $status = 'hidden'; + } + } + + $row['user_id'] = $this->auth->id; + $row['status'] = $status; + $row['publishtime'] = time(); + + Db::startTrans(); + try { + if ($archives) { + $archives->allowField(true)->save($row); + } else { + (new \app\admin\model\cms\Archives)->allowField(true)->save($row); + } + //增加积分 + $status == 'normal' && User::score($config['score']['postarchives'], $this->auth->id, '发布文章'); + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + $this->error("发生错误:" . $e->getMessage()); + } + if ($status === 'hidden') { + //发送通知 + $status === 'hidden' && Service::notice(config('cms.sitename') . '收到一篇新的文章审核'); + $this->success("发布成功!请等待审核!"); + } else { + $this->success("发布成功!"); + } + } + + + // 合并主副表 + if ($archives) { + $addon = db($model['table'])->where('id', $archives['id'])->find(); + if ($addon) { + $archives->setData($addon); + } + } + + $selectedIds = $archives ? $archives['channel_id'] : ''; + if (!$archives && !$model) { + $firstChannel = Channel::where('status', 'normal')->where('iscontribute', 1)->order('weigh DESC,id DESC')->find(); + $model = Modelx::get($firstChannel['model_id']); + $selectedIds = $firstChannel['id']; + } + + list($channelList, $disabledIds) = Channel::getContributeInfo($archives, $model); + + $tree = Tree::instance()->init($channelList, 'parent_id'); + $channelOptions = $tree->getTree(0, "", $selectedIds, $disabledIds); + $this->view->assign('channelOptions', $channelOptions); + $this->view->assign([ + 'archives' => $archives, + 'channelOptions' => $channelOptions, + 'categoryList' => '', + 'extendHtml' => $this->get_model_extend_html($archives, $model) + ]); + $this->assignconfig('archives_id', $archives ? $archives['id'] : 0); + + $tree = Tree::instance()->init($channelList, 'parent_id'); + $secondChannelOptions = $tree->getTree(0, "", explode(',', $archives ? $archives['channel_ids'] : ''), $disabledIds); + $this->view->assign('secondChannelOptions', $secondChannelOptions); + + $modelName = $model ? $model['name'] : '文章'; + $this->view->assign('title', $archives ? "修改{$modelName}" : "发布{$modelName}"); + $this->view->assign('model', $model); + return $this->view->fetch(); + } + + /** + * 我的发布 + */ + public function my() + { + $archives = new \addons\cms\model\Archives; + $model = null; + $model_id = (int)$this->request->request('model_id'); + $channel_id = (int)$this->request->request('channel_id'); + $q = $this->request->request('q'); + $config = ['query' => []]; + + // 指定模型 + if ($model_id) { + $model = Modelx::get($model_id); + if ($model) { + $archives->where('model_id', $model_id); + $config['query']['model_id'] = $model_id; + } + } + + // 搜索关键字 + if ($q) { + $archives->where('title|keywords|description', 'like', '%' . $q . '%'); + $config['query']['q'] = $q; + } + + // 栏目 + if ($channel_id) { + $archives->where('channel_id', $channel_id); + $config['query']['channel_id'] = $channel_id; + } + + $user_id = $this->auth->id; + $archivesList = $archives->where('user_id', $user_id) + ->order('id', 'desc') + ->paginate(10, null, $config); + + $channelList = Channel::where('id', 'in', function ($query) use ($user_id) { + $query->name('cms_archives')->where('user_id', $user_id)->field('channel_id'); + })->where('status', 'normal')->select(); + $this->view->assign('archivesList', $archivesList); + $this->view->assign('channelList', $channelList); + $this->view->assign('title', '我发布的' . ($model ? $model['name'] : '文档')); + $this->view->assign('model', $model); + return $this->view->fetch(); + } + + /** + * 删除文档 + */ + public function delete() + { + $config = get_addon_config('cms'); + $id = $this->request->request('id'); + $id = $config['archiveshashids'] ? IntCode::decode($id) : $id; + if (!$id) { + $this->error("参数不正确"); + } + $archives = \addons\cms\model\Archives::where('id', $id)->where('user_id', $this->auth->id)->find(); + if (!$archives) { + $this->error("未找到指定的文档"); + } + Db::startTrans(); + try { + $archives->delete(); + if ($archives->channel->items > 0) { + $archives->channel->setDec("items"); + } + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + $this->error("删除文档失败"); + } + $this->success("删除文档成功"); + } + + /** + * 获取栏目列表 + * @internal + */ + public function get_channel_fields() + { + $this->view->engine->layout(false); + $channel_id = $this->request->post('channel_id'); + $archives_id = $this->request->post('archives_id'); + $channel = Channel::get($channel_id, 'model'); + if ($channel && $channel['type'] != 'link') { + $archives = null; + $model = null; + $model_id = $channel['model_id']; + if ($archives_id) { + $archives = \app\admin\model\cms\Archives::get($archives_id); + $model_id = $archives ? $archives['model_id'] : $model_id; + } + $model = Modelx::get($model_id); + + $setting = $model->setting ?? []; + $html = $this->get_model_extend_html($archives, $model); + $this->success('', null, ['contributefields' => $setting['contributefields'] ?? [], 'html' => $html]); + } else { + $this->error(__('请选择栏目')); + } + $this->error(__('参数不能为空', 'ids')); + } + + /** + * 获取模型的扩展HTML + * @internal + */ + protected function get_model_extend_html($archives, $model) + { + if (!$archives && !$model) { + return ''; + } + $fields = []; + $values = []; + if ($archives) { + $model = $model ? $model : Modelx::get($archives['model_id']); + $values = db($model['table'])->where('id', $archives['id'])->find(); + } + $fields = Service::getCustomFields('model', $model['id'], $values, ['iscontribute' => 1]); + + return $this->view->fetch('cms/common/fields', ['fields' => $fields, 'values' => $values]); + } + + /** + * 标签自动完成 + * @internal + */ + public function tags_autocomplete() + { + $q = $this->request->request('q'); + $list = \addons\cms\model\Tag::where('name', 'like', '%' . $q . '%')->limit(10)->column('name'); + echo json_encode($list); + return; + } + + /** + * 搜索建议 + * @internal + */ + public function suggestion() + { + $q = trim($this->request->request("q")); + $id = trim($this->request->request("id/d")); + $list = []; + $result = \addons\cms\model\Archives::where("title|keywords|description", "like", "%{$q}%")->where('id', '<>', $id)->limit(10)->order("id", "desc")->select(); + foreach ($result as $index => $item) { + $list[] = ['id' => $item['id'], 'url' => $item['fullurl'], 'image' => cdnurl($item['image']), 'title' => $item['title'], 'create_date' => datetime($item['createtime'])]; + } + return json($list); + } +} diff --git a/application/index/controller/cms/Collection.php b/application/index/controller/cms/Collection.php new file mode 100644 index 0000000..325019e --- /dev/null +++ b/application/index/controller/cms/Collection.php @@ -0,0 +1,84 @@ +request->request('type'); + $q = $this->request->request('q'); + $config = ['query' => []]; + + // 指定模型 + if ($type) { + $model = Modelx::get($type); + if ($model) { + $collection->where('type', $type); + $config['query']['type'] = $type; + } + } + + // 搜索关键字 + if ($q) { + $collection->where('title|keywords|description', 'like', '%' . $q . '%'); + $config['query']['q'] = $q; + } + + $user_id = $this->auth->id; + $collectionList = $collection->where('user_id', $user_id) + ->order('id', 'desc') + ->paginate(10, null, $config); + + $this->view->assign('collectionList', $collectionList); + $this->view->assign('title', '我收藏的' . ($model ? $model['name'] : '文档')); + $this->view->assign('model', $model); + return $this->view->fetch(); + } + + /** + * 删除收藏 + */ + public function delete() + { + $id = (int)$this->request->request('id/d'); + if (!$id) { + $this->error("参数不正确"); + } + $collection = \addons\cms\model\Collection::where('id', $id)->where('user_id', $this->auth->id)->find(); + if (!$collection) { + $this->error("未找到指定的收藏"); + } + Db::startTrans(); + try { + $collection->delete(); + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + $this->error("移除收藏失败"); + } + $this->success("移除收藏成功"); + } + +} diff --git a/application/index/controller/cms/Comment.php b/application/index/controller/cms/Comment.php new file mode 100644 index 0000000..e084eec --- /dev/null +++ b/application/index/controller/cms/Comment.php @@ -0,0 +1,33 @@ +auth->id; + $commentList = \addons\cms\model\Comment::where('user_id', $user_id) + ->where('status', 'normal') + ->order('id', 'desc') + ->paginate(10, null); + + $this->view->assign('config', array_merge($this->view->config, ['jsname' => ''])); + $this->view->assign('commentList', $commentList); + $this->view->assign('title', '我发表的评论'); + return $this->view->fetch(); + } + +} diff --git a/application/index/controller/cms/Order.php b/application/index/controller/cms/Order.php new file mode 100644 index 0000000..78507b5 --- /dev/null +++ b/application/index/controller/cms/Order.php @@ -0,0 +1,33 @@ +auth->id; + $orderList = \addons\cms\model\Order::with(['archives'])->where('user_id', $user_id) + ->where('status', 'paid') + ->order('id', 'desc') + ->paginate(10, null); + + $this->view->assign('config', array_merge($this->view->config, ['jsname' => ''])); + $this->view->assign('orderList', $orderList); + $this->view->assign('title', '我的消费订单'); + return $this->view->fetch(); + } + +} diff --git a/application/index/controller/cms/Ranking.php b/application/index/controller/cms/Ranking.php new file mode 100644 index 0000000..e5f451e --- /dev/null +++ b/application/index/controller/cms/Ranking.php @@ -0,0 +1,16 @@ + '保持会话', + 'Forgot password' => '忘记密码?', + 'Username' => '用户名', + 'User id' => '会员ID', + 'Nickname' => '昵称', + 'Password' => '密码', + 'Sign up' => '注 册', + 'Sign in' => '登 录', + 'Sign out' => '退 出', + 'Guest' => '游客', + 'Welcome' => '%s,你好!', + 'Add' => '添加', + 'Edit' => '编辑', + 'Delete' => '删除', + 'Move' => '移动', + 'Name' => '名称', + 'Status' => '状态', + 'Weigh' => '权重', + 'Operate' => '操作', + 'Warning' => '温馨提示', + 'Default' => '默认', + 'Article' => '文章', + 'Page' => '单页', + 'OK' => '确定', + 'Cancel' => '取消', + 'Loading' => '加载中', + 'Money' => '余额', + 'Score' => '积分', + 'More' => '更多', + 'Normal' => '正常', + 'Hidden' => '隐藏', + 'Submit' => '提交', + 'Reset' => '重置', + 'Execute' => '执行', + 'Close' => '关闭', + 'Search' => '搜索', + 'Refresh' => '刷新', + 'First' => '首页', + 'Previous' => '上一页', + 'Next' => '下一页', + 'Last' => '末页', + 'None' => '无', + 'Online' => '在线', + 'Logout' => '退出', + 'Profile' => '个人资料', + 'Index' => '首页', + 'Hot' => '热门', + 'Recommend' => '推荐', + 'Dashboard' => '控制台', + 'Code' => '编号', + 'Message' => '内容', + 'Line' => '行号', + 'File' => '文件', + 'Menu' => '菜单', + 'Type' => '类型', + 'Title' => '标题', + 'Content' => '内容', + 'Apply' => '应用', + 'Clear' => '清空', + 'Custom Range' => '自定义', + 'Today' => '今天', + 'Yesterday' => '昨天', + 'Last 7 days' => '最近7天', + 'Last 30 days' => '最近30天', + 'Last month' => '上月', + 'This month' => '本月', + 'Choose' => '选择', + 'Append' => '追加', + 'Upload' => '上传', + 'Memo' => '备注', + 'Parent' => '父级', + 'Params' => '参数', + 'Permission' => '权限', + 'Go back' => '返回上一页', + 'Jump now' => '立即跳转', + 'Advance search' => '高级搜索', + 'Check all' => '选中全部', + 'Expand all' => '展开全部', + 'Begin time' => '开始时间', + 'End time' => '结束时间', + 'Create time' => '创建时间', + 'Flag' => '标志', + 'Gitee' => '码云', + 'Github' => 'Github', + 'QQ group' => 'QQ群', + 'Member center' => '会员中心', + 'Copyrights' => '版权所有', + 'Responsive' => '响应式开发', + 'Languages' => '多语言', + 'Module' => '模块化开发', + 'Extension' => '自由可扩展', + 'Auth' => '权限管理', + 'The fastest framework based on ThinkPHP5 and Bootstrap' => '基于ThinkPHP5和Bootstrap的极速后台开发框架', + 'Features' => '功能特性', + 'Home' => '首页', + 'Store' => '插件市场', + 'Wxapp' => '小程序', + 'Services' => '服务', + 'Download' => '下载', + 'Demo' => '演示', + 'Donation' => '捐赠', + 'Forum' => '社区', + 'Docs' => '文档', + 'User center' => '会员中心', + 'Change password' => '修改密码', + 'Please login first' => '请登录后再操作/Please login first', + 'Uploaded successful' => '上传成功', + 'You can upload up to %d file%s' => '你最多还可以上传%d个文件', + 'You can choose up to %d file%s' => '你最多还可以选择%d个文件', + 'Chunk file write error' => '分片写入失败', + 'Chunk file info error' => '分片文件错误', + 'Chunk file merge error' => '分片合并错误', + 'Chunk file disabled' => '未开启分片上传功能', + 'Cancel upload' => '取消上传', + 'Upload canceled' => '上传已取消', + 'No file upload or server upload limit exceeded' => '未上传文件或超出服务器上传限制', + 'Uploaded file format is limited' => '上传文件格式受限制', + 'Uploaded file is not a valid image' => '上传文件不是有效的图片文件', + 'Are you sure you want to cancel this upload?' => '确定取消上传?', + 'Remove file' => '移除文件', + 'You can only upload a maximum of %s files' => '你最多允许上传 %s 个文件', + 'You can\'t upload files of this type' => '不允许上传的文件类型', + 'Server responded with %s code' => '服务端响应(Code:%s)', + 'File is too big (%sMiB), Max filesize: %sMiB' => '当前上传(%sM),最大允许上传文件大小:%sM', + 'Send verification code' => '发送验证码', + 'Redirect now' => '立即跳转', + 'Operation completed' => '操作成功!', + 'Operation failed' => '操作失败!', + 'Unknown data format' => '未知的数据格式!', + 'Network error' => '网络错误!', + 'Advanced search' => '高级搜索', + 'Invalid parameters' => '未知参数', + 'No results were found' => '记录未找到', + 'Parameter %s can not be empty' => '参数%s不能为空', + 'Token verification error' => 'Token验证错误!', + 'You have no permission' => '你没有权限访问', + 'An unexpected error occurred' => '发生了一个意外错误,程序猿正在紧急处理中', + 'This page will be re-directed in %s seconds' => '页面将在 %s 秒后自动跳转', +]; diff --git a/application/index/lang/zh-cn/ajax.php b/application/index/lang/zh-cn/ajax.php new file mode 100644 index 0000000..0b67a5f --- /dev/null +++ b/application/index/lang/zh-cn/ajax.php @@ -0,0 +1,3 @@ + '栏目', + 'Channel_ids' => '副栏目', + 'Tags' => '标签', + 'Keywords' => '关键字', + 'Description' => '描述', + 'Upload' => '上传', + 'Image' => '缩略图', + 'Images' => '组图', + 'Price' => '价格', + 'Outlink' => '跳转链接', + 'Array key' => '键', + 'Array value' => '值' +]; diff --git a/application/index/lang/zh-cn/cms/diyform.php b/application/index/lang/zh-cn/cms/diyform.php new file mode 100644 index 0000000..d3a0462 --- /dev/null +++ b/application/index/lang/zh-cn/cms/diyform.php @@ -0,0 +1,7 @@ + '上传', + 'Array key' => '键', + 'Array value' => '值', + +]; \ No newline at end of file diff --git a/application/index/lang/zh-cn/index.php b/application/index/lang/zh-cn/index.php new file mode 100644 index 0000000..ca5d8ed --- /dev/null +++ b/application/index/lang/zh-cn/index.php @@ -0,0 +1,5 @@ + '会员中心', + 'Register' => '注册', + 'Login' => '登录', + 'Account' => '账号', + 'Mobile' => '手机号', + 'Email' => '邮箱', + 'Captcha' => '验证码', + 'Lv' => 'Lv', + 'Score' => '积分', + 'Day' => '天', + 'Intro' => '个人介绍', + 'Successions' => '连续登录', + 'Maxsuccessions' => '最长连续登录', + 'Logintime' => '登录时间', + 'Prevtime' => '最后登录', + 'Change' => '修改', + 'Click to edit' => '点击编辑', + 'Email/Mobile/Username' => '邮箱/手机/用户名', + 'Sign up successful' => '注册成功/Sign up successful', + 'Email active successful' => '邮箱激活成功', + 'Username can not be empty' => '用户名不能为空', + 'Username must be 3 to 30 characters' => '用户名必须是3-30位字母或数字 / Username must be 3 to 30 characters', + 'Username must be 6 to 30 characters' => '用户名必须6-30个字符', + 'Account must be 3 to 50 characters' => '账户必须3-50个字符', + 'Password can not be empty' => '密码不能为空', + 'Password must be 6 to 30 characters placeholder' => '密码必须6-30个字符', + 'Password must be 6 to 30 characters' => '密码必须6-30个字符 / Password must be 6 to 30 characters', + 'Email is incorrect' => '邮箱格式不正确', + 'Mobile is incorrect' => '手机格式不正确/Mobile is incorrect', + 'Username already exist' => '用户名已经存在/Username already exist', + 'Nickname already exist' => '昵称已经存在/Nickname already exist', + 'Email already exist' => '邮箱已经存在', + 'Mobile already exist' => '手机号已经存在/Mobile already exist', + 'Username is incorrect' => '用户名不正确/Username is incorrect', + 'Reset password' => '修改密码', + 'Reset password by email' => '通过邮箱', + 'Reset password by mobile' => '通过手机重置', + 'Reset password successful' => '修改密码成功', + 'Account is locked' => '账户已经被锁定/Account is locked', + 'Password is incorrect' => '密码不正确/Password is incorrect', + 'Account is incorrect' => '账户不正确/Account is incorrect', + 'Account not exist' => '账户不存在/Account not exist', + 'Account can not be empty' => '账户不能为空', + 'Username or password is incorrect' => '用户名或密码不正确/Username or password is incorrect', + 'You are not logged in' => '你当前还未登录/You are not logged in', + 'You\'ve logged in, do not login again' => '你已经登录,请不要重复登录/You\'ve logged in, do not login again', + 'This guy hasn\'t written anything yet' => '这个人很懒,啥也没写', + 'Profile' => '个人资料', + 'Old password' => '旧密码', + 'New password' => '新密码', + 'Renew password' => '确认新密码', + 'Change password' => '修改密码', + 'New email' => '新邮箱', + 'New mobile' => '新手机号', + 'Change password successful' => '修改密码成功', + 'Password and confirm password don\'t match' => '两次输入的密码不一致', + 'Captcha is incorrect' => '验证码不正确', + 'Logged in successful' => '登录成功/Logged in successful', + 'Logout successful' => '退出成功/Logout successful', + 'User center already closed' => '会员中心已经关闭', + 'Don\'t have an account? Sign up' => '还没有账号?点击注册', + 'Already have an account? Sign in' => '已经有账号?点击登录', + 'Operation failed' => '操作失败', + 'Invalid parameters' => '参数不正确', + 'Change password failure' => '修改密码失败', + 'All' => '全部', + 'Url' => '物理路径', + 'Imagewidth' => '宽度', + 'Imageheight' => '高度', + 'Imagetype' => '图片类型', + 'Imageframes' => '图片帧数', + 'Preview' => '预览', + 'Filename' => '文件名', + 'Filesize' => '文件大小', + 'Mimetype' => 'Mime类型', + 'Image' => '图片', + 'Audio' => '音频', + 'Video' => '视频', + 'Text' => '文档', + 'Application' => '应用', + 'Zip' => '压缩包', + 'Extparam' => '透传数据/Extparam', + 'Createtime' => '创建日期/Createtime', + 'Uploadtime' => '上传时间/Uploadtime', + 'Storage' => '存储引擎/Storage', +]; diff --git a/application/index/service/CertificateService.php b/application/index/service/CertificateService.php new file mode 100644 index 0000000..b2a7027 --- /dev/null +++ b/application/index/service/CertificateService.php @@ -0,0 +1,164 @@ +playerCertificateModel = new PlayerCertificate(); + } + + /** + * 获取用户证书,如果不存在则创建 + * @Author:Soar + * @Time:2023/12/27 10:58 + * @param array $param + * @return object|int + */ + public function getCertificate(array $param) + { + $match_id = $param['match_id'] ? $param['match_id'] : 0; + $player_id = $param['player_id'] ? $param['player_id'] : 0; + $member_id = $param['member_id'] ? $param['member_id'] : 0; + $club_id = $param['club_id'] ? $param['club_id'] : 0; + + $certificate_res = $this->playerCertificateModel + ->where("match_id", $match_id) + ->where("player_id", $player_id) + ->where("member_id", $member_id) + ->where("club_id", $club_id) + ->find(); + + if (empty($certificate_res)) { + $data['certificate_number'] = CertificateService::generateUUID(); + $data['match_id'] = $match_id; + $data['player_id'] = $player_id; + $data['member_id'] = $member_id; + $data['club_id'] = $club_id; + + return $this->addCertificate($data) ? $data['certificate_number'] : 0; + } + + return $certificate_res->certificate_number ? $certificate_res->certificate_number : 0; + } + + /** + * 根据证书编号 获取详细信息 + * @Author:Soar + * @Time:2023/12/27 13:17 + * @param string $certificate_number + * @return array + */ + public function getCertificateToNumber(string $certificate_number) + { + $info = $this->playerCertificateModel + ->where("certificate_number", $certificate_number) + ->find()->toArray(); + + $archives = new Archives(); + $match_info = $archives->where("id", $info['match_id'])->find(); + if(!empty($info['player_id'])){ + $players_info = Players::get($info['player_id'])->toArray(); + + $res['name'] = $players_info['real_name']; + $res['match_title'] = @$match_info->title ? @$match_info->title : "赛事已被删除"; + $res['certificate_number'] = $info['certificate_number']; + }else{ + $club = new Club(); + $club_info = $club->where('id',$info['club_id'])->find(); + + $res['name'] = $club_info['name_short']; + $res['match_title'] = @$match_info->title ? @$match_info->title : "赛事已被删除"; + $res['certificate_number'] = $info['certificate_number']; + } + return $res; + } + + /** + * 添加证书 + * @Author:Soar + * @Time:2023/12/27 11:01 + * @param array $param + * @return int|string + */ + public function addCertificate(array $param) + { + $data['match_id'] = $param['match_id'] ? $param['match_id'] : 0; + $data['player_id'] = $param['player_id'] ? $param['player_id'] : 0; + $data['member_id'] = $param['member_id'] ? $param['member_id'] : 0; + $data['club_id'] = $param['club_id'] ? $param['club_id'] : 0; + $data['certificate_number'] = $param["certificate_number"] ? $param["certificate_number"] : CertificateService::generateUUID(); + $data['look_num'] = 0; + + return $this->playerCertificateModel->insert($data); + + } + + /** + * 验证证书编号 + * @Author:Soar + * @Time:2023/12/27 11:02 + * @param string $certificate_number + * @return bool + */ + public function checkCertificate($certificate_number) + { + $certificate_res = $this->playerCertificateModel + ->where("certificate_number", $certificate_number) + ->field("certificate_number") + ->find(); + + if ($certificate_res ? true : false) { + $this->updateCertificate($certificate_number); + } + + return $certificate_res ? true : false; + } + + /** + * 更新验证次数 + * @Author:Soar + * @Time:2023/12/27 11:05 + * @param string $certificate_number + * @return bool + */ + public function updateCertificate(string $certificate_number) + { + $res = $this->playerCertificateModel + ->where("certificate_number", $certificate_number) + ->setInc("look_num"); + + return $res ? true : false; + } + + /** + * 生成UUID + * @Author:Soar + * @Time:2023/12/27 10:59 + * @return string + */ + public static function generateUUID() + { + $chars = md5(uniqid(mt_rand(), true)); + $uuid = substr ( $chars, 0, 8 ) . '-' + . substr ( $chars, 8, 4 ) . '-' + . substr ( $chars, 12, 4 ) . '-' + . substr ( $chars, 16, 4 ) . '-' + . substr ( $chars, 20, 12 ); + + return $uuid ; + } + + +} \ No newline at end of file diff --git a/application/index/service/MatchService.php b/application/index/service/MatchService.php new file mode 100644 index 0000000..1fdeb98 --- /dev/null +++ b/application/index/service/MatchService.php @@ -0,0 +1,186 @@ +archivesModel = new Archives(); + $this->archivesModel = new Leaguearchives(); + $this->addonproductModel = db("cms_addonproduct"); + } + public function getAllMatch($param) + { + // $data = array('url'=>'dra.sdoijhflk','full'=>'sdfsd'); + + // return ($data); + // var_dump($this->MATCH_MODEL);exit; + if (!empty($param['date'])) { + $year = $param['date']."-01-01"; + $lastYear = ($param['date']+1)."-01-01"; + + $match_list = $this->archivesModel + ->alias("archives") + ->join("cms_addonproduct" . ' addon', 'addon.id=archives.id', 'LEFT') + ->where("model_id", "eq", $this->MATCH_MODEL) + ->where("channel_id", "eq", 36) + ->where("status", "eq", "normal") + ->where("createtime", ">", strtotime($year)) + ->where("createtime", "<", strtotime($lastYear)) + ->whereNull("deletetime") + ->order("archives.id", "DESC") + ->paginate($param['limit']); + + $years = $this->archivesModel->query("SELECT DATE_FORMAT(addon.starttime, '%Y') as date_key + FROM `peewee_cms_archives` `archives` + LEFT JOIN `peewee_cms_addonproduct` `addon` + ON `addon`.`id`=`archives`.`id` + WHERE ( `model_id` = 2 + AND `channel_id` = 36 + AND `deletetime` IS NULL ) + AND `archives`.`deletetime` IS NULL GROUP BY `date_key`"); + + $result = array("total" => $match_list->total(), "rows" => $match_list->items(), "years" => $years); + + } else { + $match_list = $this->archivesModel + ->alias("archives") + ->join("cms_addonproduct" . ' addon', 'addon.id=archives.id', 'LEFT') + + ->where("model_id", "eq", $this->MATCH_MODEL) + ->where("channel_id", "eq", 36) + ->whereNull("deletetime") + ->where("status", "eq", "normal") + // ->fetchSql(true) + ->order("archives.id", "DESC") + // ->select(); + ->paginate( $param['limit']); + + + foreach ($match_list->items() as $value){ + $value['url'] = 'https://www.fpvone.cn/wurenjijingsusaicheng/'.$value['id'].'.html'; + $value['fullurl'] = 'https://www.fpvone.cn/wurenjijingsusaicheng/'.$value['id'].'.html'; + $value['status_text'] = 'Status Normal'; + } + $match_lists = $this->archivesModel + ->alias("archives") + + ->join("cms_addonproducts" . ' addons', 'addons.id=archives.id', 'LEFT') + ->where("model_id in (6)",) + ->where("channel_id in (73)") + ->whereNull("deletetime") + ->where("status", "eq", "normal") + // ->fetchSql(true) + ->order("archives.id", "DESC") + // ->select(); + ->paginate( $param['limit']); + // var_dump(str_replace("www", "dra", $match_lists->items()[0]['url']));exit; + // $match_list->union($match_lists, true); + // $change = $match_lists->items()[0]->toArray(); + // $change['url'] = ''; + $match_lists->items()[0]['url'] = 'https://dra.fpvone.cn/matchs/804.html'; + $match_lists->items()[0]['fullurl'] = 'https://dra.fpvone.cn/matchs/804.html'; + $match_lists->items()[0]['status_text'] = 'Status Normal'; + // $match_lists->items()[0]['admin_id'] = 666; + // var_dump($change[0]->toArray()['url']);exit; + // foreach ($match_lists->items()[0] as &$val){ + // $val['url'] = str_replace("www", "dra", $val['url']); + // } + // var_dump($match_lists);exit; + $mergedArray = array_merge($match_lists->items(),$match_list->items()); + // var_dump($mergedArray);exit; + array_multisort(array_column($mergedArray,'stime'), SORT_DESC,$mergedArray); + $years = $this->archivesModel->query("SELECT DATE_FORMAT(addon.starttime, '%Y') as date_key + FROM `peewee_cms_archives` `archives` + LEFT JOIN `peewee_cms_addonproduct` `addon` + ON `addon`.`id`=`archives`.`id` + WHERE ( `model_id` = 2 + AND `channel_id` = 36 + AND `deletetime` IS NULL ) + AND `archives`.`deletetime` IS NULL GROUP BY `date_key`"); + + + $result = array("total" => $match_lists->total(), "rows" => $mergedArray, "years" => $years); + + } + + if (!empty($result['rows'])) { + foreach ($result['rows'] as $key => $val) { + $matchContestant = new MatchContestant(); + //宁波站 + if($val->id == 1007){ + $val->entry_player = $matchContestant->where("match_id", $val->id) + ->whereIn("status", [1, 2]) + ->count("*"); + }else{ + $val->entry_player = '统计中'; + } + + } + } + + return $result; + } + + /** + * 根据身份证查询选手是否参加赛事 + * @Author:Soar + * @Time:2023/12/6 14:21 + * @param $match_id + * @param $user_id + * @return void + */ + public function getMatchUserInfo($match_id, $user_id) + { + $players = new Players(); + $matchContestant = new MatchContestant(); + $player_info = $players->where("card_number", "eq", $user_id)->select(); + + if (empty($player_info)) { + return; + } + + foreach ($player_info as $value) { + $match_info = $matchContestant->where("player_id", "eq", $value->id) + ->where("match_id", "eq", $match_id) + ->order("id", "DESC") + ->find(); + if (!empty($match_info)) { + $player_info = $value; + break; + } + } + + + if (empty($match_info)) { + return; + } + + $archives_info = $this->archivesModel + ->where("id", "eq", $match_id) + ->find(); + + if (empty($archives_info)) { + return; + } + + $res['title'] = $archives_info->title; + $res['status'] = $match_info->status; + $res['name'] = $player_info->real_name; + + return $res; + } +} \ No newline at end of file diff --git a/application/index/service/PlayerService.php b/application/index/service/PlayerService.php new file mode 100644 index 0000000..2d55ddb --- /dev/null +++ b/application/index/service/PlayerService.php @@ -0,0 +1,131 @@ +check($add_data)){ + print_r("aaa");exit; + } + } +} \ No newline at end of file diff --git a/application/index/service/RankingService.php b/application/index/service/RankingService.php new file mode 100644 index 0000000..eaffb7e --- /dev/null +++ b/application/index/service/RankingService.php @@ -0,0 +1,1172 @@ +rankingModel = new MatchRanking(); + } + + public function getRank($course = 32, $match_id) + { + switch ($course) { + case 32: + $limit = 64; + break; + case 16: + $limit = 32; + break; + case 8: + $limit = 8; + break; + case 4: + $limit = 4; + break; + case 1: + $limit = 99; + break; + } + + $all_ranking_bast = $this->rankingModel + ->where("match_id" , "eq", $match_id) + ->where("course", "eq", $course) + ->where("bast_performance", 1) + ->field("player_name, course, match_id, promoted, fly_num, times, grouping, course, player_id, custom_sorting, group_score") + //->order("round_score") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->limit($limit) + ->select(); + + if (!empty($all_ranking_bast[0]->custom_sorting)) { + $last_ages = array_column($all_ranking_bast,'custom_sorting'); + array_multisort($last_ages ,SORT_ASC,$all_ranking_bast); + } + + return $all_ranking_bast; + } + + public function getAllRank ($course = 32, $match_id) + { + $list = $this->rankingModel->where([ + 'course' => $course, + 'match_id' => $match_id, + 'promoted' => self::PROMOTED + ])->field("player_name, course, match_id, promoted, fly_num, times, grouping, course, player_id, custom_sorting, group_score") + ->order("group_score", "ASC") + ->select(); + + if (!empty($list[0]->custom_sorting)) { + $last_ages = array_column($list,'custom_sorting'); + array_multisort($last_ages ,SORT_ASC,$list); + } + + return $list; + } + + /** + * 根据赛事ID和赛程获取所有选手成绩, 已废弃 + * @Author:Soar + * @Time:2023/12/13 9:28 + * @param $course 赛程 + * @param $match_id 赛事id + * @return bool|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + #[Deprecated] + public function getRankAll($course = 32, $match_id = 180) + { + if (empty($course)) { + $course = 32; + } + $row = $this->rankingModel->where([ + 'course' => $course, + 'match_id' => $match_id, + ])->field("player_name, course, match_id, promoted, fly_num, times, grouping, course, player_id, integral, finals_round, id") + ->order("group_score", "ASC") + ->select(); + + $players = new Players(); + $user = new User(); + foreach ($row as $vals) { + $user_info = $user->where(['member_number' => $vals->player_id])->find(); + if (!empty($user_info)) { + $player_info = $players->where(['member_id' => $user_info->id])->find(); + if (!empty( $player_info)) { + $vals->style_photo = $player_info->style_photo; + $vals->province = $player_info->province; + $vals->age = $player_info->age; + } + } + + } + + return $row; + } + + /** + * 替换上面的接口 + * 根据赛程信息和赛事ID 获取所有选手的信息 + * @Author:Soar + * @Time:2023/12/13 9:27 + * @param int $course 赛程 + * @param int $match_id 赛事id + * @return array + */ + public function getRoundAllPlayer(int $course, int $match_id) + { + $data = $this->rankingModel + ->Distinct(true) + ->where('match_id', $match_id) + ->where("course", $course) + ->field('player_id, grouping, player_name') + ->select(); + + $data = collection($data)->toArray(); + + if ($data) { + $players = new Players(); + $user = new User(); + + foreach ($data as $key => $value) { + $user_info = $user->where(['member_number' => $value['player_id']])->find(); + if ($user_info) { + $player_info = $players->where(['member_id' => $user_info->id])->find(); + if ($player_info) { + $data[$key]['style_photo'] = $player_info->style_photo; + $data[$key]['province'] = $player_info->province; + $data[$key]['age'] = $player_info->age; + $data[$key]['player_name'] = $player_info->real_name; + } + } + } + } + + return $data; + } + + public function getMatchAll($match_id, $course = 32) + { + if (empty($course)) { + $course = 32; + } + + return $this->rankingModel->where([ + 'course' => $course, + 'match_id' => $match_id, + ])->field("player_name, course, match_id, promoted, fly_num, times, grouping, course, player_id") + ->select(); + } + + public function getSemiFinals($match_id) + { + $row = $this->rankingModel->where([ + 'course' => 4, + 'match_id' => 180, + 'promoted' => self::PROMOTED + + ])->field("player_name, course, match_id, promoted, fly_num, times, grouping, course, player_id") + ->select(); + + $players = new Players(); + $user = new User(); + foreach ($row as $vals) { + $user_info = $user->where(['member_number' => $vals->player_id])->find(); + $player_info = $players->where(['member_id' => $user_info->id])->find(); + $vals->style_photo = $player_info->style_photo; + $vals->province = $player_info->province; + $vals->age = $player_info->age; + // 获取64进32强数据 + $vals->achievement_list = $this->rankingModel->where([ + 'match_id' => $vals->match_id, + 'player_id' => $vals->player_id, + 'player_name' => $vals->player_name + ])->field("course, times")->select(); + } + + return $row; + } + + /** + * 获取分组选手信息以及成绩列表 + * @Author:Soar + * @Time:2023/11/20 16:25 + * @param $param + * @return bool|\PDOStatement|string|\think\Collection + * @throws \think\db\exception\DataNotFoundException + * @throws \think\db\exception\ModelNotFoundException + * @throws \think\exception\DbException + */ + public function getRankGroup($param) + { + $list = $this->rankingModel->where([ + 'course' => $param['course'], + 'match_id' => $param['match_id'], + 'grouping' => $param['group'], + 'bast_performance' => 1 + ])->field("player_name, course, match_id, promoted, fly_num, times, grouping, player_id, other_round") + ->select(); + + + $players = new Players(); + $user = new User(); + foreach ($list as $val) { + $user_info = $user->where(['member_number' => $val->player_id])->find(); + if (!empty($user_info)) { + $player_info = $players->where(['member_id' => $user_info->id])->find(); + if (!empty($player_info)) { + if (!empty($player_info->style_photo)) { + $val->style_photo = $player_info->style_photo; + } else { + $val->style_photo = $player_info->player_pic; + } + $val->age = $player_info->age; + $val->province = $player_info->province; + } else { + $val->style_photo = null; + $val->age = null; + $val->province = null; + } + + } + + $val->achievement_list = $this->rankingModel->where([ + 'match_id' => $val->match_id, + 'player_id' => $val->player_id, + 'player_name' => $val->player_name, + ])->field("course, times, fly_num, integral, other_round")->select(); + } + + $list_['items'] = $list; + $list_['round'] = $this->rankingModel->where("match_id", "eq", $param['match_id']) + ->where("course", "eq", $param['course']) + ->max("other_round"); + + return $list_; + } + + public function getRankFinals($match_id,$name = 0) + { + $collections = $this->rankingModel->where('match_id', 'eq', $match_id) + ->where("course", "eq", 4) + ->where("bast_performance", 1) + ->field("player_name, course, match_id, promoted, fly_num, times, grouping, course, player_id, other_round") + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->limit(4) + ->select(); + + if($name == 1){ + //名字加*号 + foreach ($collections as &$v){ + $str = $v['player_name']; + $strlen = mb_strlen($str,'UTF8'); + if($strlen == 2){ + $real_name=mb_substr($str,0,1,'UTF8').'*'; + } + if($strlen > 2){ + $tmp_str=mb_substr($str,0,$strlen-2,'UTF8').'*'; + $real_name=$tmp_str.mb_substr($str,-1,1,'UTF8'); + } + $v['player_name']= $real_name; + } + } + // exit; + // var_dump($collections);exit; + + $player_ids = array_column($collections, "player_id"); + + if(empty($collections)) { + return []; + } + + $players = new Players(); + $user = new User(); + + foreach ($collections as $val) { + // 获取选手所有积分 + $val->integral_sum = $this->rankingModel->where('match_id', 'eq', $match_id) + ->where("course", "eq", 1) + ->where("player_id", "eq", $val->player_id) + ->where("other_round", "neq", "加赛") + ->sum("integral"); + + $user_info = $user->where(['member_number' => $val->player_id])->find(); + if (!empty($user_info)) { + $player_info = $players->where(['member_id' => $user_info->id])->find(); + if (!empty($player_info)) { + if (!empty($player_info->style_photo)) { + $val->style_photo = $player_info->style_photo; + } else { + $val->style_photo = $player_info->player_pic; + } + $val->age = $player_info->age; + $val->province = $player_info->province; + } else { + $val->style_photo = null; + $val->age = null; + $val->province = null; + } + } + + $val->achievement_list = $this->rankingModel->where([ + 'match_id' => $val->match_id, + 'player_id' => $val->player_id, + 'bast_performance' => 1 + ])->where("course", "neq", 1) + ->field("player_name, fly_num, times, integral, finals_round, course, led_color, channel, grouping, other_round") + ->order("other_round", "asc") + ->select(); + + } + + $sort = array_column($collections, "integral_sum"); + + array_multisort($sort, SORT_DESC, $collections); + // var_dump($collections[0]->toArray());exit; + foreach ($collections as $key => $values) { + // 根据积分总和把加赛的人分到一个数组里 + // var_dump($values->player_id);exit; + $extra_series_arr[$values->integral_sum][$values->player_id] = $values; + } + // var_dump($extra_series_arr);exit; + // 如果数组不为空,如果没有加赛,那么肯定一个人一条数据 + // 如果积分相同的两个人,肯定数组的长度就会小于4,就会进行下列的查询 + if (!empty($extra_series_arr) && count($extra_series_arr) < 4) { + $extra_series_ranking = 0; + + // 循环需要判断加赛的数组 + foreach ($extra_series_arr as $value) { + // 如果该数组里只有一条数组,那么就说明该选手没有参与加赛 + if (count($value) > 1) { + unset($fly_three); + unset($fly_two); + unset($fly_one); + unset($fly_dnf); + // 对此数组进行排列 查询该几人的加赛数据 + $extra_series_player_ids = array_column($value, "player_id"); + // 查询加赛的数据 + $extra_series_list = $this->rankingModel->where('match_id', 'eq', $match_id) + ->whereIn("player_id", $extra_series_player_ids) + ->where("course", "eq", 1) + ->where("other_round", "eq", "加赛") + ->order("achievement", "ASC") + ->select(); + + $player_num = 0; + // 因为当时没有用到 CASE fly_num + // WHEN '3' THEN 1 + // WHEN '2' THEN 2 + // WHEN '1' THEN 3 + // WHEN 'DNF' THEN 4 + // ELSE 5 + // END 所以只能循环判断圈数,毕竟他们只上传最优的加赛成绩 + // 把3>2>1>DNF的数据取出来 + if (!empty($extra_series_list)) { + foreach ($extra_series_list as $val) { + if ($val->fly_num == 3) { + $fly_three[] = $val; + $player_num++; + } elseif ($val->fly_num == 2) { + $fly_two[] = $val; + $player_num++; + } elseif ($val->fly_num == 2) { + $fly_one[] = $val; + $player_num++; + } elseif ($val->fly_num == "DNF") { + $fly_dnf[] = $val; + $player_num++; + } + } + + $winKey = 0; + + // 下面取出每个圈数的人,根据 achievement 进行排名 + // achievement 当初的计算方式 03:01.00 + // 03 * 60 * 100; + // 01 * 100; + // 按照从小到大的排序 + + if (!empty($fly_three)) { + $sort = array_column($fly_three, "achievement"); + array_multisort($sort, SORT_ASC, $fly_three); + foreach ($fly_three as $val) { + if ($winKey == 0) { + $value[$val->player_id]->win_extra_series = 1; + } + $new_collections[$extra_series_ranking] = $value[$val->player_id]; + $extra_series_ranking++; + $winKey++; + } + } + if (!empty($fly_two)) { + $sort = array_column($fly_two, "achievement"); + array_multisort($sort, SORT_ASC, $fly_two); + foreach ($fly_two as $val) { + if ($winKey == 0) { + $value[$val->player_id]->win_extra_series = 1; + } + $new_collections[$extra_series_ranking] = $value[$val->player_id]; + $extra_series_ranking++; + $winKey++; + } + } + + if (!empty($fly_one)) { + $sort = array_column($fly_one, "achievement"); + array_multisort($sort, SORT_ASC, $fly_one); + foreach ($fly_one as $val) { + if ($winKey == 0) { + $value[$val->player_id]->win_extra_series = 1; + } + $new_collections[$extra_series_ranking] = $value[$val->player_id]; + $extra_series_ranking++; + $winKey++; + } + } + + if (!empty($fly_dnf)) { + $sort = array_column($fly_dnf, "achievement"); + array_multisort($sort, SORT_ASC, $fly_dnf); + foreach ($fly_dnf as $val) { + if ($winKey == 0) { + $value[$val->player_id]->win_extra_series = 1; + } + $new_collections[$extra_series_ranking] = $value[$val->player_id]; + $extra_series_ranking++; + $winKey++; + } + } + + // 如果为空的成绩,那么就会走这里 + if (count($value) != $player_num) { + foreach ($value as $val) { + if (!in_array($val,$new_collections)) { + array_push($new_collections, $val); + $extra_series_ranking++; + } + } + } + } else { + // 如果不存在加赛数据,那么就按照顺序排序 + foreach ($value as $val) { + $new_collections[$extra_series_ranking] = $value[$val->player_id]; + $extra_series_ranking++; + } + } + + } else { + // 走到这里,说明他没有加赛,按照积分从高到低排序的顺序排序 + $new_collections[$extra_series_ranking] = array_shift($value); + $extra_series_ranking++; + } + } + + $collections = $new_collections; + } + + // 获取所有选手的决赛记录 + $collections['integral_list'] = $this->rankingModel->where('match_id', 'eq', $match_id) + ->where("course", "eq", 1) + ->whereIn("player_id", $player_ids) + ->field("player_name, player_id, match_id, integral, finals_round, fly_num, times, other_round, other_round as finals_round") + ->order("other_round") + ->select(); + // var_dump($collections['integral_list']);exit; + foreach ($collections['integral_list'] as $k => &$v){ + // $v['player_name'] = '金志浩'; + // var_dump($v['player_name']); + } + + return $collections; + + + + } + + public function getRankFinalsold($match_id) + { + $collection = $this->rankingModel->where('match_id', 'eq', $match_id) + ->where("course", "eq", 1) + ->field("player_name, player_id, match_id, sum(integral) as integrals") + ->order("integrals", "DESC") + ->group("player_name") + ->select(); + + if (count($collection) < 4) { + foreach ($collection as $value) { + $finalsId[] = $value->player_id; + } + $collections = $this->rankingModel->where('match_id', 'eq', $match_id) + ->where("course", "eq", 4) + ->whereNotIn("player_id", $finalsId) + ->field("player_name, player_id, match_id, sum(integral) as integrals") + ->order("integrals", "DESC") + ->group("player_name") + ->select(); + + $collection = array_merge($collection, $collections); + + } + + $players = new Players(); + $user = new User(); + + foreach ($collection as $values) { + $user_info = $user->where(['member_number' => $values->player_id])->find(); + if (!empty($user_info)) { + $player_info = $players->where(['member_id' => $user_info->id])->find(); + if (!empty($player_info)) { + if (!empty($player_info->style_photo)) { + $values->style_photo = $player_info->style_photo; + } else { + $values->style_photo = $player_info->player_pic; + } + $values->age = $player_info->age; + $values->province = $player_info->province; + } else { + $values->style_photo = null; + $values->age = null; + $values->province = null; + } + } + + $values->achievement_list = $this->rankingModel->where([ + 'match_id' => $values->match_id, + 'player_id' => $values->player_id, + 'player_name' => $values->player_name, + ]) + ->order("finals_round", "ASC") + ->field("player_name, fly_num, times, integral, finals_round")->select(); + } + + return $collection; + } + + /** + * 获取单个飞手的本次比赛所有记录 + * @Author:Soar + * @Time:2023/11/23 10:45 + * @param $match_id 赛事ID + * @param $player_id 飞手ID + * @return void + */ + public function getPlayerRanking($match_id, $player_id) + { + // 获取决赛记录 + $collection = $this->rankingModel->where('match_id', 'eq', $match_id) + ->where("player_id", "eq", $player_id) + ->where("course", "eq", 1) + ->field("player_name, player_id, match_id, sum(integral) as integrals, other_round") + ->order("integrals", "DESC") + ->group("player_name") + ->select(); + // 如果没参加决赛 就获取所有参赛记录 + $players = new Players(); + $user = new User(); + if (empty($collection)) { + $user_info = $user->where(['member_number' => $player_id])->find(); + if (!empty($user_info)) { + $player_info = $players->where(['member_id' => $user_info->id])->find(); + if (empty($player_info)) { + return null; + } + + $playerInfo['player_id'] = $user_info->member_number; + if (!empty($player_info->style_photo)) { + $playerInfo['style_photo'] = $player_info->style_photo; + } else { + $playerInfo['style_photo'] = $player_info->player_pic; + } + $playerInfo['player_name'] = $player_info->real_name; + $playerInfo['age'] = $player_info->age; + $playerInfo['province'] = $player_info->province; + + $playerInfo['achievement_list'] = $this->rankingModel->where([ + 'match_id' => $match_id, + 'player_id' => $user_info->member_number, + ])->order("finals_round", "ASC") + ->field("player_name, fly_num, times, integral, finals_round, course, other_round")->select(); + + return $playerInfo; + + } + } + + $user_info = $user->where(['member_number' => $player_id])->find(); + if (!empty($user_info)) { + foreach ($collection as $val) { + $player_info = $players->where(['member_id' => $user_info->id])->find(); + if (empty($player_info)) { + return null; + } + + if (!empty($player_info->style_photo)) { + $val->style_photo = $player_info->style_photo; + } else { + $val->style_photo = $player_info->player_pic; + } + $val->age = $player_info->age; + $val->province = $player_info->province; + $val->achievement_list = $this->rankingModel->where([ + 'match_id' => $match_id, + 'player_id' => $user_info->member_number, + ])->where("course", "<>", '1') + ->order("finals_round", "ASC") + ->field("player_name, fly_num, times, integral, finals_round, course")->select(); + + return $val; + } + } + } + + /** + * 根据飞手ID、赛事ID、赛程获取飞手比赛记录 + * @Author:Soar + * @Time:2023/12/12 13:19 + * @param int $match_id 赛事id + * @param int $course 赛程 + * @param int $player_id 飞手id + * @return void + */ + public function getRoundPlayer(int $match_id, int $course, int $player_id) + { + $res['other'] = $this->rankingModel + ->where("match_id", $match_id) + ->where("course", $course) + ->where("player_id", $player_id) + //->orderRaw("CASE fly_num +// WHEN '3' THEN 1 +// WHEN '2' THEN 2 +// WHEN '1' THEN 3 +// WHEN 'DNF' THEN 4 +// ELSE 5 +// END") + ->order('other_round', 'asc') + ->field("player_name, fly_num, times, integral, finals_round, course, other_round") + ->select(); + + $res['best'] = $this->rankingModel + ->where("match_id", $match_id) + ->where("course", $course) + ->where("player_id", $player_id) + ->where("bast_performance", 1) + ->field("player_name, fly_num, times, integral, finals_round, course") + ->find(); + + $players = new Players(); + $user = new User(); + + $user_info = $user->where(['member_number' => $player_id])->find(); + + if (!empty($user_info)) { + $player_info = $players->where(['member_id' => $user_info->id])->find(); + if (!empty($player_info)) { + $res['player_info']['age'] = $player_info->age; + $res['player_info']['province'] = $player_info->province; + } + } + + return $res; + } + + /** + * 首页报名查询飞手所有成绩接口 + * @Author:Soar + * @Time:2023/12/18 11:17 + * @param $match_id + * @param $course + * @return void + */ + public function getIndexRanking($match_id, $course) + { + $list = $this->rankingModel + ->where("match_id", $match_id) + ->where("course", $course) + ->where("bast_performance", 1) + ->field("player_name, fly_num, times, integral, finals_round, course, custom_sorting, group_score, grouping, player_id") + ->select(); + + if (!empty($list[0]->custom_sorting)) { + $last_ages = array_column($list,'custom_sorting'); + array_multisort($last_ages ,SORT_ASC,$list); + } + + + return $list; + } + + public function getIndexRankingScore($match_id, $course) + { + $list = $this->rankingModel + ->where("match_id", $match_id) + ->where("course", $course) + ->where("bast_performance", 1) + ->field("player_name, fly_num, times, integral, finals_round, course, custom_sorting, group_score, grouping, player_id") + ->order("group_score", "ASC") + ->select(); + + if (!empty($list[0]->custom_sorting)) { + $last_ages = array_column($list,'custom_sorting'); + array_multisort($last_ages ,SORT_ASC,$list); + } + + + return $list; + } + + public function getIndexPlayerRanking ($match_id, $player_id) + { + // 获取决赛记录 + $collection = $this->rankingModel->where('match_id', 'eq', $match_id) + ->where("player_id", "eq", $player_id) + ->where("course", "eq", 1) + ->field("player_name, player_id, match_id, sum(integral) as integrals, other_round") + ->order("integrals", "DESC") + ->group("player_name") + ->select(); + // 如果没参加决赛 就获取所有参赛记录 + $players = new Players(); + $user = new User(); + if (empty($collection)) { + $user_info = $user->where(['member_number' => $player_id])->find(); + if (!empty($user_info)) { + $player_info = $players->where(['member_id' => $user_info->id])->find(); + if (empty($player_info)) { + return null; + } + + $playerInfo['player_id'] = $user_info->member_number; + if (!empty($player_info->style_photo)) { + $playerInfo['style_photo'] = $player_info->style_photo; + } else { + $playerInfo['style_photo'] = $player_info->player_pic; + } + $playerInfo['player_name'] = $player_info->real_name; + $playerInfo['age'] = $player_info->age; + $playerInfo['province'] = $player_info->province; + + $playerInfo['achievement_list'] = $this->rankingModel->where([ + 'match_id' => $match_id, + 'player_id' => $user_info->member_number, + ])->order("finals_round", "ASC") + ->field("player_name, fly_num, times, integral, finals_round, course, other_round")->select(); + + return $playerInfo; + + } + } + + $user_info = $user->where(['member_number' => $player_id])->find(); + if (!empty($user_info)) { + foreach ($collection as $val) { + $player_info = $players->where(['member_id' => $user_info->id])->find(); + if (empty($player_info)) { + return null; + } + + if (!empty($player_info->style_photo)) { + $val->style_photo = $player_info->style_photo; + } else { + $val->style_photo = $player_info->player_pic; + } + $val->age = $player_info->age; + $val->province = $player_info->province; + $val->achievement_list = $this->rankingModel->where([ + 'match_id' => $match_id, + 'player_id' => $user_info->member_number, + ])->where("course", "<>", '1')->where("bast_performance", 1) + ->order("finals_round", "ASC") + ->field("player_name, fly_num, times, integral, finals_round, course")->select(); + + return $val; + } + } + } + + public function getOneRanking(int $match_id, int $player_id, int $course) + { + return $this->rankingModel + ->where('match_id', 'eq', $match_id) + ->where("course", $course) + ->where("player_id", $player_id) + ->where("bast_performance", 1) + ->field("player_id, match_id, player_name, grouping, times, fly_num") + ->find(); + } + + public function getFinalsIntegral(int $match_id, int $player_id) + { + return $this->rankingModel + ->where('match_id', 'eq', $match_id) + ->where("course", 1) + ->where("player_id", $player_id) + ->field("sum(integral) as sum_integral, player_id, match_id, player_name, grouping") + ->group("player_id") + ->find(); + } + + public function getPlayerInfo(int $player_id) + { + $user = new User(); + $user_info = $user + ->alias("a") + ->where("a.member_number", $player_id) + ->join("peewee_players b", "a.id = b.member_id", "LEFT") + ->find()->toArray(); + + $player['age'] = $user_info['age']; + $player['player_id'] = $user_info['member_number'] ? $user_info['member_number'] : " "; + $player['province'] = $user_info['province']; + $player['city'] = $user_info['city']; + $player['district'] = $user_info['district']; + $player['birthday'] = $user_info['birthday']; + $player['name'] = $user_info['real_name']; + $player['gender'] = $user_info['gender']; + $player['member_id'] = $user_info['member_id']; + $player['player_pic'] = $user_info['style_photo'] ? $user_info['style_photo'] : $user_info['player_pic']; + // if (!empty($player['player_pic'])) { + // $player['player_pic'] = config("upload.cdnurl") ? config("upload.cdnurl").$player['player_pic'] : config("upload.uploadurl").$player['player_pic']; + // } + return $player; + } + + public function getSortRanking ($player_ids, $match_id, $player_id, $sort, $course) + { + + if ($course == 1) { + $sort_list = $this->rankingModel + ->where("match_id", $match_id) + ->where("course", $course) + ->whereIn("player_id", $player_ids) + ->group("player_id") + ->field("player_id, match_id, player_name, led_color, channel, sum(integral) as sum_integral, grouping") + ->order("sum_integral", "DESC") + ->select(); + } else { + $sort_list = $this->rankingModel + ->where("match_id", $match_id) + ->where("bast_performance", 1) + ->where("course", $course) + ->whereIn("player_id", $player_ids) + ->orderRaw("CASE fly_num + WHEN '3' THEN 1 + WHEN '2' THEN 2 + WHEN '1' THEN 3 + WHEN 'DNF' THEN 4 + ELSE 5 + END") + ->order('datetime_value', 'asc') + ->select(); + } + + + $sort_list = collection($sort_list)->toArray(); + + $ids = array_column($sort_list, "player_id"); + + if (in_array($player_id, $ids)) { + foreach ($sort_list as $value) { + if ($player_id != $value['player_id']) { + ++$sort; + } else { + return ++$sort; + } + } + } else { + foreach ($player_ids as $key => $val) { + foreach ($ids as $value) { + if ($val == $value) { + unset($player_ids[$key]); + } + } + } + sort($player_ids); + $sort = count($ids) + $sort; + + switch ($course) { + case 1: + $course = 4; + break; + case 4: + $course = 8; + break; + case 8: + $course = 16; + break; + case 16: + $course = 32; + break; + } + + return $this->getSortRanking($player_ids, $match_id, $player_id, $sort, $course); + } + + } + + /** + * 获取最优数据 + * @Author:Soar + * @Time:2023/12/22 15:20 + * @param int $match_id + * @param int $player_id + * @return array + */ + public function getPlayerBast(int $match_id, int $player_id, $user) + { + // 获取参赛所有选手 + $player_ids = $this->rankingModel + ->where('match_id', 'eq', $match_id) + ->where("course", 32) + ->where("bast_performance", 1) + ->field("player_id") + ->select(); + // 先判断是否参与比赛 + $isNull = $this->rankingModel->whereNull("times") + ->where("course", 32) + ->where('match_id', 'eq', $match_id) + ->where("bast_performance", 1) + ->where("player_id", $player_id) + ->find(); + + if ($isNull) { + $bast_info['sort'] = 0; + return $bast_info; + } + + $player_ids = collection($player_ids)->toArray(); + $ids = array_column($player_ids, "player_id"); + + // 获取决赛的数据 + $sort = $this->getSortRanking($ids, $match_id, $player_id, 0, 1); + // $finals_info = collection($this->getRankFinals($match_id))->toArray(); + // unset($finals_info['integral_list']); + // 排名之前查询是否有加赛 + // $bast_info['course'] = "决赛"; + // $bast_info['sort'] = array_search($player_id, array_column($finals_info, 'player_id')) + 1; + $bast_info['sort'] = $sort; + //$array_column = array_column($finals_info, "player_id"); + + /* if (!in_array($player_id, $array_column)) { + $bast = $this->rankingModel + ->where('match_id', 'eq', $match_id) + ->where("player_id", $player_id) + ->where("bast_performance", 1) + ->order('id', "DESC") + ->find(); + + $bast_info['course'] = $bast->course; + $bast_info['sort'] = $bast->custom_sorting ? $bast->custom_sorting : $bast->group_score; + } + */ + // 查询是否属于青年组 + $today = date('Y-m-d'); // 当前日期 + $previousDate = date('Y-01-01', strtotime('-15 years', strtotime($today))); // 十五年前的日期 + if ($previousDate <= $user['birthday']) { + // 查询青年组符合年龄的 + $bastYoung = $this->getYoungSort($previousDate, $match_id, $player_id, true); + $bast_info['young_sort'] = $bastYoung; + } + + return $bast_info; + } + + public function getYoungSort($date, $match_id, $player_id, $isApi = false) + { + // 获取所有这个年龄段的飞手 + $players = new Players(); + // 按照生日日期获取所有参赛选手 + $players_list = $players + ->alias("a") + ->join('user users', 'users.id = a.member_id', 'LEFT') + ->where("a.birthday", ">=", $date) + ->field("a.id, users.member_number, a.birthday") + ->order("a.birthday") + ->select(); + + $ids = array_column($players_list, "id"); + // 获取所有参赛选手 + $matchContestant = new \app\common\model\MatchContestant(); + // 去报名表查询所有参赛的飞手 + $match_list = $matchContestant->whereIn("player_id", $ids) + ->where("match_id", "eq", $match_id) + ->select(); + // 根据参赛选手 加上player_member_id + foreach ($match_list as $vals) { + foreach ($players_list as $values) { + if ($vals->player_id == $values->id) { + $vals->player_member_id = $values->member_number; + break; + } + } + } + + $member_number_ids = array_column($match_list, "player_member_id"); + // 根据User表中的 member_number 获取比赛信息 权重 决赛 > 半决赛 > 8强 > 16强 > 32强 > 预选赛 + // 决赛数据 + $rankingService = new \app\index\service\RankingService(); + $achievement['finals'] = $rankingService->getRankFinals($match_id); + + if (empty($achievement['finals'])) { + if ($isApi) { + return []; + } + $this->error("决赛暂未结束"); + } + unset($achievement['finals']['integral_list']); + // 遍历查询是否有 + foreach ($achievement['finals'] as $key => $item) { + if (!in_array($item->player_id, $member_number_ids)) { + unset($achievement['finals'][$key]); + } else { + unset($member_number_ids[array_search($item->player_id, $member_number_ids)]); + } + } + + $toArray = collection($achievement['finals'])->toArray(); + $array_column = array_column($toArray, "player_id"); + if (in_array($player_id,$array_column )) { + return array_search($player_id, array_column($toArray, 'player_id')) + 1; + } + + // 初始化model + $matchRanking = new MatchRanking(); + // 查询 进入半决赛的成绩 + $achievement['semifinal'] = $matchRanking->whereIn("player_id", $member_number_ids) + ->where("match_id", "eq", $match_id) + ->where("bast_performance", 1) + ->where("course", "eq", 4) + ->order("group_score") + ->select(); + + $toArray = collection($achievement['semifinal'])->toArray(); + $array_column = array_column($toArray, "player_id"); + if (in_array($player_id,$array_column )) { + return array_search($player_id, array_column($toArray, 'player_id')) + 1; + } + + foreach ($achievement['semifinal'] as $val) { + unset($member_number_ids[array_search($val->player_id, $member_number_ids)]); + } + + // 查询 进入8强的成绩 + $achievement['final_eight'] = $matchRanking->whereIn("player_id", $member_number_ids) + ->where("match_id", "eq", $match_id) + ->where("bast_performance", 1) + ->where("course", "eq", 8) + ->order("group_score") + ->select(); + + $toArray = collection($achievement['final_eight'])->toArray(); + $array_column = array_column($toArray, "player_id"); + if (in_array($player_id,$array_column )) { + return array_search($player_id, array_column($toArray, 'player_id')) + 1; + } + + foreach ($achievement['final_eight'] as $val) { + unset($member_number_ids[array_search($val->player_id, $member_number_ids)]); + } + + // 查询 进入16强数据 + $achievement['final_sixteen'] = $matchRanking->whereIn("player_id", $member_number_ids) + ->where("match_id", "eq", $match_id) + ->where("bast_performance", 1) + ->where("course", "eq", 16) + ->order("group_score") + ->select(); + + $toArray = collection($achievement['final_sixteen'])->toArray(); + $array_column = array_column($toArray, "player_id"); + if (in_array($player_id,$array_column )) { + return array_search($player_id, array_column($toArray, 'player_id')) + 1; + } + + foreach ($achievement['final_sixteen'] as $val) { + unset($member_number_ids[array_search($val->player_id, $member_number_ids)]); + } + // 查询 进入32强数据 + $achievement['final_thirty_two'] = $matchRanking->whereIn("player_id", $member_number_ids) + ->where("match_id", "eq", $match_id) + ->where("bast_performance", 1) + ->where("course", "eq", 32) + ->order("group_score") + ->select(); + $toArray = collection($achievement['final_thirty_two'])->toArray(); + $array_column = array_column($toArray, "player_id"); + if (in_array($player_id,$array_column )) { + return array_search($player_id, array_column($toArray, 'player_id')) + 1; + } + foreach ($achievement['final_thirty_two'] as $val) { + unset($member_number_ids[array_search($val->player_id, $member_number_ids)]); + } + + return 0; + } + + + + /** + * 计时换算成毫秒 + * @Created by PhpStorm. + * @Author:Soar + * @Time:2023/11/15 15:11 + * @param $times + * @return float|int|mixed|string + */ + private function getAllMs($times) + { + $minute_ms = 0; + $second_ms = 0; + $ms = 0; + // 截取最后的毫秒 + $allTime = explode(".", $times); + if (count($allTime) != 2) { + $this->error("成绩格式有误!"); + } + $ms = $allTime[1]; + // 截取分秒 + $minute = explode(":", $allTime[0]); + if (count($minute) != 2) { + $this->error("成绩格式有误!"); + } + if ($minute[0] != 00) { + // 如果第一位不是 00 则代表每分钟 + $minute_ms = $minute[0] * 60 * 100; + } + + if ($minute[1] != 00) { + $second_ms = $minute[1] * 100; + } + + return $ms + $second_ms + $minute_ms; + } +} \ No newline at end of file diff --git a/application/index/service/ScheduleService.php b/application/index/service/ScheduleService.php new file mode 100644 index 0000000..88bd278 --- /dev/null +++ b/application/index/service/ScheduleService.php @@ -0,0 +1,85 @@ +matchScheduleModel = new MatchSchedule(); + } + + public function getOne($scheduleId) + { + return $this->matchScheduleModel->get($scheduleId); + } + + public function getByWhere($param) + { + return $this->matchScheduleModel->all($param); + } + + public function addSchedule($add_data) + { + $data['match_id'] = $add_data['match_id']; + $data['title'] = $add_data['title']; + $data['content'] = $add_data['content']; + $data['schedule_date'] = $add_data['date']; + + Db::startTrans(); + + try { + if ($this->matchScheduleModel->insertGetId($data)) { + Db::commit(); + } + return true; + } catch (Exception $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } + + public function editSchedule($edit_data) + { + $data['match_id'] = $edit_data['match_id']; + $data['title'] = $edit_data['title']; + $data['content'] = $edit_data['content']; + $data['schedule_date'] = $edit_data['date']; + $id = $edit_data['date']; + + Db::startTrans(); + + try { + if ($this->matchScheduleModel->update($data, ['id' => $id])) { + Db::commit(); + } + return true; + } catch (Exception $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } + + public function delSchedule($scheduleId) + { + Db::startTrans(); + try { + if ($this->matchScheduleModel->where('id', $scheduleId)->delete()) { + Db::commit(); + } + return true; + } catch (Exception $exception) { + Db::rollback(); + $this->error($exception->getMessage()); + } + } +} \ No newline at end of file diff --git a/application/index/service/ScreenService.php b/application/index/service/ScreenService.php new file mode 100644 index 0000000..455d79f --- /dev/null +++ b/application/index/service/ScreenService.php @@ -0,0 +1,210 @@ +playerModel = new Players(); + $this->rankingModel = new MatchRanking(); + $this->userModel = new User(); + $this->rankingService = new RankingService(); + $this->matchContestantModel = new MatchContestant(); + $this->matchScreenModel = new MatchScreen(); + + + + } + + public function screenList($match_id) + { + $archives = ArchivesModel::get($match_id, ['channel']); + if (empty($archives)) { + return false; + } + // 获取进32强数据 + $collection['thirty_two'] = $this->rankingService->getRank(32, $match_id); + $collection['sixteen'] = $this->rankingService->getRank(16, $match_id); + $collection['eight'] = $this->rankingService->getRank(8, $match_id); + $collection['four'] = $this->rankingService->getRank(4, $match_id); + $collection['finals'] = $this->rankingService->getRank(1, $match_id); + + // 获取参赛人员信息 + $this->matchList = $this->matchContestantModel->alias("a")->with('players')->where([ + 'a.match_id' => $match_id, + 'a.status' => 2 + ])->field("player_id")->select(); + + if (empty($this->matchList)) { + return []; + } + + foreach ($collection as $vals) { + // var_dump($vals);exit; + foreach ($vals as $val) { + // var_dump($val['player_name']);exit; + //名字加*号 + $str = $val['player_name']; + $strlen = mb_strlen($str,'UTF8'); + if($strlen == 2){ + $real_name=mb_substr($str,0,1,'UTF8').'*'; + } + if($strlen > 2){ + $tmp_str=mb_substr($str,0,$strlen-2,'UTF8').'*'; + $real_name=$tmp_str.mb_substr($str,-1,1,'UTF8'); + } + $val['player_name']= $real_name; + + $user_info = $this->userModel->where(['member_number' => $val->player_id])->find(); + if (!empty($user_info)) { + $player_info = $this->playerModel->where(['member_id' => $user_info->id])->find(); + if (!empty($player_info)) { + $val->photo = $player_info->player_pic ? $player_info->player_pic : ""; +// $val->photo = base64_encode(file_get_contents($val->photo)); + $val->province = $player_info->province; + } else { + $val->photo = " "; + $val->province = " "; + } + + } + } + } + $i = 0; + // 获取所有参赛飞手 + foreach ($this->matchList as $key => $val) { + $user_info = $this->userModel->where(['id' => $val->players->member_id])->find(); + $collection['allPlayer'][$i]['player_id'] = 'Null'; + if (!empty($user_info)) { + $collection['allPlayer'][$i]['player_id'] = $user_info->member_number; + } + //名字加*号 + $str = $val->players->real_name; + $strlen = mb_strlen($str,'UTF8'); + if($strlen == 2){ + $real_name=mb_substr($str,0,1,'UTF8').'*'; + } + if($strlen > 2){ + $tmp_str=mb_substr($str,0,$strlen-2,'UTF8').'*'; + $real_name=$tmp_str.mb_substr($str,-1,1,'UTF8'); + } + + $collection['allPlayer'][$i]['real_name'] = $real_name; + $collection['allPlayer'][$i]['age'] = $val->players->age; + $collection['allPlayer'][$i]['gender'] = $val->players->gender; + $collection['allPlayer'][$i]['province'] = $val->players->province; + $collection['allPlayer'][$i]['player_pic'] = $val->players->player_pic; + $collection['allPlayer'][$i]['member_id'] = $val->players->member_id; + + ++$i; + } + + $collection['count'] = $this->sumPlayer($match_id); + $collection['title'] = $archives->title; + + return $collection; + } + + public function sumPlayer($match_id) + { + // 获取参赛人员信息 + $this->matchList = $this->matchContestantModel->alias("a")->with('players')->where([ + 'a.match_id' => $match_id, + ])->where('a.status', 'in', '1,2')->field("player_id")->select(); + + //总数 + $countPalyer['count'] = count($this->matchList); + // 男女总数 + $countPalyer['gender_man'] = 0; + $countPalyer['gender_woman'] = 0; + // 大于等于五岁但是小于十岁的年龄 + $countPalyer['one_level_age'] = 0; + // 大于等于十岁但是小于十五岁的年龄 + $countPalyer['two_level_age'] = 0; + // 大于等于十五岁但是小于三十岁的年龄 + $countPalyer['three_level_age'] = 0; + // 大于等于三十岁的年龄总和 + $countPalyer['four_level_age'] = 0; + // 统计各个省份人数 + $countPalyer['province'] = []; + foreach ($this->matchList as $vals) { + if (!empty($vals->players)) { + if ($vals->players->gender == "男") { + ++$countPalyer['gender_man']; + } else { + ++$countPalyer['gender_woman']; + } + + if ($vals->players->age >= 5 && $vals->players->age <= 9) { + ++$countPalyer['one_level_age']; + } else if ($vals->players->age >= 10 && $vals->players->age <= 14) { + ++$countPalyer['two_level_age']; + } else if ($vals->players->age >= 15 && $vals->players->age <= 29) { + ++$countPalyer['three_level_age']; + } else if ($vals->players->age >= 30) { + ++$countPalyer['four_level_age']; + } + + if (empty($countPalyer['province'][$vals->players->province]) + && !empty($vals->players->province)) { + $countPalyer['province'][$vals->players->province] = 1; + } else { + ++$countPalyer['province'][$vals->players->province]; + } + } + } + + return $countPalyer; + } + + public function verifyScreen($param) + { + // 查询是否为空密码 + $screen_info = $this->matchScreenModel->where([ + 'match_id' => $param['match_id'], + 'type' => $param['type'] + ])->find(); + + + if (empty($screen_info->look_pwd)) { + return true; + } else { + if (empty($param['look_pwd'])) { + return false; + } + if (md5($param['look_pwd']) == $screen_info->look_pwd) { + return true; + } + } + + return false; + + } + + public function getScreenInfo($param) + { + $res = $this->matchScreenModel + ->where("match_id", $param['match_id']) + ->where("type", $param['type']) + ->field("title, backound_img, font_rgb, match_id") + ->find(); + + return $res; + } +} \ No newline at end of file diff --git a/application/index/service/Service.php b/application/index/service/Service.php new file mode 100644 index 0000000..0c8093c --- /dev/null +++ b/application/index/service/Service.php @@ -0,0 +1,17 @@ + + + + + + +
                                                                                                                  + {switch name="$clubinfo.status" } + {case value="1"} +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 俱乐部 + 身份信息正在认证中... +

                                                                                                                  +

                                                                                                                  + Club + Under certification... +

                                                                                                                  + 加急审核请致电:13136225305 +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + + {case value="3"} + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 俱乐部 + 身份信息正在认证中... +

                                                                                                                  +

                                                                                                                  + Club + Under certification... +

                                                                                                                  + 加急审核请致电:13136225305 +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + + {case value="2"} + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 俱乐部 + 身份信息认证失败! +

                                                                                                                  +

                                                                                                                  + Club + Authentication Failure! +

                                                                                                                  +
                                                                                                                  审核内容:{$clubinfo.reject_reason|default="无"}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + {case value="4"} + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 俱乐部 + 身份信息正在认证中... +

                                                                                                                  +

                                                                                                                  + Club + Under certification... +

                                                                                                                  + 加急审核请致电:13136225305 +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + + {case value="9"} + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  + 俱乐部 + Club +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + 信息审核通过,你已获得俱乐部认证 +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + {/switch} + {if $clubinfo.status==9} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队伍名称

                                                                                                                  +

                                                                                                                  Club Name

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.name}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  所属地区

                                                                                                                  +

                                                                                                                  Address

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.province}{$clubinfo.city}{$clubinfo.district}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛主体

                                                                                                                  +

                                                                                                                  Competitors

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.competitors}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队徽

                                                                                                                  +

                                                                                                                  Club Logo

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队旗

                                                                                                                  +

                                                                                                                  Club Flag

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  ASFC会员

                                                                                                                  +

                                                                                                                  ASFC User

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.asfc_num}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  领队

                                                                                                                  +

                                                                                                                  Club Leader

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.leader}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.leader_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  副领队1

                                                                                                                  +

                                                                                                                  Deputy Leader

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.deputy_leader1}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.deputy_leader1_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  副领队2

                                                                                                                  +

                                                                                                                  Deputy Leader

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.deputy_leader2}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.deputy_leader2_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  主教练

                                                                                                                  +

                                                                                                                  Deputy Leader

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.head_coach}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.head_coach_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  教练员1

                                                                                                                  +

                                                                                                                  Deputy Leader

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach1}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach1_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.coach2} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  教练员2

                                                                                                                  +

                                                                                                                  Deputy Leader

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach2}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach2_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {/if} + {if $clubinfo.coach3} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  教练员2

                                                                                                                  +

                                                                                                                  Deputy Leader

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach3}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach3_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {/if} + {if $clubinfo.coach4} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  教练员2

                                                                                                                  +

                                                                                                                  Deputy Leader

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach4}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach4_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {/if} +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  提交时间 {$clubinfo.updatetime}

                                                                                                                  + +

                                                                                                                  重新提交

                                                                                                                  +

                                                                                                                  Resubmit

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {/if} + +
                                                                                                                  diff --git a/application/index/view/club - 副本/myplayer.html b/application/index/view/club - 副本/myplayer.html new file mode 100644 index 0000000..24409d7 --- /dev/null +++ b/application/index/view/club - 副本/myplayer.html @@ -0,0 +1,332 @@ + + + + + + + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  俱乐部飞手 | Club Members

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  飞手姓名

                                                                                                                  +

                                                                                                                  Pilot Name

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  申请时间

                                                                                                                  +

                                                                                                                  Application Time

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  申请事项

                                                                                                                  +

                                                                                                                  Application Item

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  操作

                                                                                                                  +

                                                                                                                  Operation

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + + \ No newline at end of file diff --git a/application/index/view/club - 副本/registerclub.html b/application/index/view/club - 副本/registerclub.html new file mode 100644 index 0000000..02cb216 --- /dev/null +++ b/application/index/view/club - 副本/registerclub.html @@ -0,0 +1,1007 @@ + + + + + + + + + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + + + + + + + 俱乐部 + Club +

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 俱乐部基本信息 + Basic Information +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  类型

                                                                                                                  +

                                                                                                                  Type

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队伍名称

                                                                                                                  +

                                                                                                                  Club Name

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  队伍名称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  (例)北京市模型运动协会飞鹰无人机竞速队

                                                                                                                  +

                                                                                                                  (简称:北京飞鹰/地名+队名)。

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  参赛主体

                                                                                                                  +

                                                                                                                  Competitors

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  参赛主体

                                                                                                                  +
                                                                                                                  +

                                                                                                                  中国无人机竞速联赛参赛主体实行注册制,中国境内任何符合条件的参赛队伍均可注册报名参赛。

                                                                                                                  +

                                                                                                                  1、参赛主体须是在中国境内设立的具有独立法人资质的社会团体、民非社团组织或市场主体(有限公司),主体组织全称含有体育、教育、运动、科技、无人机、航模、航天、竞速字样均可。

                                                                                                                  +

                                                                                                                  2、参赛主体法定代表人须是中华人民共和国公民。

                                                                                                                  +

                                                                                                                  3、参赛主体须获得中国航空运动协会团体会员资格。

                                                                                                                  +

                                                                                                                  4、向中国航空运动协会提交参赛队伍注册材料,获得审批后即可成为中国无人机竞速联赛的参赛队伍。

                                                                                                                  +

                                                                                                                  5、同一参赛主体只能申请一支参赛队伍参赛。

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  所属地区

                                                                                                                  +

                                                                                                                  District

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  地址

                                                                                                                  +

                                                                                                                  Address

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  营业执照上传

                                                                                                                  +

                                                                                                                  Business License

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.business_license} + + {else/} + +

                                                                                                                  上传照片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  社会团体、民非社团组织等登记证书均可,文件大小不超过5MB

                                                                                                                  +

                                                                                                                  Registration certificates of social organizations, civil non-association organizations, etc. are acceptable, and the file size does not exceed 5MB.

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人身份证

                                                                                                                  +

                                                                                                                  Corporate Identity Card

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.card_front_view} + + {else /} +

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  + {if $clubinfo.card_back_view} + + {else /} +

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队徽上传

                                                                                                                  +

                                                                                                                  Club Logo

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.logo} + + {else/} + +

                                                                                                                  上传照片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传jpg/png文件,文件大小不超过5MB,尽量为正方形。

                                                                                                                  +

                                                                                                                  Please upload jpg/png file, the file size should not exceed 5MB, as square as possible.

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队旗上传

                                                                                                                  +

                                                                                                                  Club Flag

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.flag} + + {else/} + +

                                                                                                                  上传照片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传jpg/png文件,文件大小不超过5MB。

                                                                                                                  +

                                                                                                                  Please upload jpg/png file, the file size should not exceed 5MB.

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +

                                                                                                                  Competition Slogan

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  ASFC团体会员

                                                                                                                  +

                                                                                                                  ASFC User

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 领队人员信息 + Leader Information +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  领队

                                                                                                                  +

                                                                                                                  Leader

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队1

                                                                                                                  +

                                                                                                                  Deputy Leader 1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队2

                                                                                                                  +

                                                                                                                  Deputy Leader 2

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 教练人员信息 + Coach Information +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主教练

                                                                                                                  +

                                                                                                                  Head Coach

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员1

                                                                                                                  +

                                                                                                                  Coach 1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  新增教练员

                                                                                                                  +

                                                                                                                  More Coach

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 俱乐部基本信息 + Basic Information +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  类型

                                                                                                                  +

                                                                                                                  Type

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队伍名称

                                                                                                                  +

                                                                                                                  Club Name

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  队伍名称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  (例)北京市模型运动协会飞鹰无人机竞速队

                                                                                                                  +

                                                                                                                  (简称:北京飞鹰/地名+队名)。

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  参赛主体

                                                                                                                  +

                                                                                                                  Competitors

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  参赛主体

                                                                                                                  +
                                                                                                                  +

                                                                                                                  中国无人机竞速联赛参赛主体实行注册制,中国境内任何符合条件的参赛队伍均可注册报名参赛。

                                                                                                                  +

                                                                                                                  1、参赛主体须是在中国境内设立的具有独立法人资质的社会团体、民非社团组织或市场主体(有限公司),主体组织全称含有体育、教育、运动、科技、无人机、航模、航天、竞速字样均可。

                                                                                                                  +

                                                                                                                  2、参赛主体法定代表人须是中华人民共和国公民。

                                                                                                                  +

                                                                                                                  3、参赛主体须获得中国航空运动协会团体会员资格。

                                                                                                                  +

                                                                                                                  4、向中国航空运动协会提交参赛队伍注册材料,获得审批后即可成为中国无人机竞速联赛的参赛队伍。

                                                                                                                  +

                                                                                                                  5、同一参赛主体只能申请一支参赛队伍参赛。

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  所属地区

                                                                                                                  +

                                                                                                                  District

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  地址

                                                                                                                  +

                                                                                                                  Address

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  营业执照上传

                                                                                                                  +

                                                                                                                  Business License

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传照片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  社会团体、民非社团组织等登记证书均可,文件大小不超过5MB

                                                                                                                  +

                                                                                                                  Registration certificates of social organizations, civil non-association organizations, etc. are acceptable, and the file size does not exceed 5MB.

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人身份证

                                                                                                                  +

                                                                                                                  Corporate Identity Card

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队徽上传

                                                                                                                  +

                                                                                                                  Club Logo

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传照片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传jpg/png文件,文件大小不超过5MB,尽量为正方形。

                                                                                                                  +

                                                                                                                  Please upload jpg/png file, the file size should not exceed 5MB, as square as possible.

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队旗上传

                                                                                                                  +

                                                                                                                  Club Flag

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传照片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传jpg/png文件,文件大小不超过5MB。

                                                                                                                  +

                                                                                                                  Please upload jpg/png file, the file size should not exceed 5MB.

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +

                                                                                                                  Competition Slogan

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  ASFC团体会员

                                                                                                                  +

                                                                                                                  ASFC User

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 领队人员信息 + Leader Information +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  领队

                                                                                                                  +

                                                                                                                  Leader

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队1

                                                                                                                  +

                                                                                                                  Deputy Leader 1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队2

                                                                                                                  +

                                                                                                                  Deputy Leader 2

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 教练人员信息 + Coach Information +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主教练

                                                                                                                  +

                                                                                                                  Head Coach

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员1

                                                                                                                  +

                                                                                                                  Coach 1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +

                                                                                                                  Contact

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  新增教练员

                                                                                                                  +

                                                                                                                  More Coach

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + + +
                                                                                                                  + + + + + + + \ No newline at end of file diff --git a/application/index/view/club/index - 副本.html b/application/index/view/club/index - 副本.html new file mode 100644 index 0000000..29825c0 --- /dev/null +++ b/application/index/view/club/index - 副本.html @@ -0,0 +1,233 @@ + + + + + + +
                                                                                                                  + {switch name="$clubinfo.status" } + {case value="1"} +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 队伍 + 身份信息正在认证中... +

                                                                                                                  +

                                                                                                                  + Club + Under certification... +

                                                                                                                  + 加急审核请致电:13136225305 +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + + {case value="3"} + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 队伍 + 身份信息正在认证中... +

                                                                                                                  +

                                                                                                                  + Club + Under certification... +

                                                                                                                  + 加急审核请致电:13136225305 +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + + {case value="2"} + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 队伍 + 身份信息认证失败! +

                                                                                                                  +

                                                                                                                  + Club + Authentication Failure! +

                                                                                                                  +
                                                                                                                  审核内容:{$clubinfo.reject_reason|default="无"}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + {case value="4"} + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 队伍 + 身份信息正在认证中... +

                                                                                                                  +

                                                                                                                  + Club + Under certification... +

                                                                                                                  + 加急审核请致电:13136225305 +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + + {case value="9"} + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  + 队伍 + Club +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + 信息审核通过,你已获得队伍认证 +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + {/switch} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  主体名称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.competitors}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  营业执照

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队伍名称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.name}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  所在地区

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.province}{$clubinfo.city=='选择'?'':$clubinfo.city}{$clubinfo.district=='选择'?'':$clubinfo.district}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队徽

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队旗

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  口号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.competition_slogan}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  法人

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.legal_name}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.legal_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  领队

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.leader}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.leader_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  中国航空团体会员

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {if $clubinfo.is_asfc}是{else/}否{/if}
                                                                                                                  +
                                                                                                                  + {if $clubinfo.is_asfc} +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  团体会员证书

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {/if} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  主体单位简介

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.job_situ}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  提交时间 {$clubinfo.updatetime}

                                                                                                                  + +

                                                                                                                  重新提交

                                                                                                                  +

                                                                                                                  Resubmit

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  diff --git a/application/index/view/club/index.html b/application/index/view/club/index.html new file mode 100644 index 0000000..a139cb0 --- /dev/null +++ b/application/index/view/club/index.html @@ -0,0 +1,410 @@ + + + + + + + +
                                                                                                                  + {switch name="$clubinfo.status" } + {case value="1"} +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 队伍 + 身份信息正在认证中... +

                                                                                                                  +

                                                                                                                  + Club + Under certification... +

                                                                                                                  + 加急审核请致电:13136225305 +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + + {case value="3"} + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 队伍 + 身份信息正在认证中... +

                                                                                                                  +

                                                                                                                  + Club + Under certification... +

                                                                                                                  + 加急审核请致电:13136225305 +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + + {case value="2"} + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 队伍 + 身份信息认证失败! +

                                                                                                                  +

                                                                                                                  + Club + Authentication Failure! +

                                                                                                                  +
                                                                                                                  审核内容:{$clubinfo.reject_reason|default="无"}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + {case value="4"} + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + 队伍 + 身份信息正在认证中... +

                                                                                                                  +

                                                                                                                  + Club + Under certification... +

                                                                                                                  + 加急审核请致电:13136225305 +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + + {case value="9"} + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  + 队伍 + Club +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + 信息审核通过,你已获得队伍认证 +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + {/case} + {/switch} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛主体基本信息

                                                                                                                  +

                                                                                                                  参赛队伍基本信息

                                                                                                                  +

                                                                                                                  队伍其他信息

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  主体单位类型

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.competitors_type}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  主体单位全称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.competitors}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  营业执照或登记证书编号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.business_num}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  主体营业执照或登记证书原件照片

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  所在地区

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.province}{$clubinfo.city=='选择'?'':$clubinfo.city}{$clubinfo.district=='选择'?'':$clubinfo.district}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  法人姓名

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.legal_name}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.legal_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  中国航空团体会员

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.is_asfc?'是':'否'}
                                                                                                                  +
                                                                                                                  + {if $clubinfo.is_asfc} +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  团体会员证书

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {/if} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  主体单位简介

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.job_situ}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队伍全称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.name}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队伍简称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.name_short}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队徽

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队旗

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.competition_slogan}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  领队

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.leader}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.leader_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  副领队1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.deputy_leader1}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.deputy_leader1_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  副领队2

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.deputy_leader2}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.deputy_leader2_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  主教练

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.head_coach}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.head_coach_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  教练员1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach1}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach1_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.coach2} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  教练员2

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach2}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach2_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {/if} + {if $clubinfo.coach3} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  教练员3

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach3}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach3_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {/if} + {if $clubinfo.coach4} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  教练员4

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach4}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  联系方式

                                                                                                                  +
                                                                                                                  +
                                                                                                                  {$clubinfo.coach4_tel}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {/if} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛承诺书

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  提交时间 {$clubinfo.updatetime}

                                                                                                                  + +

                                                                                                                  重新提交

                                                                                                                  +

                                                                                                                  Resubmit

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + \ No newline at end of file diff --git a/application/index/view/club/myplayer - 副本.html b/application/index/view/club/myplayer - 副本.html new file mode 100644 index 0000000..eae98e9 --- /dev/null +++ b/application/index/view/club/myplayer - 副本.html @@ -0,0 +1,332 @@ + + + + + + + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  俱乐部飞手 | Club Members

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  飞手姓名

                                                                                                                  +

                                                                                                                  Pilot Name

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  申请时间

                                                                                                                  +

                                                                                                                  Application Time

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  申请事项

                                                                                                                  +

                                                                                                                  Application Item

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  状态

                                                                                                                  +

                                                                                                                  Status

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + + \ No newline at end of file diff --git a/application/index/view/club/myplayer.html b/application/index/view/club/myplayer.html new file mode 100644 index 0000000..6bb1eae --- /dev/null +++ b/application/index/view/club/myplayer.html @@ -0,0 +1,425 @@ + + + + + + + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  俱乐部飞手 | Club Members

                                                                                                                  + + + +

                                                                                                                  审核问题请致电 13136225305

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  飞手姓名

                                                                                                                  +

                                                                                                                  Pilot Name

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  申请时间

                                                                                                                  +

                                                                                                                  Application Time

                                                                                                                  +
                                                                                                                  + + + + +
                                                                                                                  +

                                                                                                                  状态

                                                                                                                  +

                                                                                                                  Status

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + + \ No newline at end of file diff --git a/application/index/view/club/registerclub - 备份2024-03-15.html b/application/index/view/club/registerclub - 备份2024-03-15.html new file mode 100644 index 0000000..c8319fa --- /dev/null +++ b/application/index/view/club/registerclub - 备份2024-03-15.html @@ -0,0 +1,1500 @@ + + + + + + + + + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + + + + + + + 队伍 + Club +

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + 参赛主体基本信息 +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛主体实行注册制,中国境内任何符合条件的组织机构均可注册报名。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位类型

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位全称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  主体单位全称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛主体须是在中国境内设立的具有独立法人资质的社会团体、民非社团组织或市场主体(有限公司),主体组织全称含有体育、教育、运动、科技、无人机、航模、航天、竞速字样均可。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  营业执照或登记证书编号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体营业执照副本或登记证书原件照片上传

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.business_license} + + {else/} + + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传jpg/png文件,文件大小不超过5MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  所在地区

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  通讯地址

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人姓名

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人身份证号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人手机号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  上传法人身份证

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.card_front_view} + + {else/} +

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  + {if $clubinfo.card_back_view} + + {else/} +

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  是否获得中国航空运动协会团体会员资格

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位简介

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 参赛队伍基本信息 +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队伍全称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  队伍全称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  主体单位全称 + 无人机竞速队

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队伍简称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  队伍简称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  命名规则
                                                                                                                  1.队伍简称须为地名+队名的命名结构,从参赛队伍全称中任意选择 4-6个汉字进行组合,如全称中无地名信息,则以注册地址地名为准。
                                                                                                                  2.例:队伍全称上海青蜓体育俱乐部有限公司无人机竞速队,简称可以是上海青蜓、上海体育、上海竞速等组合,最终结果由平台审核为准。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队徽(需原创)

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.logo} + + {else/} + +

                                                                                                                  上传图片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传(400*400)尺寸的图片,大小不超过5MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队旗(需原创)

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.flag} + + {else/} + +

                                                                                                                  上传图片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  文件大小不超过5MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +
                                                                                                                  +

                                                                                                                  体现体育道德风尚或运动特性并具有正能量的竞赛口号

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  领队

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队2

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主教练

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.coach2} +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员2

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {/if} + {if $clubinfo.coach3} +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员3

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {/if} + {if $clubinfo.coach4} +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员4

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  新增教练员

                                                                                                                  +

                                                                                                                  More Coach

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 队伍其他信息 +

                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  参赛承诺书扫描件上传

                                                                                                                  + 模板下载 +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.promise_stuff} + + {else/} + +

                                                                                                                  上传文件

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  文件大小不超过5MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + 参赛主体基本信息 +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛主体实行注册制,中国境内任何符合条件的组织机构均可注册报名。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  认证类型

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位类型

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位全称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  主体单位全称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛主体须是在中国境内设立的具有独立法人资质的社会团体、民非社团组织或市场主体(有限公司),主体组织全称含有体育、教育、运动、科技、无人机、航模、航天、竞速字样均可。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  营业执照或登记证书编号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体营业执照副本或登记证书原件照片上传

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传jpg/png文件,文件大小不超过5MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  所在地区

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  通讯地址

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人姓名

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  法定代表人须是中华人民共和国公民

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人身份证号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人手机号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  上传法人身份证

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  是否获得中国航空运动协会团体会员资格

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  团体会员证书编号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  团体会员证书上传

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传文件

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  文件大小不超过5MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位简介

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 参赛队伍基本信息 +

                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  每个参赛主体只能申请一支参赛队伍注册

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队伍全称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  队伍全称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  主体单位全称 + 无人机竞速队

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队伍简称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  队伍简称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  命名规则
                                                                                                                  1.队伍简称须为地名+队名的命名结构,从参赛队伍全称中任意选择 4-6个汉字进行组合,如全称中无地名信息,则以注册地址地名为准。
                                                                                                                  2.例:队伍全称上海青蜓体育俱乐部有限公司无人机竞速队,简称可以是上海青蜓、上海体育、上海竞速等组合,最终结果由平台审核为准。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队徽(需原创)

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传图片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传(400*400)尺寸的图片,大小不超过5MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队旗(需原创)

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传图片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  文件大小不超过5MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +
                                                                                                                  +

                                                                                                                  体现体育道德风尚或运动特性并具有正能量的竞赛口号

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  领队

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队2

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主教练

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  新增教练员

                                                                                                                  +

                                                                                                                  More Coach

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 其他信息 +

                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  参赛承诺书扫描件上传

                                                                                                                  + 模板下载 +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传文件

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  文件大小不超过5MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + + +
                                                                                                                  + + + + + + + + \ No newline at end of file diff --git a/application/index/view/club/registerclub.html b/application/index/view/club/registerclub.html new file mode 100644 index 0000000..1112b72 --- /dev/null +++ b/application/index/view/club/registerclub.html @@ -0,0 +1,1760 @@ + + + + + + + + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + + + + + + + 队伍 + Club +

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + 参赛主体基本信息 +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛主体实行注册制,中国境内任何符合条件的组织机构均可注册报名。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位类型

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位全称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  主体单位全称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛主体须是在中国境内设立的具有独立法人资质的社会团体、民非社团组织或市场主体(有限公司),主体组织全称含有体育、教育、运动、科技、无人机、航模、航天、竞速字样均可。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  统一社会信用代码或登记证书编号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体营业执照副本或登记证书原件照片上传

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + + + + + + + + + + + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  所在地区

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  通讯地址

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人姓名

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人手机号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人身份信息

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  上传法人身份证

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.card_front_view} + + {else/} +

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  + {if $clubinfo.card_back_view} + + {else/} +

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  是否获得中国航空运动协会团体会员资格

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  团体会员证书上传

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.asfc_cert} + + {else/} + +

                                                                                                                  上传文件

                                                                                                                  + {/if} +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位简介

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 参赛队伍基本信息 +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队伍全称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  队伍全称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  主体单位全称 + 无人机竞速队

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队伍简称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  队伍简称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  命名规则
                                                                                                                  1.队伍简称须为地名+队名的命名结构,从参赛队伍全称中任意选择 4-6个汉字进行组合,如全称中无地名信息,则以注册地址地名为准。
                                                                                                                  2.例:队伍全称上海青蜓体育俱乐部有限公司无人机竞速队,简称可以是上海青蜓、上海体育、上海竞速等组合,最终结果由平台审核为准。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队徽(需原创)

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.logo} + + {else/} + +

                                                                                                                  上传图片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传(400*400)尺寸的图片,大小不超过2MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队旗(需原创)

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.flag} + + {else/} + +

                                                                                                                  上传图片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  文件大小不超过2MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +
                                                                                                                  +

                                                                                                                  体现体育道德风尚或运动特性并具有正能量的竞赛口号

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  统一制服上传

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.club_clothes1} + + {else/} + +

                                                                                                                  上传图片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  + {if $clubinfo.club_clothes2} + + {else/} + +

                                                                                                                  上传图片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  + {if $clubinfo.club_clothes3} + + {else/} + +

                                                                                                                  上传图片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  + {if $clubinfo.club_clothes4} + + {else/} + +

                                                                                                                  上传图片

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  请上传jpg/png文件,文件大小不超过2MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  媒体宣传平台

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  媒体名称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  媒体账号名称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  领队

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队2

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主教练

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.coach2} +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员2

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {/if} + {if $clubinfo.coach3} +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员3

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {/if} + {if $clubinfo.coach4} +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员4

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  新增教练员

                                                                                                                  +

                                                                                                                  More Coach

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 队伍其他信息 +

                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  参赛承诺书扫描件上传

                                                                                                                  + 模板下载 +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $clubinfo.promise_stuff} + + {else/} + +

                                                                                                                  上传文件

                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  文件大小不超过2MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + 参赛主体基本信息 +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛主体实行注册制,中国境内任何符合条件的组织机构均可注册报名。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  认证类型

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位类型

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位全称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  主体单位全称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  参赛主体须是在中国境内设立的具有独立法人资质的社会团体、民非社团组织或市场主体(有限公司),主体组织全称含有体育、教育、运动、科技、无人机、航模、航天、竞速字样均可。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  统一社会信用代码或登记证书编号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体营业执照副本或登记证书原件照片上传

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传jpg/png文件,文件大小不超过2MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  所在地区

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  通讯地址

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人姓名

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  法定代表人须是中华人民共和国公民

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人手机号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  法人身份信息

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  上传法人身份证

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  是否获得中国航空运动协会团体会员资格

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + + + + + + + + + + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  团体会员证书上传

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传文件

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  文件大小不超过2MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主体单位简介

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 参赛队伍基本信息 +

                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  每个参赛主体只能申请一支参赛队伍注册

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队伍全称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  队伍全称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  主体单位全称 + 无人机竞速队

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  队伍简称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  队伍简称

                                                                                                                  +
                                                                                                                  +

                                                                                                                  命名规则
                                                                                                                  1.队伍简称须为地名+队名的命名结构,从参赛队伍全称中任意选择 4-6个汉字进行组合,如全称中无地名信息,则以注册地址地名为准。
                                                                                                                  2.例:队伍全称上海青蜓体育俱乐部有限公司无人机竞速队,简称可以是上海青蜓、上海体育、上海竞速等组合,最终结果由平台审核为准。

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队徽(需原创)

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传图片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  请上传(400*400)尺寸的图片,大小不超过2MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  队旗(需原创)

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传图片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  文件大小不超过2MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  竞赛口号

                                                                                                                  +
                                                                                                                  +

                                                                                                                  体现体育道德风尚或运动特性并具有正能量的竞赛口号

                                                                                                                  +

                                                                                                                  +

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  统一制服上传

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传图片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +

                                                                                                                  上传图片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +

                                                                                                                  上传图片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +

                                                                                                                  上传图片

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  请上传jpg/png文件,文件大小不超过2MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  媒体宣传平台

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  媒体名称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  媒体账号名称

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  领队

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  副领队2

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  主教练

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  教练员1

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  联系电话

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  新增教练员

                                                                                                                  +

                                                                                                                  More Coach

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +

                                                                                                                  + 其他信息 +

                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + * +
                                                                                                                  +

                                                                                                                  参赛承诺书扫描件上传

                                                                                                                  + 模板下载 +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +

                                                                                                                  上传文件

                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  文件大小不超过2MB

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + + +
                                                                                                                  + + + + + + + + \ No newline at end of file diff --git a/application/index/view/club/sociaty - 副本.html b/application/index/view/club/sociaty - 副本.html new file mode 100644 index 0000000..c50e5a3 --- /dev/null +++ b/application/index/view/club/sociaty - 副本.html @@ -0,0 +1,167 @@ + + + + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + + + + + + + + + +
                                                                                                                  +

                                                                                                                  飞手姓名

                                                                                                                  +

                                                                                                                  Pilot Name

                                                                                                                  +
                                                                                                                  +

                                                                                                                  性别

                                                                                                                  +

                                                                                                                  Gender

                                                                                                                  +
                                                                                                                  +

                                                                                                                  出生日期

                                                                                                                  +

                                                                                                                  Birth

                                                                                                                  +
                                                                                                                  +

                                                                                                                  操作

                                                                                                                  +

                                                                                                                  Operation

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + + \ No newline at end of file diff --git a/application/index/view/club/sociaty.html b/application/index/view/club/sociaty.html new file mode 100644 index 0000000..67e743f --- /dev/null +++ b/application/index/view/club/sociaty.html @@ -0,0 +1,329 @@ + + + + + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + + + + + + + + + +
                                                                                                                  +

                                                                                                                  飞手姓名

                                                                                                                  +

                                                                                                                  Pilot Name

                                                                                                                  +
                                                                                                                  +

                                                                                                                  居住地

                                                                                                                  +

                                                                                                                  To Live

                                                                                                                  +
                                                                                                                  +

                                                                                                                  出生日期

                                                                                                                  +

                                                                                                                  Birth

                                                                                                                  +
                                                                                                                  +

                                                                                                                  操作

                                                                                                                  +

                                                                                                                  Operation

                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + + \ No newline at end of file diff --git a/application/index/view/cms/archives/my.html b/application/index/view/cms/archives/my.html new file mode 100644 index 0000000..c419d97 --- /dev/null +++ b/application/index/view/cms/archives/my.html @@ -0,0 +1,116 @@ + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {include file="common/sidenav" /} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + + {if in_array('postarchives', explode(',', config('cms.usersidenav')))} + 发布{:$model?$model.name:'文档'} + {/if} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + + + {volist name='archivesList' id='item'} +
                                                                                                                  + +
                                                                                                                  +

                                                                                                                  + {$item.title|htmlentities} +

                                                                                                                  +

                                                                                                                  {$item.channel.name}

                                                                                                                  +

                                                                                                                  创建时间:{$item.createtime|datetime}

                                                                                                                  +
                                                                                                                  + {if $item['status'] == 'normal'} + 修改 + {elseif $item['status'] == 'rejected'} + 被拒绝 + {elseif $item['status'] == 'pulloff'} + 已下架 + {elseif $item['status'] == 'hidden' /} + 正在审核 + {/if} + {if false} 删除{/if} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {/volist} + {if $archivesList->total()==0} + 未找到任何记录! + {/if} +
                                                                                                                  {$archivesList->render()}
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  diff --git a/application/index/view/cms/archives/post.html b/application/index/view/cms/archives/post.html new file mode 100644 index 0000000..104796d --- /dev/null +++ b/application/index/view/cms/archives/post.html @@ -0,0 +1,207 @@ + + + + + + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {include file="common/sidenav" /} +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {if $archives && $archives['status']=='normal'} +
                                                                                                                  + 温馨提示:当前{:$model?$model.name:'文章'}已经发布,如果修改将重新进入审核 +
                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  +
                                                                                                                  + {:token()} +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {if !$model || $model->iscontribute('channel_ids')} +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {/if} +
                                                                                                                  + +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + {if !$model || $model->iscontribute('image')} +
                                                                                                                  + +
                                                                                                                  +
                                                                                                                  + +
                                                                                                                  + + + +
                                                                                                                  +
                                                                                                                  +
                                                                                                                    +
                                                                                                                    +
                                                                                                                    + {/if} + {if !$model || $model->iscontribute('images')} +
                                                                                                                    + +
                                                                                                                    +
                                                                                                                    + +
                                                                                                                    + + + +
                                                                                                                    +
                                                                                                                    +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      + {/if} + {if !$model || $model->iscontribute('tags')} +
                                                                                                                      + +
                                                                                                                      + +
                                                                                                                      +
                                                                                                                      + {/if} + {if !$model || $model->iscontribute('content')} +
                                                                                                                      + +
                                                                                                                      + +
                                                                                                                      +
                                                                                                                      + {/if} + {if !$model || $model->iscontribute('price')} +
                                                                                                                      + +
                                                                                                                      + +
                                                                                                                      +
                                                                                                                      + {/if} + {if !$model || $model->iscontribute('outlink')} +
                                                                                                                      + +
                                                                                                                      + +
                                                                                                                      +
                                                                                                                      + {/if} + {if !$model || $model->iscontribute('keywords')} +
                                                                                                                      + +
                                                                                                                      + +
                                                                                                                      +
                                                                                                                      + {/if} + {if !$model || $model->iscontribute('description')} +
                                                                                                                      + +
                                                                                                                      + +
                                                                                                                      +
                                                                                                                      + {/if} +
                                                                                                                      + {$extendHtml} +
                                                                                                                      + + +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      diff --git a/application/index/view/cms/collection/index.html b/application/index/view/cms/collection/index.html new file mode 100644 index 0000000..477b43c --- /dev/null +++ b/application/index/view/cms/collection/index.html @@ -0,0 +1,79 @@ + +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      + {include file="common/sidenav" /} +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      + +
                                                                                                                      + + {volist name='collectionList' id='item'} +
                                                                                                                      + +
                                                                                                                      +

                                                                                                                      + {$item.title|htmlentities} +

                                                                                                                      +

                                                                                                                      收藏于 {$item.createtime|datetime}

                                                                                                                      +
                                                                                                                      + 移除 +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      + {/volist} + {if $collectionList->total()==0} + 未找到任何记录! + {/if} +
                                                                                                                      {$collectionList->render()}
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      diff --git a/application/index/view/cms/comment/index.html b/application/index/view/cms/comment/index.html new file mode 100644 index 0000000..023f8b2 --- /dev/null +++ b/application/index/view/cms/comment/index.html @@ -0,0 +1,77 @@ + +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      + {include file="common/sidenav" /} +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      + +
                                                                                                                      + + {volist name='commentList' id='item'} +
                                                                                                                      + +
                                                                                                                      +

                                                                                                                      + {$item.source.title|default='未知文档'|htmlentities} +

                                                                                                                      +

                                                                                                                      {$item.content|htmlentities}

                                                                                                                      +

                                                                                                                      发表于 {$item.createtime|datetime}

                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      + {/volist} + {if $commentList->total()==0} + 未找到任何记录! + {/if} +
                                                                                                                      {$commentList->render()}
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      +
                                                                                                                      diff --git a/application/index/view/cms/common/fields.html b/application/index/view/cms/common/fields.html new file mode 100644 index 0000000..895fd8c --- /dev/null +++ b/application/index/view/cms/common/fields.html @@ -0,0 +1,156 @@ +{__NOLAYOUT__} + + +{foreach $fields as $item} + +
                                                                                                                      +
                                                                                                                      {$item.title}
                                                                                                                      +
                                                                                                                      + {switch $item.type} + {case string} + + {/case} + {case password} + + {/case} + {case value="text" break="0"}{/case} + {case editor} + + {/case} + {case array} + {if $item.name=='downloadurl'} + {php}$item['value']=isset($values[$item['name']])?$item['value']:$item['download_list'];{/php} +
                                                                                                                      +
                                                                                                                      + 来源 + 地址 + 密码(可为空) +
                                                                                                                      +
                                                                                                                      {:__('Append')}
                                                                                                                      + +
                                                                                                                      + {else /} + {php}$arrList=isset($values[$item['name']])?(array)json_decode($item['value'],true):$item['content_list'];{/php} +
                                                                                                                      +
                                                                                                                      + {:isset($item["setting"]["key"])&&$item["setting"]["key"]?$item["setting"]["key"]:__('Array key')} + {:isset($item["setting"]["value"])&&$item["setting"]["value"]?$item["setting"]["value"]:__('Array value')} +
                                                                                                                      + +
                                                                                                                      {:__('Append')}
                                                                                                                      + +
                                                                                                                      + {/if} + {/case} + {case date} + + {/case} + {case time} + + {/case} + {case datetime} + + {/case} + {case datetimerange} + + {/case} + {case number} + + {/case} + {case checkbox} + {foreach name="item.content_list" item="vo"} +
                                                                                                                      + +
                                                                                                                      + {/foreach} + {/case} + {case radio} + {foreach name="item.content_list" item="vo"} +
                                                                                                                      + +
                                                                                                                      + {/foreach} + {/case} + {case value="select" break="0"}{/case} + {case value="selects"} + + {/case} + {case value="image" break="0"}{/case} + {case value="images"} +
                                                                                                                      + +
                                                                                                                      + + +
                                                                                                                      + +
                                                                                                                      +
                                                                                                                        + {/case} + {case value="file" break="0"}{/case} + {case value="files"} +
                                                                                                                        + +
                                                                                                                        + + +
                                                                                                                        + +
                                                                                                                        + {/case} + {case switch} + + + + + {/case} + {case bool} + + + {/case} + {case city} +
                                                                                                                        + +
                                                                                                                        + {/case} + {case value="selectpage" break="0"}{/case} + {case value="selectpages"} + + {/case} + {case custom} + {$item.extend_html} + {/case} + {/switch} +
                                                                                                                        +
                                                                                                                        +{/foreach} + + + diff --git a/application/index/view/cms/order/index.html b/application/index/view/cms/order/index.html new file mode 100644 index 0000000..aefe352 --- /dev/null +++ b/application/index/view/cms/order/index.html @@ -0,0 +1,89 @@ + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {include file="common/sidenav" /} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + {volist name='orderList' id='item'} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + + {if $item.archives} + {$item.archives.title|default='未知文档'|htmlentities} + {else/} + {$item.title} + {/if} + +

                                                                                                                        +

                                                                                                                        支付金额:¥{$item.payamount}元

                                                                                                                        +

                                                                                                                        支付时间:{$item.paytime|datetime}

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/volist} + {if $orderList->total()==0} + 未找到任何记录! + {/if} +
                                                                                                                        {$orderList->render()}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        diff --git a/application/index/view/common/captcha.html b/application/index/view/common/captcha.html new file mode 100644 index 0000000..617e3e0 --- /dev/null +++ b/application/index/view/common/captcha.html @@ -0,0 +1,27 @@ + +{if "[type]" == 'email'} + + + 发送验证码 + +{elseif "[type]" == 'mobile'/} + + + 发送验证码 / Send + +{elseif "[type]" == 'wechat'/} + {if get_addon_info('wechat')} + + + 获取验证码 + + {else/} + 请在后台插件管理中安装《微信管理插件》 + {/if} +{elseif "[type]" == 'text' /} + + + + +{/if} + \ No newline at end of file diff --git a/application/index/view/common/meta.html b/application/index/view/common/meta.html new file mode 100644 index 0000000..365cb20 --- /dev/null +++ b/application/index/view/common/meta.html @@ -0,0 +1,26 @@ + +{$title|default=''|htmlentities} – {$site.name|htmlentities} + + + +{if isset($keywords)} + +{/if} +{if isset($description)} + +{/if} + + + + + + + + diff --git a/application/index/view/common/script.html b/application/index/view/common/script.html new file mode 100644 index 0000000..bae2707 --- /dev/null +++ b/application/index/view/common/script.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/application/index/view/common/sidenav - ╕▒▒╛.html b/application/index/view/common/sidenav - ╕▒▒╛.html new file mode 100644 index 0000000..f1c41e1 --- /dev/null +++ b/application/index/view/common/sidenav - ╕▒▒╛.html @@ -0,0 +1,12 @@ + + diff --git a/application/index/view/common/sidenav - 副本.html b/application/index/view/common/sidenav - 副本.html new file mode 100644 index 0000000..f1c41e1 --- /dev/null +++ b/application/index/view/common/sidenav - 副本.html @@ -0,0 +1,12 @@ + + diff --git a/application/index/view/common/sidenav.html b/application/index/view/common/sidenav.html new file mode 100644 index 0000000..58a3b6c --- /dev/null +++ b/application/index/view/common/sidenav.html @@ -0,0 +1,11 @@ + + diff --git a/application/index/view/index/index.html b/application/index/view/index/index.html new file mode 100644 index 0000000..5ef2ba5 --- /dev/null +++ b/application/index/view/index/index.html @@ -0,0 +1,34 @@ + + + + + + + + + + {$site.name|htmlentities} + + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$site.name|htmlentities}

                                                                                                                        + {:__('Member center')} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + diff --git a/application/index/view/layout - 副本/default.html b/application/index/view/layout - 副本/default.html new file mode 100644 index 0000000..572a4fe --- /dev/null +++ b/application/index/view/layout - 副本/default.html @@ -0,0 +1,297 @@ + + + + + + + + {include file="common/meta" /} + + + + + + + {$title|default=''|htmlentities} – {$site.name|htmlentities} + + + + + + + + + + + + + + + + + + + + + + + + {cms:block name="header" field="content" /} + + + + + +
                                                                                                                        + + + + +
                                                                                                                        + +
                                                                                                                        + {__CONTENT__} +
                                                                                                                        + + + + + + + + + + + + + + + + +{cms:block name="footer" field="content" /} + + + + +{include file="common/script" /} + + + \ No newline at end of file diff --git a/application/index/view/layout - 副本/default2.html b/application/index/view/layout - 副本/default2.html new file mode 100644 index 0000000..93f6761 --- /dev/null +++ b/application/index/view/layout - 副本/default2.html @@ -0,0 +1,63 @@ + + + + {include file="common/meta" /} + + + + + + + +
                                                                                                                        + {__CONTENT__} +
                                                                                                                        + + + + {include file="common/script" /} + + + + diff --git a/application/index/view/layout - 副本/defaults.html b/application/index/view/layout - 副本/defaults.html new file mode 100644 index 0000000..a7a0430 --- /dev/null +++ b/application/index/view/layout - 副本/defaults.html @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + {$title|default=''|htmlentities} – {$site.name|htmlentities} + + + + + + + + + + + + + + + + + + + + + + + + {cms:block name="header" field="content" /} + + + + + +
                                                                                                                        + + + + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {__CONTENT__} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + +{cms:block name="footer" field="content" /} + + + + + + \ No newline at end of file diff --git a/application/index/view/layout - 副本/defaultsRegister.html b/application/index/view/layout - 副本/defaultsRegister.html new file mode 100644 index 0000000..6ee2c7a --- /dev/null +++ b/application/index/view/layout - 副本/defaultsRegister.html @@ -0,0 +1,303 @@ + + + + + + + + + + + + + + {$title|default=''|htmlentities} – {$site.name|htmlentities} + + + + + + + + + + + + + + + + + + + + + + + + {cms:block name="header" field="content" /} + + + + + +
                                                                                                                        + + + + +
                                                                                                                        + +
                                                                                                                        + {__CONTENT__} +
                                                                                                                        + + + + + + + + + + + + + + + + +{cms:block name="footer" field="content" /} + + + + + + \ No newline at end of file diff --git a/application/index/view/layout/default.html b/application/index/view/layout/default.html new file mode 100644 index 0000000..372a65f --- /dev/null +++ b/application/index/view/layout/default.html @@ -0,0 +1,309 @@ + + + + + + + + {include file="common/meta" /} + + + + + + + {$title|default=''|htmlentities} – {$site.name|htmlentities} + + + + + + + + + + + + + + + + + + + + + + + + {cms:block name="header" field="content" /} + + + + + +
                                                                                                                        + + + + +
                                                                                                                        + +
                                                                                                                        + {__CONTENT__} +
                                                                                                                        + + + + + + + + + + + + + + + + +{cms:block name="footer" field="content" /} + + + + +{include file="common/script" /} + + + \ No newline at end of file diff --git a/application/index/view/layout/default2.html b/application/index/view/layout/default2.html new file mode 100644 index 0000000..93f6761 --- /dev/null +++ b/application/index/view/layout/default2.html @@ -0,0 +1,63 @@ + + + + {include file="common/meta" /} + + + + + + + +
                                                                                                                        + {__CONTENT__} +
                                                                                                                        + + + + {include file="common/script" /} + + + + diff --git a/application/index/view/layout/defaults - 备份_2024-03-15.html b/application/index/view/layout/defaults - 备份_2024-03-15.html new file mode 100644 index 0000000..4748868 --- /dev/null +++ b/application/index/view/layout/defaults - 备份_2024-03-15.html @@ -0,0 +1,453 @@ + + + + + + + + + + + + + + {$title|default=''|htmlentities} – {$site.name|htmlentities} + + + + + + + + + + + + + + + + + + + + + + + + {cms:block name="header" field="content" /} + + + + + +
                                                                                                                        + + + + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {__CONTENT__} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + +{cms:block name="footer" field="content" /} + + + + + + \ No newline at end of file diff --git a/application/index/view/layout/defaults.html b/application/index/view/layout/defaults.html new file mode 100644 index 0000000..6e83986 --- /dev/null +++ b/application/index/view/layout/defaults.html @@ -0,0 +1,436 @@ + + + + + + + + + + + + + + {$title|default=''|htmlentities} – {$site.name|htmlentities} + + + + + + + + + + + + + + + + + + + + + + + + {cms:block name="header" field="content" /} + + + + + +
                                                                                                                        + + + + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {__CONTENT__} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + +{cms:block name="footer" field="content" /} + + + + + + \ No newline at end of file diff --git a/application/index/view/layout/defaultsRegister.html b/application/index/view/layout/defaultsRegister.html new file mode 100644 index 0000000..4d1fd08 --- /dev/null +++ b/application/index/view/layout/defaultsRegister.html @@ -0,0 +1,315 @@ + + + + + + + + + + + + + + {$title|default=''|htmlentities} – {$site.name|htmlentities} + + + + + + + + + + + + + + + + + + + + + + + + {cms:block name="header" field="content" /} + + + + + +
                                                                                                                        + + + + +
                                                                                                                        + +
                                                                                                                        + {__CONTENT__} +
                                                                                                                        + + + + + + + + + + + + + + + + +{cms:block name="footer" field="content" /} + + + + + + \ No newline at end of file diff --git a/application/index/view/layout/defaults_1.html b/application/index/view/layout/defaults_1.html new file mode 100644 index 0000000..84f8a1f --- /dev/null +++ b/application/index/view/layout/defaults_1.html @@ -0,0 +1,410 @@ + + + + + + + + + + + + + + {$title|default=''|htmlentities} – {$site.name|htmlentities} + + + + + + + + + + + + + + + + + + + + + + + + {cms:block name="header" field="content" /} + + + + + +
                                                                                                                        + + + + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {__CONTENT__} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + +{cms:block name="footer" field="content" /} + + + + + + \ No newline at end of file diff --git a/application/index/view/live/live.html b/application/index/view/live/live.html new file mode 100644 index 0000000..2f16d24 --- /dev/null +++ b/application/index/view/live/live.html @@ -0,0 +1,702 @@ + + + + + + {$screeninfo.title|default="比翼飞行网"} + + + + + + + + + +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        + + + + + + + + + + + + + + + + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        最终成绩以现场裁判组裁定公布为准

                                                                                                                        +

                                                                                                                        技术支持:云朵科技

                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + + + + + + + + + \ No newline at end of file diff --git a/application/index/view/live/login.html b/application/index/view/live/login.html new file mode 100644 index 0000000..ff66612 --- /dev/null +++ b/application/index/view/live/login.html @@ -0,0 +1,127 @@ + + + + + + 登录 + + + + +
                                                                                                                        + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        欢迎登录
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + \ No newline at end of file diff --git a/application/index/view/match/club_match.html b/application/index/view/match/club_match.html new file mode 100644 index 0000000..1172a52 --- /dev/null +++ b/application/index/view/match/club_match.html @@ -0,0 +1,356 @@ + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        俱乐部赛事 | Club Events

                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 赛事报名 + Apply +

                                                                                                                        + 加急审核请致电:13136225305 +
                                                                                                                        +
                                                                                                                        + {empty name="club_matchlist"} +
                                                                                                                        +
                                                                                                                        您还未参与任何赛事
                                                                                                                        + + + +
                                                                                                                        + {else /} +
                                                                                                                          +
                                                                                                                          +

                                                                                                                          赛事

                                                                                                                          +

                                                                                                                          申请时间

                                                                                                                          +

                                                                                                                          参与资格

                                                                                                                          +

                                                                                                                          操作

                                                                                                                          +
                                                                                                                          + {foreach $club_matchlist as $key=>$vo } + {if $vo.match} +
                                                                                                                        • +

                                                                                                                          {$vo.match.title ?? '已过期赛事'}

                                                                                                                          +

                                                                                                                          {$vo.created_at ?? '未知时间'}

                                                                                                                          +

                                                                                                                          + {if $vo.status == 1} 审核中 {elseif $vo.status == 2} 审核通过 {else /} 审核不通过 {/if} +

                                                                                                                          +

                                                                                                                          +

                                                                                                                          +
                                                                                                                        • + {/if} + {/foreach} +
                                                                                                                        + {$page} + {/empty} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + + + diff --git a/application/index/view/match/user_match.html b/application/index/view/match/user_match.html new file mode 100644 index 0000000..b9cd56d --- /dev/null +++ b/application/index/view/match/user_match.html @@ -0,0 +1,315 @@ + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        我的赛事 | My Events

                                                                                                                        + +
                                                                                                                        + {empty name="matchlist"} +
                                                                                                                        +
                                                                                                                        您还未参与任何赛事 / You have not entered any races
                                                                                                                        + + + +
                                                                                                                        + {else /} +
                                                                                                                          +
                                                                                                                          +

                                                                                                                          赛事

                                                                                                                          +

                                                                                                                          申请时间

                                                                                                                          +

                                                                                                                          参与资格

                                                                                                                          +

                                                                                                                          操作

                                                                                                                          +
                                                                                                                          + {foreach $matchlist as $key=>$vo } +
                                                                                                                        • +

                                                                                                                          {$vo.match.title ?? '已过期赛事'}

                                                                                                                          +

                                                                                                                          {$vo.created_at ?? '未知时间'}

                                                                                                                          +

                                                                                                                          + {if $vo.status == 1} 审核中 | In Review {elseif $vo.status == 2} 审核通过 | Pass {else /} 审核不通过 + | Fail {/if} +

                                                                                                                          +

                                                                                                                          +

                                                                                                                          +
                                                                                                                        • + {/foreach} +
                                                                                                                        + {/empty} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + +

                                                                                                                        +
                                                                                                                        +

                                                                                                                        +

                                                                                                                        +
                                                                                                                        +
                                                                                                                          +
                                                                                                                        • +

                                                                                                                          申请时间

                                                                                                                          +

                                                                                                                          +
                                                                                                                        • +
                                                                                                                        • +

                                                                                                                          支付费用

                                                                                                                          +

                                                                                                                          +
                                                                                                                        • +
                                                                                                                        • +

                                                                                                                          参与资格

                                                                                                                          + +

                                                                                                                          +
                                                                                                                        • +
                                                                                                                        • +

                                                                                                                          审核说明

                                                                                                                          +

                                                                                                                          +
                                                                                                                        • +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + \ No newline at end of file diff --git a/application/index/view/players - 副本/authenticationflayers.html b/application/index/view/players - 副本/authenticationflayers.html new file mode 100644 index 0000000..3dcb358 --- /dev/null +++ b/application/index/view/players - 副本/authenticationflayers.html @@ -0,0 +1,192 @@ + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 主页 +

                                                                                                                        +

                                                                                                                        + 身份认证 +

                                                                                                                        +

                                                                                                                        + 我的赛事 +

                                                                                                                        +

                                                                                                                        + 退出 +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {switch name="players.player_status" } + {case value="1"} +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="3"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="2"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息认证失败! +

                                                                                                                        +

                                                                                                                        + Pilot + Authentication Failure! +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {case value="-1"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息认证失败! +

                                                                                                                        +

                                                                                                                        + Pilot + Authentication Failure! +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="9"} + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + 信息审核通过,你已获得飞手认证 + 去参加赛事 + Participate In Events + 修改信息 +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {/switch} +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        提交时间 {$players.created_at}

                                                                                                                        + {if $players.player_status == 2} + 重新提交 | Resubmit + {/if} +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        {$players.real_name} + +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.gender}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        {$players.birthday}
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.country}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Honor Or Completed Races

                                                                                                                        +
                                                                                                                        {$players.experience}
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        diff --git a/application/index/view/players - 副本/edit.html b/application/index/view/players - 副本/edit.html new file mode 100644 index 0000000..d8c46cf --- /dev/null +++ b/application/index/view/players - 副本/edit.html @@ -0,0 +1,766 @@ + + + + + + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + + + + + + + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.player_pic} + + {else/} + +

                                                                                                                        上传照片

                                                                                                                        + {/if} +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        俱乐部成员

                                                                                                                        +

                                                                                                                        Club Members

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 监护人证件信息 + Guardian Id Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人姓名

                                                                                                                        +

                                                                                                                        Name Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人手机号码

                                                                                                                        +

                                                                                                                        Guardian Mobile Phone Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件类型

                                                                                                                        +

                                                                                                                        Type Of Guardian Certificate

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件号码

                                                                                                                        +

                                                                                                                        Id Number Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件照片

                                                                                                                        +

                                                                                                                        Guardian Id Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.guarder_card_front_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if $playerinfo.guarder_card_back_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + \ No newline at end of file diff --git a/application/index/view/players - 副本/index.html b/application/index/view/players - 副本/index.html new file mode 100644 index 0000000..8a24a39 --- /dev/null +++ b/application/index/view/players - 副本/index.html @@ -0,0 +1,176 @@ + + + + + +
                                                                                                                        + {switch name="$players.player_status" } + {case value="1"} +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        + 加急审核请致电:13136225305 +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="3"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        + 加急审核请致电:13136225305 +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="2"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息认证失败! +

                                                                                                                        +

                                                                                                                        + Pilot + Authentication Failure! +

                                                                                                                        +
                                                                                                                        审核内容:{$players.pass_centent.mark|default="无"}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {case value="-1"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息认证失败! +

                                                                                                                        +

                                                                                                                        + Pilot + Authentication Failure! +

                                                                                                                        +
                                                                                                                        理由:{$players.pass_centent.mark|default="无"}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="9"} + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + 信息审核通过,你已获得飞手认证 +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {/switch} +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        提交时间 {$players.created_at}

                                                                                                                        + {if $players.player_status != -1 && $players.player_status != 9} + 重新提交 | Resubmit + {/if} + {if $players.player_status == 9} + 重新提交 | Resubmit + {/if} +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        {$players.real_name} + +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.gender}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        {$players.birthday}
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.country}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Honor Or Completed Races

                                                                                                                        +
                                                                                                                        {$players.experience}
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        diff --git a/application/index/view/players - 副本/joinclub.html b/application/index/view/players - 副本/joinclub.html new file mode 100644 index 0000000..09d2a30 --- /dev/null +++ b/application/index/view/players - 副本/joinclub.html @@ -0,0 +1,132 @@ + + + + + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        俱乐部名称

                                                                                                                        +

                                                                                                                        Club Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        所属地区

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        操作

                                                                                                                        +

                                                                                                                        Operation

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + \ No newline at end of file diff --git a/application/index/view/players - 副本/myclub.html b/application/index/view/players - 副本/myclub.html new file mode 100644 index 0000000..764cbef --- /dev/null +++ b/application/index/view/players - 副本/myclub.html @@ -0,0 +1,356 @@ + + + + + + +
                                                                                                                        + {switch name="$user_player_status" } + {case value="1"} +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 加入俱乐部 + 申请正在审核中... +

                                                                                                                        +

                                                                                                                        + Join The Club + Under Review... +

                                                                                                                        + 撤销申请 +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="3"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 加入俱乐部 + 申请已被拒绝! +

                                                                                                                        +

                                                                                                                        + Join The Club + The application has been rejected! +

                                                                                                                        +
                                                                                                                        拒绝理由:{$user_player_reject_reason|default="无"}
                                                                                                                        + 加入俱乐部 +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="2"} + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        + 加入俱乐部 + Join The Club +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + 申请通过,您已是俱乐部成员 +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + {/case} + {case value="4"} +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 退出俱乐部 + 申请正在审核中... +

                                                                                                                        +

                                                                                                                        + Exit The Club + Under Review... +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {case value="5"} +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 退出俱乐部 + 申请已被拒绝! +

                                                                                                                        +

                                                                                                                        + Exit The Club + The application has been rejected! +

                                                                                                                        +
                                                                                                                        拒绝理由:{$user_player_reject_reason|default="无"}
                                                                                                                        + 撤销申请 +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {/switch} + {if $user_player_status==2||$user_player_status==4||$user_player_status==5} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        队伍名称

                                                                                                                        +

                                                                                                                        Club Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.name}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        所属地区

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.province}{$user_player_clubinfo.city}{$user_player_clubinfo.district}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛主体

                                                                                                                        +

                                                                                                                        Competitors

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.competitors}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        队徽

                                                                                                                        +

                                                                                                                        Club Logo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        队旗

                                                                                                                        +

                                                                                                                        Club Flag

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        ASFC会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.asfc_num}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        领队

                                                                                                                        +

                                                                                                                        Club Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.leader}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.leader_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        副领队1

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.deputy_leader1}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.deputy_leader1_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        副领队2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.deputy_leader2}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.deputy_leader2_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        主教练

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.head_coach}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.head_coach_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员1

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.coach1}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.coach1_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $user_player_clubinfo.coach2} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.coach2}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.coach2_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/if} + {if $user_player_clubinfo.coach3} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.coach3}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.coach3_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/if} + {if $user_player_clubinfo.coach4} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.coach4}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$user_player_clubinfo.coach4_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/if} +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        提交时间 {$user_player_clubinfo.player_applytime}

                                                                                                                        + {if $user_player_status==2} + +

                                                                                                                        退出俱乐部

                                                                                                                        +

                                                                                                                        Exit The Club

                                                                                                                        +
                                                                                                                        + {/if} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/if} +
                                                                                                                        + \ No newline at end of file diff --git a/application/index/view/players - 副本/reapply.html b/application/index/view/players - 副本/reapply.html new file mode 100644 index 0000000..7ed34b1 --- /dev/null +++ b/application/index/view/players - 副本/reapply.html @@ -0,0 +1,762 @@ + + + + + + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + + + + + + + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.player_pic} + + {else/} + +

                                                                                                                        上传照片

                                                                                                                        + {/if} +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        俱乐部成员

                                                                                                                        +

                                                                                                                        Club Members

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 监护人证件信息 + Guardian Id Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人姓名

                                                                                                                        +

                                                                                                                        Name Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人手机号码

                                                                                                                        +

                                                                                                                        Guardian Mobile Phone Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件类型

                                                                                                                        +

                                                                                                                        Type Of Guardian Certificate

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件号码

                                                                                                                        +

                                                                                                                        Id Number Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件照片

                                                                                                                        +

                                                                                                                        Guardian Id Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.guarder_card_front_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if $playerinfo.guarder_card_back_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + \ No newline at end of file diff --git a/application/index/view/players - 副本/registeredflyers.html b/application/index/view/players - 副本/registeredflyers.html new file mode 100644 index 0000000..559c5f6 --- /dev/null +++ b/application/index/view/players - 副本/registeredflyers.html @@ -0,0 +1,1185 @@ + + + + + + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + + + + + + + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.player_pic} + + {else/} + +

                                                                                                                        上传照片

                                                                                                                        + {/if} +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        俱乐部成员

                                                                                                                        +

                                                                                                                        Club Members

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 监护人证件信息 + Guardian Id Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人姓名

                                                                                                                        +

                                                                                                                        Name Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人手机号码

                                                                                                                        +

                                                                                                                        Guardian Mobile Phone Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件类型

                                                                                                                        +

                                                                                                                        Type Of Guardian Certificate

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件号码

                                                                                                                        +

                                                                                                                        Id Number Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件照片

                                                                                                                        +

                                                                                                                        Guardian Id Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.guarder_card_front_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if $playerinfo.guarder_card_back_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        上传照片

                                                                                                                        +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        俱乐部成员

                                                                                                                        +

                                                                                                                        Club Members

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + + + + + + + \ No newline at end of file diff --git a/application/index/view/players/authenticationflayers.html b/application/index/view/players/authenticationflayers.html new file mode 100644 index 0000000..3dcb358 --- /dev/null +++ b/application/index/view/players/authenticationflayers.html @@ -0,0 +1,192 @@ + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 主页 +

                                                                                                                        +

                                                                                                                        + 身份认证 +

                                                                                                                        +

                                                                                                                        + 我的赛事 +

                                                                                                                        +

                                                                                                                        + 退出 +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {switch name="players.player_status" } + {case value="1"} +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="3"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="2"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息认证失败! +

                                                                                                                        +

                                                                                                                        + Pilot + Authentication Failure! +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {case value="-1"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息认证失败! +

                                                                                                                        +

                                                                                                                        + Pilot + Authentication Failure! +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="9"} + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + 信息审核通过,你已获得飞手认证 + 去参加赛事 + Participate In Events + 修改信息 +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {/switch} +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        提交时间 {$players.created_at}

                                                                                                                        + {if $players.player_status == 2} + 重新提交 | Resubmit + {/if} +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        {$players.real_name} + +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.gender}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        {$players.birthday}
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.country}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Honor Or Completed Races

                                                                                                                        +
                                                                                                                        {$players.experience}
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        diff --git a/application/index/view/players/edit.html b/application/index/view/players/edit.html new file mode 100644 index 0000000..1f9f99b --- /dev/null +++ b/application/index/view/players/edit.html @@ -0,0 +1,872 @@ + + + + + + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + + + + + + + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        中文名

                                                                                                                        +

                                                                                                                        Chinese Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        英文名

                                                                                                                        +

                                                                                                                        English Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.player_pic} + + {else/} + +

                                                                                                                        上传照片

                                                                                                                        + {/if} +
                                                                                                                        + +

                                                                                                                        请上传白底电子一寸照 (像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,白色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with white color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        一名飞手只允许填写一名教练员或助手。

                                                                                                                        +

                                                                                                                        A flyer is only allowed to fill in one trainer or assistant.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        ASFC飞行证号

                                                                                                                        +

                                                                                                                        ASFC Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        FAI许可证编号

                                                                                                                        +

                                                                                                                        FAI license number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        * 请检查FAI有效期。* 您必须获得FAI许可证。如果您没有FAI许可证,可咨询工作人员(咨询邮箱:service@fpvone.cn)。FAI许可证可以通过您所在国家的FAI NAC获得。如果您所在国家/地区的FAI NAC未处于活动状态,则可以直接从以下站点获取。=>http://www.droneracing.aero/

                                                                                                                        +

                                                                                                                        + * Please check the FAI expiration date. * You must obtain an FAI license. If you do not have an FAI permit, you may consult the staff at service@fpvone.cn. FAI licenses can be obtained through the FAI NAC in your country. If the FAI NAC for your country is not active, it can be obtained directly from the following sites. =>http://www.droneracing.aero/ +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 监护人证件信息 + Guardian Id Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人姓名

                                                                                                                        +

                                                                                                                        Name Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人手机号码

                                                                                                                        +

                                                                                                                        Guardian Mobile Phone Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件类型

                                                                                                                        +

                                                                                                                        Type Of Guardian Certificate

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件号码

                                                                                                                        +

                                                                                                                        Id Number Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件照片

                                                                                                                        +

                                                                                                                        Guardian Id Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.guarder_card_front_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if $playerinfo.guarder_card_back_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + \ No newline at end of file diff --git a/application/index/view/players/index.html b/application/index/view/players/index.html new file mode 100644 index 0000000..e152d50 --- /dev/null +++ b/application/index/view/players/index.html @@ -0,0 +1,173 @@ + + + + + +
                                                                                                                        + {switch name="$players.player_status" } + {case value="1"} +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        + 加急审核请致电 / Please call for urgent review:0086-13136225305 + 联系邮箱 / Contact email:service@fpvone.cn +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="3"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        + 加急审核请致电 / Please call for urgent review:0086-13136225305
                                                                                                                        + 联系邮箱 / Contact email:service@fpvone.cn +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="2"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息认证失败!/ Failed to authenticate the identity information. Procedure! +

                                                                                                                        +

                                                                                                                        + Pilot + Authentication Failure! +

                                                                                                                        +
                                                                                                                        审核内容:{$players.pass_centent.mark|default="无"}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {case value="-1"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息认证失败!/ Failed to authenticate the identity information. Procedure! +

                                                                                                                        +

                                                                                                                        + Pilot + Authentication Failure! +

                                                                                                                        +
                                                                                                                        理由:{$players.pass_centent.mark|default="无"}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="9"} + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + 信息审核通过,你已获得飞手认证 / The information is approved and you are certified as a Flyer +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {/switch} +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        提交时间 / Submission time {$players.created_at}

                                                                                                                        + 重新提交 | Resubmit +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        {$players.real_name} + +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.gender}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        {$players.birthday}
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.country}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Honor Or Completed Races

                                                                                                                        +
                                                                                                                        {$players.experience}
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        diff --git a/application/index/view/players/joinclub.html b/application/index/view/players/joinclub.html new file mode 100644 index 0000000..51b9629 --- /dev/null +++ b/application/index/view/players/joinclub.html @@ -0,0 +1,191 @@ + + + + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + + + + + + + +
                                                                                                                        +

                                                                                                                        队伍名称

                                                                                                                        +

                                                                                                                        Club Name

                                                                                                                        +
                                                                                                                        +

                                                                                                                        地区

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +

                                                                                                                        操作

                                                                                                                        +

                                                                                                                        Operation

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + \ No newline at end of file diff --git a/application/index/view/players/joinclub111.html b/application/index/view/players/joinclub111.html new file mode 100644 index 0000000..09d2a30 --- /dev/null +++ b/application/index/view/players/joinclub111.html @@ -0,0 +1,132 @@ + + + + + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        俱乐部名称

                                                                                                                        +

                                                                                                                        Club Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        所属地区

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        操作

                                                                                                                        +

                                                                                                                        Operation

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + \ No newline at end of file diff --git a/application/index/view/players/myclub - 副本.html b/application/index/view/players/myclub - 副本.html new file mode 100644 index 0000000..156ecdf --- /dev/null +++ b/application/index/view/players/myclub - 副本.html @@ -0,0 +1,386 @@ + + + + + + + +
                                                                                                                        + + {if $player_status==0} + + + + + + + + + + + +
                                                                                                                        + {elseif $player_status==2} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        队伍名称

                                                                                                                        +

                                                                                                                        Club Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.name}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        所属地区

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.province}{$club_info.city=='选择'?'':$club_info.city}{$club_info.district=='选择'?'':$club_info.district}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛主体

                                                                                                                        +

                                                                                                                        Competitors

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.competitors}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        队徽

                                                                                                                        +

                                                                                                                        Club Logo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        队旗

                                                                                                                        +

                                                                                                                        Club Flag

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        ASFC会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.is_asfc?'是':'否'}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        领队

                                                                                                                        +

                                                                                                                        Club Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.leader}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.leader_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        副领队1

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.deputy_leader1}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.deputy_leader1_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        副领队2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.deputy_leader2}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.deputy_leader2_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        主教练

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.head_coach}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.head_coach_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员1

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach1}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach1_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $club_info.coach2} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach2}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach2_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/if} + {if $club_info.coach3} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach3}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach3_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/if} + {if $club_info.coach4} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach4}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach4_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + {elseif $player_status==3} +
                                                                                                                        + +

                                                                                                                        请等待联赛组委会审核,加急审核请致电:13136225305

                                                                                                                        +
                                                                                                                        + {else} +
                                                                                                                        + +

                                                                                                                        暂未收到队伍的邀请通知,敬请期待

                                                                                                                        +
                                                                                                                        + + {/if} +
                                                                                                                        + + \ No newline at end of file diff --git a/application/index/view/players/myclub.html b/application/index/view/players/myclub.html new file mode 100644 index 0000000..5e6cc89 --- /dev/null +++ b/application/index/view/players/myclub.html @@ -0,0 +1,376 @@ + + + + + + + +
                                                                                                                        + + {if $player_status==0} + +
                                                                                                                        + {elseif $player_status==2} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        队伍名称

                                                                                                                        +

                                                                                                                        Club Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.name}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        所属地区

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.province}{$club_info.city=='选择'?'':$club_info.city}{$club_info.district=='选择'?'':$club_info.district}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛主体

                                                                                                                        +

                                                                                                                        Competitors

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.competitors}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        队徽

                                                                                                                        +

                                                                                                                        Club Logo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        队旗

                                                                                                                        +

                                                                                                                        Club Flag

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        ASFC会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.is_asfc?'是':'否'}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        领队

                                                                                                                        +

                                                                                                                        Club Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.leader}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.leader_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        副领队1

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.deputy_leader1}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.deputy_leader1_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        副领队2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.deputy_leader2}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.deputy_leader2_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        主教练

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.head_coach}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.head_coach_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员1

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach1}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach1_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $club_info.coach2} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach2}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach2_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/if} + {if $club_info.coach3} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach3}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach3_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/if} + {if $club_info.coach4} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        教练员2

                                                                                                                        +

                                                                                                                        Deputy Leader

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach4}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        联系电话

                                                                                                                        +

                                                                                                                        Contact

                                                                                                                        +
                                                                                                                        +
                                                                                                                        {$club_info.coach4_tel}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + {elseif $player_status==3} +
                                                                                                                        + +

                                                                                                                        请等待联赛组委会审核,加急审核请致电:13136225305

                                                                                                                        +
                                                                                                                        + {else} +
                                                                                                                        + +

                                                                                                                        暂未收到队伍的邀请通知,敬请期待

                                                                                                                        +
                                                                                                                        + + {/if} +
                                                                                                                        + + \ No newline at end of file diff --git a/application/index/view/players/reapply.html b/application/index/view/players/reapply.html new file mode 100644 index 0000000..7ed34b1 --- /dev/null +++ b/application/index/view/players/reapply.html @@ -0,0 +1,762 @@ + + + + + + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + + + + + + + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.player_pic} + + {else/} + +

                                                                                                                        上传照片

                                                                                                                        + {/if} +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        俱乐部成员

                                                                                                                        +

                                                                                                                        Club Members

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 监护人证件信息 + Guardian Id Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人姓名

                                                                                                                        +

                                                                                                                        Name Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人手机号码

                                                                                                                        +

                                                                                                                        Guardian Mobile Phone Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件类型

                                                                                                                        +

                                                                                                                        Type Of Guardian Certificate

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件号码

                                                                                                                        +

                                                                                                                        Id Number Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件照片

                                                                                                                        +

                                                                                                                        Guardian Id Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.guarder_card_front_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if $playerinfo.guarder_card_back_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + \ No newline at end of file diff --git a/application/index/view/players/registeredflyers - 3-17 b/application/index/view/players/registeredflyers - 3-17 new file mode 100644 index 0000000..4a82f23 --- /dev/null +++ b/application/index/view/players/registeredflyers - 3-17 @@ -0,0 +1,1215 @@ + + + + + + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + + + + + + + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.player_pic} + + {else/} + +

                                                                                                                        上传照片

                                                                                                                        + {/if} +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        俱乐部成员

                                                                                                                        +

                                                                                                                        Club Members

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 监护人证件信息 + Guardian Id Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人姓名

                                                                                                                        +

                                                                                                                        Name Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人手机号码

                                                                                                                        +

                                                                                                                        Guardian Mobile Phone Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件类型

                                                                                                                        +

                                                                                                                        Type Of Guardian Certificate

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件号码

                                                                                                                        +

                                                                                                                        Id Number Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件照片

                                                                                                                        +

                                                                                                                        Guardian Id Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.guarder_card_front_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if $playerinfo.guarder_card_back_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        上传照片

                                                                                                                        +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + + + + + + + + \ No newline at end of file diff --git a/application/index/view/players/registeredflyers - 副本.html b/application/index/view/players/registeredflyers - 副本.html new file mode 100644 index 0000000..85ba17e --- /dev/null +++ b/application/index/view/players/registeredflyers - 副本.html @@ -0,0 +1,1191 @@ + + + + + + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + + + + + + + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.player_pic} + + {else/} + +

                                                                                                                        上传照片

                                                                                                                        + {/if} +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        俱乐部成员

                                                                                                                        +

                                                                                                                        Club Members

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 监护人证件信息 + Guardian Id Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人姓名

                                                                                                                        +

                                                                                                                        Name Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人手机号码

                                                                                                                        +

                                                                                                                        Guardian Mobile Phone Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件类型

                                                                                                                        +

                                                                                                                        Type Of Guardian Certificate

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件号码

                                                                                                                        +

                                                                                                                        Id Number Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件照片

                                                                                                                        +

                                                                                                                        Guardian Id Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.guarder_card_front_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if $playerinfo.guarder_card_back_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        上传照片

                                                                                                                        +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + + + + + + + + \ No newline at end of file diff --git a/application/index/view/players/registeredflyers - 备份_2024-03-15.html b/application/index/view/players/registeredflyers - 备份_2024-03-15.html new file mode 100644 index 0000000..194f4bc --- /dev/null +++ b/application/index/view/players/registeredflyers - 备份_2024-03-15.html @@ -0,0 +1,1186 @@ + + + + + + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + + + + + + + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.player_pic} + + {else/} + +

                                                                                                                        上传照片

                                                                                                                        + {/if} +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        俱乐部成员

                                                                                                                        +

                                                                                                                        Club Members

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 监护人证件信息 + Guardian Id Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人姓名

                                                                                                                        +

                                                                                                                        Name Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人手机号码

                                                                                                                        +

                                                                                                                        Guardian Mobile Phone Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件类型

                                                                                                                        +

                                                                                                                        Type Of Guardian Certificate

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件号码

                                                                                                                        +

                                                                                                                        Id Number Of Guardian

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        监护人证件照片

                                                                                                                        +

                                                                                                                        Guardian Id Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if $playerinfo.guarder_card_front_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if $playerinfo.guarder_card_back_view} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        上传照片

                                                                                                                        +
                                                                                                                        + +

                                                                                                                        请上传电子一寸照(建议像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,纯色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with solid color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        飞行证号

                                                                                                                        +

                                                                                                                        Flight number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + + + + + + + + \ No newline at end of file diff --git a/application/index/view/players/registeredflyers.html b/application/index/view/players/registeredflyers.html new file mode 100644 index 0000000..08b376d --- /dev/null +++ b/application/index/view/players/registeredflyers.html @@ -0,0 +1,856 @@ + + + + + + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + + + + + + + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 个人基本信息 + Basic Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        类型

                                                                                                                        +

                                                                                                                        Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        中文名

                                                                                                                        +

                                                                                                                        Chinese Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        英文名

                                                                                                                        +

                                                                                                                        English Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Male +
                                                                                                                        +
                                                                                                                        + + Female +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        居住地

                                                                                                                        +

                                                                                                                        To Live

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        地址

                                                                                                                        +

                                                                                                                        Address

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        照片

                                                                                                                        +

                                                                                                                        Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        上传照片

                                                                                                                        +
                                                                                                                        + +

                                                                                                                        请上传白底电子一寸照 (像素:295*413px)

                                                                                                                        +
                                                                                                                        +

                                                                                                                        1.请上传电子一寸照,白色背景,图像清晰。

                                                                                                                        +

                                                                                                                        Please upload bareheaded photo with white color background.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        2.请勿出现大幅度表情

                                                                                                                        +

                                                                                                                        Do not show exaggerated expressions.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        辅导员姓名

                                                                                                                        +

                                                                                                                        Counselor Name

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        一名飞手只允许填写一名教练员或助手。

                                                                                                                        +

                                                                                                                        A flyer is only allowed to fill in one trainer or assistant.

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        ASFC 会员

                                                                                                                        +

                                                                                                                        ASFC User

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + Yes +
                                                                                                                        +
                                                                                                                        + + Deny +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        ASFC飞行证号

                                                                                                                        +

                                                                                                                        ASFC Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        FAI许可证编号

                                                                                                                        +

                                                                                                                        FAI license number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        * 请检查FAI有效期。* 您必须获得FAI许可证。如果您没有FAI许可证,可咨询工作人员(咨询邮箱:service@fpvone.cn)。FAI许可证可以通过您所在国家的FAI NAC获得。如果您所在国家/地区的FAI NAC未处于活动状态,则可以直接从以下站点获取。=>http://www.droneracing.aero/

                                                                                                                        +

                                                                                                                        + * Please check the FAI expiration date. * You must obtain an FAI license. If you do not have an FAI permit, you may consult the staff at service@fpvone.cn. FAI licenses can be obtained through the FAI NAC in your country. If the FAI NAC for your country is not active, it can be obtained directly from the following sites. =>http://www.droneracing.aero/ +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Entries And Awards

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 证件信息 + ID Information +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件类型

                                                                                                                        +

                                                                                                                        ID Type

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件号码

                                                                                                                        +

                                                                                                                        ID Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + * +
                                                                                                                        +

                                                                                                                        证件照片

                                                                                                                        +

                                                                                                                        ID Photo

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_front_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + {if($playerinfo && $playerinfo.card_back_view)} + + {else /} +

                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        照片可以是身份证、户口本、社保卡等证件照

                                                                                                                        +

                                                                                                                        Photos can be ID cards, household registration books, social security cards and other documents

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + + + diff --git a/application/index/view/players/sociaty.html b/application/index/view/players/sociaty.html new file mode 100644 index 0000000..72c9214 --- /dev/null +++ b/application/index/view/players/sociaty.html @@ -0,0 +1,77 @@ + + + + +
                                                                                                                        +
                                                                                                                        + + {if $is_sociaty} +
                                                                                                                        + + + + + + + + + + + + + +
                                                                                                                        +

                                                                                                                        飞手姓名

                                                                                                                        +

                                                                                                                        Pilot Name

                                                                                                                        +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Birth

                                                                                                                        +
                                                                                                                        {$playerinfo.real_name}{$playerinfo.gender}{$playerinfo.birthday}
                                                                                                                        +

                                                                                                                        您已加入飞手公会,敬请期待俱乐部邀请您加入队伍参赛

                                                                                                                        + +
                                                                                                                        + {else} + {if $is_joinclub} +
                                                                                                                        + +

                                                                                                                        您已同意队伍邀请,无法再次加入飞手公会

                                                                                                                        +
                                                                                                                        + {else} +
                                                                                                                        + +

                                                                                                                        飞手仅在加入公会后,俱乐部才可查询并邀请飞手加入队伍参赛

                                                                                                                        + +
                                                                                                                        + {/if} + {/if} +
                                                                                                                        +
                                                                                                                        + + + \ No newline at end of file diff --git a/application/index/view/third/prepare.html b/application/index/view/third/prepare.html new file mode 100644 index 0000000..bbb0c61 --- /dev/null +++ b/application/index/view/third/prepare.html @@ -0,0 +1,132 @@ +
                                                                                                                        + {if isset($userinfo['avatar'])} +
                                                                                                                        + +
                                                                                                                        {$userinfo.nickname|default=''|htmlentities}
                                                                                                                        +
                                                                                                                        + {/if} + +
                                                                                                                        + + diff --git a/application/index/view/user - 副本/attachment.html b/application/index/view/user - 副本/attachment.html new file mode 100644 index 0000000..7c34ae7 --- /dev/null +++ b/application/index/view/user - 副本/attachment.html @@ -0,0 +1,61 @@ + +{if $Think.get.dialog} + +{/if} +
                                                                                                                        + {if !$Think.get.mimetype||$Think.get.mimetype=='*'} +
                                                                                                                        + +
                                                                                                                        + {/if} + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + {if request()->get('multiple') == 'true'} + {:__('Choose')} + {/if} +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        diff --git a/application/index/view/user - 副本/authenticationflayers.html b/application/index/view/user - 副本/authenticationflayers.html new file mode 100644 index 0000000..44137b8 --- /dev/null +++ b/application/index/view/user - 副本/authenticationflayers.html @@ -0,0 +1,171 @@ + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 主页 +

                                                                                                                        +

                                                                                                                        + 身份认证 +

                                                                                                                        +

                                                                                                                        + 我的赛事 +

                                                                                                                        +

                                                                                                                        + 退出 +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {switch name="players.player_status" } + {case value="1"} +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="3"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="2"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息认证失败! +

                                                                                                                        +

                                                                                                                        + Pilot + Authentication Failure! +

                                                                                                                        +
                                                                                                                        {{ $playerService->getLastCheckLogContent() }}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="9"} + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + 信息审核通过,你已获得飞手认证 + 去参加赛事 + Participate In Events +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {/switch} +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        提交时间 {$players.created_at}

                                                                                                                        + {if $players.player_status == 2} + 重新提交 | Resubmit + {/if} +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        {$players.real_name} + +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.gender}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        {$players.birthday}
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.country}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Honor Or Completed Races

                                                                                                                        +
                                                                                                                        {$players.experience}
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        diff --git a/application/index/view/user - 副本/changepwd.html b/application/index/view/user - 副本/changepwd.html new file mode 100644 index 0000000..0b3ac72 --- /dev/null +++ b/application/index/view/user - 副本/changepwd.html @@ -0,0 +1,47 @@ + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {:token()} +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        diff --git a/application/index/view/user - 副本/identitylist.html b/application/index/view/user - 副本/identitylist.html new file mode 100644 index 0000000..87ded82 --- /dev/null +++ b/application/index/view/user - 副本/identitylist.html @@ -0,0 +1,86 @@ + + + + + diff --git a/application/index/view/user - 副本/index.html b/application/index/view/user - 副本/index.html new file mode 100644 index 0000000..c0c12a7 --- /dev/null +++ b/application/index/view/user - 副本/index.html @@ -0,0 +1,272 @@ + + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + {$user.nickname|htmlentities} +

                                                                                                                        +

                                                                                                                        + No. + {$user.member_number} +

                                                                                                                        +
                                                                                                                        + {if $user_type == 1} +
                                                                                                                        飞手
                                                                                                                        + {elseif $user_type==2} +
                                                                                                                        俱乐部
                                                                                                                        + {else} +
                                                                                                                        游客
                                                                                                                        + {/if} +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        UserName

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + 更改手机号 +
                                                                                                                        + +

                                                                                                                        Phone Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + 密码修改 +

                                                                                                                        Password

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        WeChat

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {if $user.avatar == ''} +
                                                                                                                        + +

                                                                                                                        上传照片

                                                                                                                        +
                                                                                                                        + {else /} + + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + \ No newline at end of file diff --git a/application/index/view/user - 副本/login.html b/application/index/view/user - 副本/login.html new file mode 100644 index 0000000..6532ddd --- /dev/null +++ b/application/index/view/user - 副本/login.html @@ -0,0 +1,133 @@ + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        立即注册用户,享受更多服务

                                                                                                                        +
                                                                                                                          +
                                                                                                                        • + +

                                                                                                                          更多资源

                                                                                                                          +
                                                                                                                        • +
                                                                                                                        • + +

                                                                                                                          便利沟通

                                                                                                                          +
                                                                                                                        • +
                                                                                                                        • + +

                                                                                                                          活动讯息

                                                                                                                          +
                                                                                                                        • +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + \ No newline at end of file diff --git a/application/index/view/user - 副本/profile.html b/application/index/view/user - 副本/profile.html new file mode 100644 index 0000000..b9271a2 --- /dev/null +++ b/application/index/view/user - 副本/profile.html @@ -0,0 +1,202 @@ + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {include file="common/sidenav" /} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {:token()} + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        {:__('Click to edit')}
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + + {:__('Change')} + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + + {:__('Change')} + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + diff --git a/application/index/view/user - 副本/register.html b/application/index/view/user - 副本/register.html new file mode 100644 index 0000000..b34083d --- /dev/null +++ b/application/index/view/user - 副本/register.html @@ -0,0 +1,84 @@ + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        立即注册用户,享受更多服务

                                                                                                                        +
                                                                                                                          +
                                                                                                                        • + +

                                                                                                                          更多资源

                                                                                                                          +
                                                                                                                        • +
                                                                                                                        • + +

                                                                                                                          便利沟通

                                                                                                                          +
                                                                                                                        • +
                                                                                                                        • + +

                                                                                                                          活动讯息

                                                                                                                          +
                                                                                                                        • +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        diff --git a/application/index/view/user/attachment.html b/application/index/view/user/attachment.html new file mode 100644 index 0000000..7c34ae7 --- /dev/null +++ b/application/index/view/user/attachment.html @@ -0,0 +1,61 @@ + +{if $Think.get.dialog} + +{/if} +
                                                                                                                        + {if !$Think.get.mimetype||$Think.get.mimetype=='*'} +
                                                                                                                        + +
                                                                                                                        + {/if} + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + {if request()->get('multiple') == 'true'} + {:__('Choose')} + {/if} +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        diff --git a/application/index/view/user/authenticationflayers.html b/application/index/view/user/authenticationflayers.html new file mode 100644 index 0000000..44137b8 --- /dev/null +++ b/application/index/view/user/authenticationflayers.html @@ -0,0 +1,171 @@ + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + 主页 +

                                                                                                                        +

                                                                                                                        + 身份认证 +

                                                                                                                        +

                                                                                                                        + 我的赛事 +

                                                                                                                        +

                                                                                                                        + 退出 +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {switch name="players.player_status" } + {case value="1"} +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="3"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息正在认证中... +

                                                                                                                        +

                                                                                                                        + Pilot + Under certification... +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="2"} + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        + 飞手 + 身份信息认证失败! +

                                                                                                                        +

                                                                                                                        + Pilot + Authentication Failure! +

                                                                                                                        +
                                                                                                                        {{ $playerService->getLastCheckLogContent() }}
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + + {case value="9"} + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +

                                                                                                                        + 飞手 + Pilot +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + 信息审核通过,你已获得飞手认证 + 去参加赛事 + Participate In Events +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {/case} + {/switch} +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        提交时间 {$players.created_at}

                                                                                                                        + {if $players.player_status == 2} + 重新提交 | Resubmit + {/if} +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +

                                                                                                                        姓名

                                                                                                                        +

                                                                                                                        Name

                                                                                                                        +
                                                                                                                        {$players.real_name} + +
                                                                                                                        +

                                                                                                                        性别

                                                                                                                        +

                                                                                                                        Gender

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.gender}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        出生日期

                                                                                                                        +

                                                                                                                        Date Of Birth

                                                                                                                        +
                                                                                                                        {$players.birthday}
                                                                                                                        +

                                                                                                                        国籍

                                                                                                                        +

                                                                                                                        Nationality

                                                                                                                        +
                                                                                                                        +

                                                                                                                        {$players.country}

                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛获奖经历

                                                                                                                        +

                                                                                                                        Honor Or Completed Races

                                                                                                                        +
                                                                                                                        {$players.experience}
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        diff --git a/application/index/view/user/changepwd.html b/application/index/view/user/changepwd.html new file mode 100644 index 0000000..0b3ac72 --- /dev/null +++ b/application/index/view/user/changepwd.html @@ -0,0 +1,47 @@ + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {:token()} +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        diff --git a/application/index/view/user/identitylist.html b/application/index/view/user/identitylist.html new file mode 100644 index 0000000..87ded82 --- /dev/null +++ b/application/index/view/user/identitylist.html @@ -0,0 +1,86 @@ + + + + + diff --git a/application/index/view/user/index.html b/application/index/view/user/index.html new file mode 100644 index 0000000..9049e5f --- /dev/null +++ b/application/index/view/user/index.html @@ -0,0 +1,454 @@ + + + + + + + + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        + {$user.nickname|htmlentities} +

                                                                                                                        +

                                                                                                                        + No. + {$user.member_number} +

                                                                                                                        +
                                                                                                                        + {if $user_type == 1} +
                                                                                                                        飞手
                                                                                                                        + {elseif $user_type==2} +
                                                                                                                        队伍
                                                                                                                        + {else} +
                                                                                                                        游客
                                                                                                                        + {/if} +
                                                                                                                        + {if $user_type == 1||$user_type == 2} +
                                                                                                                        +

                                                                                                                        开启微信通知

                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + {/if} +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +
                                                                                                                        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        UserName

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + + 更改手机号 +
                                                                                                                        + +

                                                                                                                        Phone Number

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + 密码修改 +

                                                                                                                        Password

                                                                                                                        +
                                                                                                                        + {if $playerinfo} +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        FAI license number

                                                                                                                        +
                                                                                                                        + {/if} +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +

                                                                                                                        WeChat

                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {if $user.avatar == ''} +
                                                                                                                        + +

                                                                                                                        上传照片

                                                                                                                        +
                                                                                                                        + {else /} + + {/if} +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + + + \ No newline at end of file diff --git a/application/index/view/user/login.html b/application/index/view/user/login.html new file mode 100644 index 0000000..6362838 --- /dev/null +++ b/application/index/view/user/login.html @@ -0,0 +1,221 @@ + + + + + + + + +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + + \ No newline at end of file diff --git a/application/index/view/user/profile.html b/application/index/view/user/profile.html new file mode 100644 index 0000000..b9271a2 --- /dev/null +++ b/application/index/view/user/profile.html @@ -0,0 +1,202 @@ + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + {include file="common/sidenav" /} +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + {:token()} + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        {:__('Click to edit')}
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + + {:__('Change')} + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + + {:__('Change')} + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        + + + + diff --git a/application/index/view/user/register.html b/application/index/view/user/register.html new file mode 100644 index 0000000..aa6ad75 --- /dev/null +++ b/application/index/view/user/register.html @@ -0,0 +1,110 @@ + + + +
                                                                                                                        + +
                                                                                                                        + +
                                                                                                                        +
                                                                                                                        + + diff --git a/application/index/view/wrj/index.html b/application/index/view/wrj/index.html new file mode 100644 index 0000000..8f3ef18 --- /dev/null +++ b/application/index/view/wrj/index.html @@ -0,0 +1,247 @@ + + + + + + + + + + + +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        +
                                                                                                                        + +

                                                                                                                        + 云朵科技 + 数据驱动决策 +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        +
                                                                                                                        +

                                                                                                                        +

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛飞手

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛飞手年龄段统计

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +

                                                                                                                        参赛飞手男女比例统计

                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                        +
                                                                                                                          +
                                                                                                                          +
                                                                                                                          +
                                                                                                                          +
                                                                                                                          +
                                                                                                                          +
                                                                                                                          +
                                                                                                                          +
                                                                                                                          +
                                                                                                                          +

                                                                                                                          竞赛日程

                                                                                                                          +
                                                                                                                          +
                                                                                                                          +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +

                                                                                                                            比赛成绩

                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                              +
                                                                                                                            • 决出前32

                                                                                                                            • +
                                                                                                                            • 32进16

                                                                                                                            • +
                                                                                                                            • 16进8

                                                                                                                            • +
                                                                                                                            • 8进4

                                                                                                                            • +
                                                                                                                            • 决赛

                                                                                                                            • +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +

                                                                                                                            + Copyright @ 浙江比翼智慧科技有限公司 浙ICP备2021002605号-3 + 技术支持:云朵科技 +

                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                              +
                                                                                                                            • 飞手数量
                                                                                                                            • +
                                                                                                                            • 飞手名单
                                                                                                                            • +
                                                                                                                            • 飞手分布
                                                                                                                            • +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            + + +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +

                                                                                                                            参赛飞手年龄段比例统计

                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +

                                                                                                                            参赛飞手男女比例统计

                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +

                                                                                                                            + Copyright @ 浙江比翼智慧科技有限公司 + 浙ICP备2021002605号-3 技术支持:云朵科技 +

                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                            +

                                                                                                                            竞赛日程

                                                                                                                            +
                                                                                                                            +
                                                                                                                            +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +

                                                                                                                              参赛飞手

                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +

                                                                                                                              比赛成绩

                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                                +
                                                                                                                              • 决出前32

                                                                                                                              • +
                                                                                                                              • 32进16

                                                                                                                              • +
                                                                                                                              • 16进8

                                                                                                                              • +
                                                                                                                              • 8进4

                                                                                                                              • +
                                                                                                                              • 决赛

                                                                                                                              • +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +

                                                                                                                              + Copyright @ 浙江比翼智慧科技有限公司 + 浙ICP备2021002605号-3 技术支持:云朵科技 +

                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +

                                                                                                                              + Copyright @ 浙江比翼智慧科技有限公司 + 浙ICP备2021002605号-3 技术支持:云朵科技 +

                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              + + + + + + + + + diff --git a/application/index/view/wrj/login.html b/application/index/view/wrj/login.html new file mode 100644 index 0000000..ff66612 --- /dev/null +++ b/application/index/view/wrj/login.html @@ -0,0 +1,127 @@ + + + + + + 登录 + + + + +
                                                                                                                              + + + +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              欢迎登录
                                                                                                                              + +
                                                                                                                              +
                                                                                                                              +
                                                                                                                              + + + + + \ No newline at end of file diff --git a/application/route.php b/application/route.php new file mode 100644 index 0000000..8d93637 --- /dev/null +++ b/application/route.php @@ -0,0 +1,25 @@ + +// +---------------------------------------------------------------------- + +return [ + //别名配置,别名只能是映射到控制器且访问时必须加上请求的方法 + '__alias__' => [ + ], + //变量规则 + '__pattern__' => [ + ], +// 域名绑定到模块 +// '__domain__' => [ +// 'admin' => 'admin', +// 'api' => 'api', +// ], +]; diff --git a/application/service/MatchService.php b/application/service/MatchService.php new file mode 100644 index 0000000..9d4aa36 --- /dev/null +++ b/application/service/MatchService.php @@ -0,0 +1,232 @@ +matchModel = new Archives(); + $this->matchContestantModel = new MatchContestant(); + $this->addonproductModel = db("cms_addonproduct"); + } + + /** + * 获取赛事信息开启缓存 + * @Author:Soar + * @Time:2023/10/8 15:33 + * @return array|\think\response\Json + */ + public function get_match() + { + $match = $this->matchModel::get($this->match_id, [], true); + $addon = $this->addonproductModel->where('id', $this->match_id)->find(); + if ($addon) { + $match->setData($addon); + } + + try { + $match = $match->toArray(); + } catch (\Exception $exception) { + $message = $exception->getMessage(); + + return $this->result([], $message, 102); + + } + + return $this->result($match); + } + + /** + * 获取赛事信息禁用缓存 + * @Author:Soar + * @Time:2023/10/8 15:33 + * @return array|\think\response\Json + */ + public function get_line_match() + { + $match = $this->matchModel::get($this->match_id, [], false); + $addon = $this->addonproductModel->where('id', $this->match_id)->find(); + if ($addon) { + $match->setData($addon); + } + + try { + $match = $match->toArray(); + } catch (\Exception $exception) { + $message = $exception->getMessage(); + + return $this->result([], $message, 102); + + } + + return $this->result($match, "请求成功"); + } + + public function get_match_players(int $players_id = null, array $where = []) + { + $collection = $this->matchContestantModel->all($where, [], true); + + return $collection; + } + + /** + * 飞手报名参赛 + * @Author:Soar + * @Time:2023/10/8 16:48 + * @param int $players_id + * @param int $match_id + */ + public function add_players_to_match(int $players_id = null, int $match_id = null) + { + //验证 + $json = $this->check_match($match_id); + + if (is_array($json) && $json['code'] != 100) { + return $json; + } + + $match_info_players = $this->get_match_user($players_id, $match_id); + + if (!empty($match_info_players)) { + return json_encode($this->result([], "报名信息已存在!请勿重复报名!", 102)); + } + + if (!$this->add_match_ontestant($players_id, $match_id)) { + return json_encode($this->result([], "报名参赛出错!", 102)); + } + + return json_encode($this->result([], "报名成功!")); + } + + /** + * 查询飞手报名列表信息 + * @Author:Soar + * @Time:2023/10/9 14:55 + * @param int|null $players_id 飞手id + * @param int|null $match_id 赛事id + * @param bool $cache 是否缓存数据 + */ + public function get_match_user(int $players_id = null, int $match_id = null, bool $cache = false) + { + // 查询是否已经报名参赛 + $match_info_players = $this->matchContestantModel; + $data = ['player_id' => $players_id, 'match_id' => $match_id]; + // 读取缓存 + if ($cache) { + $cacheKey = $this->get_cache_key($data); + + if (Cache::has($cacheKey)) { + return Cache::get($cacheKey); + } + } + + if ($players_id) { + $match_info_players->where('player_id', $players_id); + } + + $match_info_players = $match_info_players->where('match_id', $match_id) + ->where('status', 'in', [1, 2]) + ->select(); + // 设置缓存 + if ($cache) { + Cache::set($this->get_cache_key($data), $match_info_players, 3600); + } + + if (empty($match_info_players)) { + return null; + } + + return $match_info_players; + } + + /** + * 验证信息 + * @Author:Soar + * @Time:2023/10/9 15:28 + * @param int $match_id + */ + public function check_match(int $match_id) + { + $info = $this->get_match(); + //验证是否是飞手 + + // 验证赛事是否已经报名结束 + if (@strtotime($info['data']['endtime']) <= time()) { + return $this->result([], "报名时间已经截至!", 102); + } + // 验证赛事是否已经开始报名 + if (@strtotime($info['data']['starttime']) > time()) { + return $this->result([], "报名暂未开始!", 102); + } + // 验证赛事参赛人员是否已经满员 + if (@$info['data']['csrs'] <= count($this->get_match_user(null, 181, false))) { + return $this->result([], "报名人数已满!", 102); + } + + return $this->result([], 'success', 100); + } + + /** + * 参加赛事 + * @Author:Soar + * @Time:2023/10/9 15:56 + * @param int $player_id + * @param int $match_id + */ + public function add_match_ontestant(int $player_id, int $match_id) + { + return $this->matchContestantModel->insert([ + 'match_id' => $match_id, + 'player_id' => $player_id, + 'status' => 1, + 'created_at' => date("Y-m-d H:i:s") + ]); + } + + /** + * 获取缓存主键 + * @Created by PhpStorm. + * @Author:Soar + * @Time:2023/10/9 15:27 + * @param array $data 条件信息 + * @return string + */ + public function get_cache_key(array $data) + { + return $cacheKey = md5('match:' . serialize($data)); + } + + public function result($data = [], $msg = " ", $code = 100) + { + $res['msg'] = $msg; + $res['data'] = $data; + $res['code'] = $code; + + return $res; + } + + public function getMatchId(): int + { + return $this->match_id; + } + + public function setMatchId(int $match_id): void + { + $this->match_id = $match_id; + } + + + +} \ No newline at end of file diff --git a/application/shopro/Category.php b/application/shopro/Category.php new file mode 100644 index 0000000..7ef40bc --- /dev/null +++ b/application/shopro/Category.php @@ -0,0 +1,216 @@ +model = new CategoryModel; + } + + + /** + * 服务保障列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $categories = $this->model->sheepFilter()->where('parent_id', 0)->order('weigh', 'desc')->order('id', 'desc')->select(); + + $this->success('获取成功', null, $categories); + } + + + + + /** + * 添加服务保障 + * + * @return \think\Response + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'name', 'style', 'description', 'weigh', 'categories' + ]); + $this->svalidate($params, ".add"); + $categories = json_decode($params['categories'], true); + + Db::transaction(function () use ($params, $categories) { + $this->model->allowField(true)->save($params); + + //递归处理分类数据 + $this->createOrEditCategory($categories, $this->model->id); + }); + $this->success('保存成功'); + } + + + /** + * 服务保障详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $category = $this->model->where('parent_id', 0)->where('id', $id)->find(); + if (!$category) { + $this->error(__('No Results were found')); + } + + $categories = $this->model->with('children.children')->where('parent_id', $category->id)->order('weigh', 'desc')->order('id', 'desc')->select(); + + $this->success('获取成功', null, ['category' => $category, 'categories' => $categories]); + } + + + + /** + * 修改商品分类 + * + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isPost()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'name', 'style', 'description', 'weigh', 'categories' + ]); + $this->svalidate($params, ".edit"); + $categories = json_decode($params['categories'], true); + $category = $this->model->where('parent_id', 0)->where('id', $id)->find(); + if (!$category) { + $this->error(__('No Results were found')); + } + Db::transaction(function () use ($category, $params, $categories) { + $category->allowField(true)->save($params); + + //递归处理分类数据 + $this->createOrEditCategory($categories, $category->id); + }); + $this->success('更新成功'); + } + + + + /** + * 删除服务标签 + * + * @param string $id 要删除的服务保障列表 + * @return void + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->with('children')->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + if ($item->children) { + $this->error('请先删除子分类'); + } + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + /** + * 获取所有服务列表 + * + * @return \think\Response + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $categories = (new Tree(function () { + // 组装搜索条件,排序等 + return $this->model->field("id, name, parent_id, status, weigh")->normal() + ->order('weigh', 'desc')->order('id', 'asc'); + }))->getTree(0); + + $this->success('获取成功', null, $categories); + } + + + + private function createOrEditCategory($categories, $parent_id) + { + foreach ($categories as $key => $data) { + $data['parent_id'] = $parent_id; + + if (isset($data['id']) && $data['id']) { + $category = $this->model->find($data['id']); + if (!$category) { + $this->error(__('No Results were found')); + } + if (isset($data['deleted']) && $data['deleted'] == 1) { + $category->delete(); + } else { + $category->name = $data['name']; + $category->parent_id = $data['parent_id']; + $category->image = $data['image']; + $category->description = $data['description'] ?? null; + $category->status = $data['status']; + $category->weigh = $data['weigh']; + $category->save(); + } + } else { + if (!isset($data['deleted']) || !$data['deleted']) { + $category = new CategoryModel; + $category->name = $data['name']; + $category->parent_id = $data['parent_id']; + $category->image = $data['image']; + $category->description = $data['description'] ?? null; + $category->status = $data['status']; + $category->weigh = $data['weigh']; + $category->save(); + $data['id'] = $category->id; + } + } + + if (isset($data['children']) && !empty($data['children']) && isset($data['id'])) { + $this->createOrEditCategory($data['children'], $data['id']); + } + } + } +} diff --git a/application/shopro/Common.php b/application/shopro/Common.php new file mode 100644 index 0000000..50f4bff --- /dev/null +++ b/application/shopro/Common.php @@ -0,0 +1,31 @@ + 'v3'], false); + } + + $is_pro = check_env('commission', false); + \think\View::share('is_pro', $is_pro); + $this->assignconfig("is_pro", $is_pro); + } +} diff --git a/application/shopro/Config.php b/application/shopro/Config.php new file mode 100644 index 0000000..794dfaf --- /dev/null +++ b/application/shopro/Config.php @@ -0,0 +1,319 @@ + '基本信息', + 'name' => 'shopro/config/basic', + 'status' => $this->auth->check('shopro/config/basic') + ], + [ + 'label' => '用户配置', + 'name' => 'shopro/config/user', + 'status' => $this->auth->check('shopro/config/user') + ], + [ + 'label' => '平台配置', + 'name' => 'shopro/config/platform', + 'status' => $this->auth->check('shopro/config/platform') + ], + [ + 'label' => '订单配置', + 'name' => 'shopro/config/order', + 'status' => $this->auth->check('shopro/config/order') + ], + [ + 'label' => '商品配置', + 'name' => 'shopro/config/goods', + 'status' => $this->auth->check('shopro/config/goods') + ], + [ + 'label' => '物流配置', + 'name' => 'shopro/config/dispatch', + 'status' => $this->auth->check('shopro/config/dispatch') + ], + [ + 'label' => '充值提现', + 'name' => 'shopro/config/rechargewithdraw', + 'status' => $this->auth->check('shopro/config/rechargewithdraw') + ], + [ + 'label' => '分销配置', + 'name' => 'shopro/config/commission', + 'status' => $this->auth->check('shopro/config/commission') + ], + [ + 'label' => '支付配置', + 'name' => 'shopro/pay_config', + 'status' => $this->auth->check('shopro/pay_config') + ], + [ + 'label' => '客服配置', + 'name' => 'shopro/config/chat', + 'status' => $this->auth->check('shopro/config/chat') + ], + [ + 'label' => 'Redis配置', + 'name' => 'shopro/config/redis', + 'status' => $this->auth->check('shopro/config/redis') + ] + ]; + $this->assignconfig("configList", $configList); + return $this->view->fetch(); + } + + /** + * 基本配置 + */ + public function basic() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.basic', false); + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.basic', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 用户默认配置 + */ + public function user() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.user', false); + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.user', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 物流配置 + */ + public function dispatch() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.dispatch', false); + $configs['callback'] = $this->request->domain() . '/addons/shopro/order.express/push'; + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.dispatch', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 平台状态 + */ + public function platformStatus() + { + $status = [ + 'H5' => ShoproConfig::getConfigs('shop.platform.H5.status', false), + 'App' => ShoproConfig::getConfigs('shop.platform.App.status', false), + 'WechatMiniProgram' => ShoproConfig::getConfigs('shop.platform.WechatMiniProgram.status', false), + 'WechatOfficialAccount' => ShoproConfig::getConfigs('shop.platform.WechatOfficialAccount.status', false), + ]; + + $this->success('操作成功', null, $status); + } + + + + /** + * 平台配置 + */ + public function platform($platform) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + if (!in_array($platform, ['App', 'H5', 'WechatMiniProgram', 'WechatOfficialAccount'])) { + $this->error('平台不支持'); + } + + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.platform.' . $platform, false); + } elseif ('POST' === $this->request->method()) { + $params = $this->request->param(); + if (!isset($params['share']['methods'])) { + $params['share']['methods'] = []; + } + if (!isset($params['payment']['methods'])) { + $params['payment']['methods'] = []; + } + $configs = ShoproConfig::setConfigs('shop.platform.' . $platform, $params); + } + $this->success('操作成功', null, $configs); + } + + + public function commission() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.commission', false); + } elseif ('POST' === $this->request->method()) { + check_env('commission'); + $params = $this->request->param(); + + $configs = ShoproConfig::setConfigs('shop.commission', $params); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 订单配置 + */ + public function order() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.order', false); + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.order', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 商品配置 + */ + public function goods() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.goods', false); + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('shop.goods', $this->request->param()); + } + $this->success('操作成功', null, $configs); + } + + + /** + * 充值提现配置 + */ + public function rechargeWithdraw() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('shop.recharge_withdraw', false); + } elseif ('POST' === $this->request->method()) { + $params = $this->request->param(); + if (!isset($params['recharge']['methods'])) { + $params['recharge']['methods'] = []; + } + if (!isset($params['recharge']['quick_amounts'])) { + $params['recharge']['quick_amounts'] = []; + } + if (!isset($params['withdraw']['methods'])) { + $params['withdraw']['methods'] = []; + } + $configs = ShoproConfig::setConfigs('shop.recharge_withdraw', $params); + } + $this->success('操作成功', null, $configs); + } + + + + /** + * 客服配置 + */ + public function chat() + { + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('chat', false); + } elseif ('POST' === $this->request->method()) { + $configs = ShoproConfig::setConfigs('chat', $this->request->param()); + + // 存文件 + file_put_contents( + ROOT_PATH . 'application' . DS . 'extra' . DS . 'chat.php', + 'request->param(), true) . ";" + ); + } + $this->success('操作成功', null, $configs); + } + + + + /** + * redis 配置 + */ + public function redis() + { + if ('GET' === $this->request->method()) { + $default = [ + 'host' => '127.0.0.1', // redis 主机地址 + 'password' => '', // redis 密码 + 'port' => 6379, // redis 端口 + 'select' => 1, // redis 数据库 + 'timeout' => 0, // redis 超时时间 + 'persistent' => false, // redis 持续性,连接复用 + ]; + $redis = \think\Config::get('redis'); + $redis['empty_password'] = 0; + $redis['password'] = ''; // 隐藏密码 + $configs = $redis ? array_merge($default, $redis) : $default; + } elseif ('POST' === $this->request->method()) { + $configs = $this->request->param(); + $empty_password = (int)$configs['empty_password']; // 是否设置空密码 + unset($configs['empty_password']); + + if (isset($configs['password']) && empty($configs['password'])) { + $redis = \think\Config::get('redis'); + // 不修改密码,保持为原始值 + $configs['password'] = $redis['password'] ?? ''; + } elseif ($empty_password) { + $configs['password'] = ''; + } + + $configs['persistent'] = (isset($configs['persistent']) && ($configs['persistent'] === true || $configs['persistent'] == 'true')) ? true : false; + + // 存文件 + file_put_contents( + ROOT_PATH . 'application' . DS . 'extra' . DS . 'redis.php', + 'success('操作成功', null, $configs); + } + + + + public function getPlatformUrl() + { + $h5Url = ShoproConfig::getConfigField('shop.basic.domain'); + $wechatMpAppid = ShoproConfig::getConfigField('shop.platform.WechatMiniProgram.app_id'); + + $this->success('', null, [ + 'url' => $h5Url, + 'appid' => $wechatMpAppid + ]); + } +} diff --git a/application/shopro/Coupon.php b/application/shopro/Coupon.php new file mode 100644 index 0000000..b501283 --- /dev/null +++ b/application/shopro/Coupon.php @@ -0,0 +1,292 @@ +model = new CouponModel; + } + + + + /** + * 优惠券列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $coupons = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10))->each(function ($coupon) { + // 优惠券领取和使用数量 + $coupon->get_num = $coupon->get_num; + $coupon->use_num = $coupon->use_num; + }); + + $result = [ + 'coupons' => $coupons, + ]; + + $result['total_num'] = UserCouponModel::count(); + $result['expire_num'] = UserCouponModel::expired()->count(); + $result['use_num'] = UserCouponModel::used()->count(); + $result['use_percent'] = 0 . '%'; + if ($result['total_num']) { + $result['use_percent'] = bcdiv(bcmul((string)$result['use_num'], '100'), (string)$result['total_num'], 1) . '%'; + } + + $this->success('获取成功', null, $result); + } + + + + + /** + * 添加优惠券 + * + * @return \think\Response + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'name', 'type', 'use_scope', 'items', 'amount', 'max_amount', 'enough', + 'stock', 'limit_num', 'get_time', + 'use_time_type', 'use_time', 'start_days', 'days', + 'is_double_discount', 'description', 'status' + ]); + $this->svalidate($params, ".add"); + + // 时间转换 + $getTime = explode(' - ', $params['get_time']); + $useTime = explode(' - ', $params['use_time']); + unset($params['get_time'], $params['use_time']); + $params['get_start_time'] = $getTime[0] ?? 0; + $params['get_end_time'] = $getTime[1] ?? 0; + $params['use_start_time'] = $useTime[0] ?? 0; + $params['use_end_time'] = $useTime[1] ?? 0; + + $this->model->save($params); + + $this->success('保存成功'); + } + + + /** + * 优惠券详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $coupon = $this->model->where('id', $id)->find(); + if (!$coupon) { + $this->error(__('No Results were found')); + } + + $coupon->items_value = $coupon->items_value; // 可用范围值 + + $this->success('获取成功', null, $coupon); + } + + + + /** + * 修改优惠券 + * + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'name', 'use_scope', 'items', 'amount', 'max_amount', 'enough', + 'stock', 'limit_num', 'get_time', + 'use_time_type', 'use_time', 'start_days', 'days', + 'is_double_discount', 'description', 'status' + ]); + $this->svalidate($params); + + // 时间转换 + if (isset($params['get_time'])) { + $getTime = explode(' - ', $params['get_time']); + + $params['get_start_time'] = $getTime[0] ?? 0; + $params['get_end_time'] = $getTime[1] ?? 0; + unset($params['get_time']); + } + + if (isset($params['use_time'])) { + $useTime = explode(' - ', $params['use_time']); + + $params['use_start_time'] = $useTime[0] ?? 0; + $params['use_end_time'] = $useTime[1] ?? 0; + unset($params['use_time']); + } + + $coupon = $this->model->where('id', $id)->find(); + if (!$coupon) { + $this->error(__('No Results were found')); + } + + $coupon->save($params); + $this->success('更新成功'); + } + + + + /** + * 删除优惠券 + * + * @param string $id 要删除的优惠券 + * @return void + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + /** + * 优惠券列表 + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type', 'page'); + + $coupons = $this->model->sheepFilter(); + + if ($type == 'select') { + // 普通结果 + $coupons = $coupons->select(); + } elseif ($type == 'find') { + $coupons = $coupons->find(); + } else { + // 分页结果 + $coupons = $coupons->paginate($this->request->param('list_rows', 10)); + } + + $this->success('获取成功', null, $coupons); + } + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $coupons = $this->model->onlyTrashed()->sheepFilter()->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $coupons); + } + + + /** + * 还原(支持批量) + * + * @param $id + * @return \think\Response + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $items = $this->model->onlyTrashed()->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + $count += $item->restore(); + } + + return $count; + }); + + if ($result) { + $this->success('还原成功', null, $result); + } else { + $this->error(__('No rows were updated')); + } + } + + + /** + * 销毁(支持批量) + * + * @param $id + * @return \think\Response + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + if ($id !== 'all') { + $items = $this->model->onlyTrashed()->whereIn('id', $id)->select(); + } else { + $items = $this->model->onlyTrashed()->select(); + } + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + // 删除商品 + $count += $item->delete(true); + } + return $count; + }); + + if ($result) { + $this->success('销毁成功', null, $result); + } + $this->error('销毁失败'); + } +} diff --git a/application/shopro/Dashboard.php b/application/shopro/Dashboard.php new file mode 100644 index 0000000..a969b22 --- /dev/null +++ b/application/shopro/Dashboard.php @@ -0,0 +1,241 @@ + 'total', + 'card' => 'shopro/dashboard/total', + 'status' => $this->auth->check('shopro/dashboard/total') + ], + [ + 'name' => 'chart', + 'card' => 'shopro/dashboard/chart', + 'status' => $this->auth->check('shopro/dashboard/chart') + ], + [ + 'name' => 'ranking', + 'card' => 'shopro/dashboard/ranking', + 'status' => $this->auth->check('shopro/dashboard/ranking') + ], + ]; + $this->assignconfig('cardList', $cardList); + return $this->view->fetch(); + } + + + public function total() + { + // 用户数据 + $userData = [ + 'total' => User::count(), + 'today' => User::whereTime('createtime', 'today')->count(), + 'week' => User::whereTime('createtime', 'week')->count(), + 'array' => collection(User::field('id,createtime')->whereTime('createtime', 'today')->select())->each(function ($user) { + $user->counter = 1; + $user->createtime_unix = $user->getData('createtime') * 1000; + }) + ]; + + // -- commission code start -- + $agentData = [ + 'total' => \app\admin\model\shopro\commission\Agent::count(), + 'today' => \app\admin\model\shopro\commission\Agent::whereTime('createtime', 'today')->count(), + 'week' => \app\admin\model\shopro\commission\Agent::whereTime('createtime', 'week')->count(), + 'array' => collection(\app\admin\model\shopro\commission\Agent::field('user_id,createtime')->whereTime('createtime', 'today')->select())->each(function ($agent) { + $agent->counter = 1; + $agent->createtime_unix = $agent->getData('createtime') * 1000; + }) + ]; + // -- commission code end -- + + // 分享数据 + $shareData = [ + 'total' => Share::count(), + 'today' => Share::whereTime('createtime', 'today')->count(), + 'week' => Share::whereTime('createtime', 'week')->count(), + 'array' => collection(Share::field('id,createtime')->whereTime('createtime', 'today')->select())->each(function ($share) { + $share->counter = 1; + $share->createtime_unix = $share->getData('createtime') * 1000; + }) + ]; + + $this->success('获取成功', null, [ + 'user_data' => $userData, + 'agent_data' => $agentData ?? null, + 'share_data' => $shareData + ]); + } + + + + public function chart() + { + $date = $this->request->param('date', ''); + $date = array_values(array_filter(explode(' - ', $date))); + + $orders = Order::with(['items'])->whereTime('createtime', 'between', $date) + ->order('id', 'asc')->select(); + + // 订单数 + $data['orderNum'] = count($orders); + $data['orderArr'] = []; + + // 支付订单(包含退款的订单, 不包含货到付款还未收货(未支付)订单) + $data['payOrderNum'] = 0; + $data['payOrderArr'] = []; + //支付金额(包含退款的订单, 不包含货到付款还未收货(未支付)订单) + $data['payAmountNum'] = 0; + $data['payAmountArr'] = []; + + // 支付用户(一个人不管下多少单,都算一个,包含退款的订单, 不包含货到付款还未收货(未支付)订单) + $userIds = []; + $data['payUserNum'] = 0; + $data['payUserArr'] = []; + + // 代发货(包含货到付款) + $data['noSendNum'] = 0; + $data['noSendArr'] = []; + //售后维权 + $data['aftersaleNum'] = 0; + $data['aftersaleArr'] = []; + //退款订单 + $data['refundNum'] = 0; + $data['refundArr'] = []; + + foreach ($orders as $key => $order) { + $data['orderArr'][] = [ + 'counter' => 1, + 'createtime' => $order->getData('createtime') * 1000, + 'user_id' => $order->user_id + ]; + + // 已支付的,不包含,货到付款未支付的 + if (in_array($order->status, [Order::STATUS_PAID, Order::STATUS_COMPLETED])) { + // 支付订单数 + $data['payOrderNum']++; + + $data['payOrderArr'][] = [ + 'counter' => 1, + 'createtime' => $order->getData('createtime') * 1000, + 'user_id' => $order->user_id + ]; + + // 支付金额 + $data['payAmountNum'] = bcadd((string)$data['payAmountNum'], $order->pay_fee, 2); + + $data['payAmountArr'][] = [ + 'counter' => $order->pay_fee, + 'createtime' => $order->getData('createtime') * 1000, + ]; + + // 下单用户 + if (!in_array($order->user_id, $userIds)) { + $data['payUserNum']++; + $data['payUserArr'][] = [ + 'counter' => 1, + 'createtime' => $order->getData('createtime') * 1000, + 'user_id' => $order->user_id + ]; + } + } + + // 已支付的,和 货到付款未支付的 + if (in_array($order->status, [Order::STATUS_PAID, Order::STATUS_COMPLETED]) || $order->isOffline($order)) { + $flagnoSend = false; + $flagaftersale = false; + $flagrefund = false; + $aftersaleIng = false; + foreach ($order->items as $k => $item) { + if ( + !$flagnoSend + && $item->dispatch_status == OrderItem::DISPATCH_STATUS_NOSEND + && $item->refund_status == OrderItem::REFUND_STATUS_NOREFUND + && in_array($order->apply_refund_status, [ + Order::APPLY_REFUND_STATUS_NOAPPLY, + Order::APPLY_REFUND_STATUS_REFUSE + ]) + ) { + $flagnoSend = true; + } + + if ( + $item->aftersale_status == OrderItem::AFTERSALE_STATUS_ING + && $item->dispatch_status == OrderItem::DISPATCH_STATUS_NOSEND + && $item->refund_status == OrderItem::REFUND_STATUS_NOREFUND + ) { + $aftersaleIng = true; + } + + if (!$flagaftersale && $item->aftersale_status != OrderItem::AFTERSALE_STATUS_NOAFTER) { + $data['aftersaleNum']++; + // $data['aftersaleArr'][] = [ + // 'counter' => 1, + // 'createtime' => $order->getData('createtime') * 1000, + // ]; + $flagaftersale = true; + } + + if (!$flagrefund && $item->refund_status > OrderItem::REFUND_STATUS_NOREFUND) { + $data['refundNum']++; + // $data['refundArr'][] = [ + // 'counter' => 1, + // 'createtime' => $order->getData('createtime') * 1000, + // ]; + $flagrefund = true; + } + } + + if (!$aftersaleIng && $flagnoSend) { + // 存在正在售后中的订单,不算待发货(和订单列表保持一直) + $data['noSendNum']++; + // $data['noSendArr'][] = [ + // 'counter' => 1, + // 'createtime' => $order->getData('createtime') * 1000, + // ]; + } + } + } + + $this->success('获取成功', null, $data); + } + + + + public function ranking() + { + $goods = Goods::limit(5)->order('sales', 'desc')->select(); + foreach ($goods as $key => $gd) { + $gd->append(['real_sales']); + $result = OrderItem::field('sum(goods_num * goods_price) as sale_total_money')->where('goods_id', $gd['id']) + ->whereExists(function ($query) use ($gd) { + $order_table_name = (new Order())->getQuery()->getTable(); + $table_name = (new OrderItem())->getQuery()->getTable(); + + $query->table($order_table_name)->where($table_name . '.order_id=' . $order_table_name . '.id') + ->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED]); // 已支付的订单 + })->find(); + + $gd['sale_total_money'] = $result['sale_total_money'] ?: 0; + } + + $searchHistory = new SearchHistory(); + + $this->success('获取成功', null, [ + 'goods' => $goods, + 'hot_search' => $searchHistory->hotSearch() + ]); + } +} diff --git a/application/shopro/Feedback.php b/application/shopro/Feedback.php new file mode 100644 index 0000000..057e9e5 --- /dev/null +++ b/application/shopro/Feedback.php @@ -0,0 +1,112 @@ +model = new FeedbackModel(); + } + + + /** + * 查看 + * + * @return Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $feedbacks = $this->model->sheepFilter()->with('user')->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $feedbacks); + } + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $detail = $this->model->with('user')->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $detail); + } + + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + $params = $this->request->only(['status', 'remark']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + /** + * 删除优惠券 + * + * @param string $id 要删除的意见反馈 + * @return void + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/shopro/PayConfig.php b/application/shopro/PayConfig.php new file mode 100644 index 0000000..dfbfa2e --- /dev/null +++ b/application/shopro/PayConfig.php @@ -0,0 +1,223 @@ +model = new PayConfigModel; + } + + + /** + * 支付配置列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $payConfigs = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $payConfigs); + } + + + + + /** + * 添加支付配置 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'name', 'type', 'params', 'status' + ]); + $this->svalidate($params, ".add"); + $this->svalidate($params['params'], '.' . $params['type']); // 验证对应的支付参数是否设置完整 + + $this->model->save($params); + + $this->success('保存成功'); + } + + + /** + * 支付配置详情 + * + * @param $id + */ + public function detail($id) + { + $payConfig = $this->model->where('id', $id)->find(); + if (!$payConfig) { + $this->error(__('No Results were found')); + } + + $payConfig->append(['params']); + $this->success('获取成功', null, pay_config_show($payConfig)); + } + + + + /** + * 修改支付配置 + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'name', 'params', 'status' + ]); + $this->svalidate($params); + + $payConfig = $this->model->where('id', $id)->find(); + if (!$payConfig) { + $this->error(__('No Results were found')); + } + + if (isset($params['params'])) { + $this->svalidate($params['params'], '.' . $payConfig['type']); // 验证对应的支付参数是否设置完整 + } + + $payConfig->save($params); + $this->success('更新成功'); + } + + + + /** + * 删除支付配置 + * + * @param string $id 要删除的商品分类列表 + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + /** + * 获取所有支付配置 + */ + public function select() + { + $payConfigs = $this->model->sheepFilter()->normal() + ->field('id, name, type,status') + ->select(); + + $this->success('获取成功', null, $payConfigs); + } + + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $goods = $this->model->onlyTrashed()->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $goods); + } + + + /** + * 还原(支持批量) + * + * @param $id + * @return \think\Response + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $items = $this->model->onlyTrashed()->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + $count += $item->restore(); + } + + return $count; + }); + + if ($result) { + $this->success('还原成功', null, $result); + } else { + $this->error(__('No rows were updated')); + } + } + + + /** + * 销毁(支持批量) + * + * @param $id + * @return \think\Response + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + if ($id !== 'all') { + $items = $this->model->onlyTrashed()->whereIn('id', $id)->select(); + } else { + $items = $this->model->onlyTrashed()->select(); + } + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $config) { + // 删除商品 + $count += $config->delete(true); + } + return $count; + }); + + if ($result) { + $this->success('销毁成功', null, $result); + } + $this->error('销毁失败'); + } +} diff --git a/application/shopro/Share.php b/application/shopro/Share.php new file mode 100644 index 0000000..a82e46e --- /dev/null +++ b/application/shopro/Share.php @@ -0,0 +1,30 @@ +model = new ShareModel(); + } + + /** + * 查看用户分享记录 + */ + public function index() + { + $share_id = $this->request->param('id'); + + $data = ShareModel::with(['user' => function ($query) { + return $query->field(['id', 'nickname', 'avatar']); + }])->where('share_id', $share_id)->sheepFilter()->paginate($this->request->param('list_rows', 8)); + + $this->success('', null, $data); + } +} diff --git a/application/shopro/Withdraw.php b/application/shopro/Withdraw.php new file mode 100644 index 0000000..d9b9227 --- /dev/null +++ b/application/shopro/Withdraw.php @@ -0,0 +1,126 @@ +model = new WithdrawModel; + $this->logModel = new WithdrawLogModel; + } + + + + /** + * 提现列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $withdraws = $this->model->sheepFilter()->with(['user'])->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $withdraws); + } + + + /** + * 提现日志 + */ + public function log($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $logs = $this->logModel->where('withdraw_id', $id)->order('id desc')->select(); + $morphs = [ + 'user' => UserModel::class, + 'admin' => AdminModel::class, + 'system' => AdminModel::class + ]; + $logs = morph_to($logs, $morphs, ['oper_type', 'oper_id']); + $logs = $logs->toArray(); + + // 解析操作人信息 + foreach ($logs as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + $this->success('获取成功', null, $logs); + } + + + public function handle($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->param(); + $action = $params['action'] ?? null; + $refuse_msg = $params['refuse_msg'] ?? ''; + if ($action == 'refuse' && !$refuse_msg) { + $this->error('请输入拒绝原因'); + } + + $ids = is_array($id) ? $id : explode(',', $id); + foreach ($ids as $key => $id) { + Db::startTrans(); + try { + $withdraw = $this->model->lock(true)->where('id', $id)->find(); + if (!$withdraw) { + $this->error(__('No Results were found')); + } + $withdrawLib = new WithdrawLibrary($withdraw->user_id); + + switch ($action) { + case 'agree': + $withdraw = $withdrawLib->handleAgree($withdraw); + break; + case 'agree&withdraw': + $withdraw = $withdrawLib->handleAgree($withdraw); + $withdraw = $withdrawLib->handleWithdraw($withdraw); + break; + case 'withdraw': + $withdraw = $withdrawLib->handleWithdraw($withdraw); + break; + case 'refuse': + $withdraw = $withdrawLib->handleRefuse($withdraw, $refuse_msg); + break; + } + + Db::commit(); + } catch (ShoproException $e) { + Db::commit(); // 不回滚,记录错误日志 + $this->error($e->getMessage()); + } catch (HttpResponseException $e) { + $data = $e->getResponse()->getData(); + $message = $data ? ($data['msg'] ?? '') : $e->getMessage(); + $this->error($message); + } catch (\Exception $e) { + Db::rollback(); + $this->error($e->getMessage()); + } + } + + $this->success('处理成功'); + } +} diff --git a/application/shopro/activity/Activity.php b/application/shopro/activity/Activity.php new file mode 100644 index 0000000..cc6822b --- /dev/null +++ b/application/shopro/activity/Activity.php @@ -0,0 +1,321 @@ +model = new ActivityModel; + $this->manager = ActivityFacade::instance(); + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + $type = $this->request->param('type', null); + + if (!$this->request->isAjax()) { + if ($type) { + return $this->view->fetch('shopro/activity/activity/index'); + } + + return $this->view->fetch('shopro/activity/activity/activity'); + } + + $activities = $this->model->sheepFilter()->where('type', $type)->paginate(request()->param('list_rows', 10))->toArray(); + + $items = $activities['data']; + + // 关联活动的商品 + $goodsIds = array_values(array_filter(array_column($items, 'goods_ids'))); + $goodsIdsArr = []; + foreach ($goodsIds as $ids) { + $idsArr = explode(',', $ids); + $goodsIdsArr = array_merge($goodsIdsArr, $idsArr); + } + $goodsIdsArr = array_values(array_filter(array_unique($goodsIdsArr))); + if ($goodsIdsArr) { + // 查询商品 + $goods = GoodsModel::where('id', 'in', $goodsIdsArr)->select(); + $goods = array_column($goods, null, 'id'); + } + foreach ($items as $key => $activity) { + $items[$key]['goods'] = []; + if ($activity['goods_ids']) { + $idsArr = explode(',', $activity['goods_ids']); + foreach ($idsArr as $id) { + if (isset($goods[$id])) { + $items[$key]['goods'][] = $goods[$id]; + } + } + } + } + + $activities['data'] = $items; + + $this->success('获取成功', null, $activities); + } + + + // 获取数据类型 + public function getType() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $typeList = $this->model->typeList(); + + $result = [ + 'type_list' => $typeList, + ]; + + $data = []; + foreach ($result as $key => $list) { + $data[$key][] = ['name' => '全部', 'type' => 'all']; + + foreach ($list as $k => $v) { + $data[$key][] = [ + 'name' => $v, + 'type' => $k + ]; + } + } + + $this->success('获取成功', null, $data); + } + + + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'title', 'goods_ids', 'type', 'prehead_time', 'start_time', 'end_time', + 'rules', 'richtext_id', 'richtext_title', 'goods_list' + ]); + if (isset($params['goods_list'])) { + $params['goods_list'] = json_decode($params['goods_list'], true); + } + $this->svalidate($params, ".add"); + + Db::transaction(function () use ($params) { + $this->manager->save($params); + }); + + $this->success('保存成功'); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $activity = $this->model->where('id', $id)->find(); + if (!$activity) { + $this->error(__('No Results were found')); + } + $activity->goods_list = $activity->goods_list; + $activity->rules = $activity->rules; + + $this->success('获取成功', null, $activity); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'title', 'goods_ids', 'prehead_time', 'start_time', 'end_time', + 'rules', 'richtext_id', 'richtext_title', 'goods_list' + ]); + if (isset($params['goods_list'])) { + $params['goods_list'] = json_decode($params['goods_list'], true); + } + $this->svalidate($params); + + $id = explode(',', $id); + $items = $this->model->whereIn('id', $id)->select(); + + Db::transaction(function () use ($items, $params) { + foreach ($items as $activity) { + $this->manager->update($activity, $params); + } + }); + + $this->success('更新成功'); + } + + + /** + * 获取活动商品规格并且初始化 + */ + public function skus() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->param(); + $id = $params['id'] ?? 0; + $goods_id = $params['goods_id'] ?? 0; + $activity_type = $params['activity_type'] ?? ''; + $start_time = $params['start_time'] ?? ''; + $end_time = $params['end_time'] ?? ''; + $prehead_time = $params['prehead_time'] ?? ''; + + if ($start_time && $end_time && $activity_type) { + // 如果存在开始结束时间,并且是要修改 + $goodsList = [$goods_id => ['id' => $goods_id]]; + + $this->checkActivityConflict([ + 'type' => $activity_type, + 'classify' => $this->model->getClassify($activity_type), + 'start_time' => $start_time, + 'end_time' => $end_time, + 'prehead_time' => $prehead_time + ], $goodsList, $id); + } + + // 商品规格 + $skus = SkuModel::with('children')->where('goods_id', $goods_id)->where('parent_id', 0)->select(); + + // 获取规格 + $skuPrices = SkuPriceModel::with(['activity_sku_price' => function ($query) use ($id) { + $query->where('activity_id', $id); + }])->where('goods_id', $goods_id)->select(); + + + //编辑 + $activitySkuPrices = []; + foreach ($skuPrices as $k => $skuPrice) { + $activitySkuPrices[$k] = $skuPrice->activity_sku_price ? $skuPrice->activity_sku_price->toArray() : []; + // 活动规格数据初始化 + if (!$activitySkuPrices[$k]) { + $activitySkuPrices[$k]['id'] = 0; + $activitySkuPrices[$k]['status'] = 'down'; + $activitySkuPrices[$k]['price'] = ''; + $activitySkuPrices[$k]['stock'] = ''; + $activitySkuPrices[$k]['sales'] = '0'; + $activitySkuPrices[$k]['goods_sku_price_id'] = $skuPrice->id; + } + + // 个性化初始化每个活动的 规格 字段 + $activitySkuPrices[$k] = $this->manager->showSkuPrice($activity_type, $activitySkuPrices[$k]); + } + + $this->success('获取成功', null, [ + 'skus' => $skus, + 'sku_prices' => $skuPrices, + 'activity_sku_prices' => $activitySkuPrices, + ]); + } + + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type'); + $activities = $this->model->sheepFilter()->whereIn('type', $type)->paginate(request()->param('list_rows', 10))->toArray(); + + $this->success('获取成功', null, $activities); + } + + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $this->manager->delete($item); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type'); + $activities = $this->model->onlyTrashed()->sheepFilter()->where('type', $type)->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $activities); + } +} diff --git a/application/shopro/activity/Groupon.php b/application/shopro/activity/Groupon.php new file mode 100644 index 0000000..cadc494 --- /dev/null +++ b/application/shopro/activity/Groupon.php @@ -0,0 +1,124 @@ +model = new GrouponModel; + } + + + + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $groupons = $this->model->sheepFilter()->with(['goods', 'user', 'grouponLogs']) + ->paginate(request()->param('list_rows', 10)); + + $this->success('获取成功', null, $groupons); + } + + + + /** + * 团详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $groupon = $this->model->with(['goods', 'user', 'grouponLogs'])->where('id', $id)->find(); + if (!$groupon) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $groupon); + } + + + /** + * 添加虚拟用户(人满自动成团) + * + * @param Request $request + * @param integer $id + */ + public function addUser($id) + { + $groupon = $this->model->where('id', $id)->find(); + if (!$groupon) { + $this->error(__('No Results were found')); + } + + $activity = ActivityModel::where('id', $groupon['activity_id'])->find(); + if (!$activity) { + $this->error('活动不存在'); + } + if ($groupon['status'] != 'ing' || $groupon['current_num'] > $groupon['num']) { + $this->error('团已完成或已失效'); + } + + $avatar = $this->request->param('avatar', ''); + $nickname = $this->request->param('nickname', ''); + $user = ['avatar' => $avatar, 'nickname' => $nickname]; + + Db::transaction(function () use ($activity, $groupon, $user) { + // 增加人数 + $this->finishFictitiousGroupon($activity, $groupon, false, 1, [$user]); + }); + + $this->success('操作成功'); + } + + + + /** + * 解散团,自动退款 + * + * @param Request $request + * @param integer $id + */ + public function invalid($id) + { + $admin = $this->auth->getUserInfo(); + $admin = Admin::find($admin['id']); + $groupon = $this->model->where('id', $id)->find(); + + if ($groupon['status'] != 'ing') { + $this->error('团已完成或已失效'); + } + + Db::transaction(function () use ($groupon, $admin) { + // 解散团,并退款 + $this->invalidRefundGroupon($groupon, $admin); + }); + + $this->success('操作成功'); + } +} diff --git a/application/shopro/app/ScoreShop.php b/application/shopro/app/ScoreShop.php new file mode 100644 index 0000000..46f7fe4 --- /dev/null +++ b/application/shopro/app/ScoreShop.php @@ -0,0 +1,313 @@ +model = new ScoreSkuPrice; + $this->goodsModel = new GoodsModel(); + } + + + /** + * 积分商城商品列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $scoreGoodsIds = $this->model->group('goods_id')->column('goods_id'); + + $scoreGoods = $this->goodsModel->sheepFilter()->with(['score_sku_prices']) + ->whereIn('id', $scoreGoodsIds) + ->paginate($this->request->param('list_rows', 10))->each(function($goods) { + $goods->score_price = $goods->score_price; + $goods->score_sales = $goods->score_sales; + $goods->score_stock = $goods->score_stock; + }); + + $this->success('获取成功', null, $scoreGoods); + } + + + /** + * skuPrices列表 + */ + public function skuPrices($goods_id) + { + $skuPrices = $this->model->up()->with(['sku_price'])->where('goods_id', $goods_id)->select(); + $skuPrices = collection($skuPrices)->each(function ($skuPrice) { + $skuPrice->goods_sku_ids = $skuPrice->goods_sku_ids; + $skuPrice->goods_sku_text = $skuPrice->goods_sku_text; + $skuPrice->image = $skuPrice->image; + $skuPrice->score_price = $skuPrice->score_price; + }); + + $this->success('获取成功', null, $skuPrices); + } + + + + /** + * 添加积分商城商品 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'goods_id', 'sku_prices' + ]); + $this->svalidate($params, ".add"); + + // 检查是否已经是积分商品了 + $count = $this->model->where('goods_id', $params['goods_id'])->count(); + if ($count) { + error_stop('该商品已经是积分商城商品了'); + } + + Db::transaction(function () use ($params) { + $this->editSkuPrices($params['goods_id'], $params); + }); + $this->success('保存成功'); + } + + + public function skus($goods_id) + { + $skus = SkuModel::with('children')->where('goods_id', $goods_id)->where('parent_id', 0)->select(); + $skuPrices = SkuPriceModel::with(['score_sku_price'])->where('goods_id', $goods_id)->select(); + + //编辑 + $scoreSkuPrices = []; + foreach ($skuPrices as $k => $skuPrice) { + $scoreSkuPrices[$k] = $skuPrice->score_sku_price ? : []; + // 活动规格数据初始化 + if (!$scoreSkuPrices[$k]) { + $scoreSkuPrices[$k]['id'] = 0; + $scoreSkuPrices[$k]['status'] = 'down'; + $scoreSkuPrices[$k]['price'] = ''; + $scoreSkuPrices[$k]['score'] = ''; + $scoreSkuPrices[$k]['stock'] = ''; + $scoreSkuPrices[$k]['goods_sku_price_id'] = $skuPrice->id; + } + } + + $this->success('获取成功', null, [ + 'skus' => $skus, + 'sku_prices' => $skuPrices, + 'score_sku_prices' => $scoreSkuPrices + ]); + } + + + /** + * 编辑积分商城商品 + */ + public function edit($goods_id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'sku_prices' + ]); + $this->svalidate($params, ".edit"); + + Db::transaction(function () use ($goods_id, $params) { + $this->editSkuPrices($goods_id, $params); + }); + $this->success('更新成功'); + } + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type', 'page'); + $scoreGoodsIds = $this->model->group('goods_id')->column('goods_id'); + + $scoreGoods = $this->goodsModel->sheepFilter()->with(['score_sku_prices']) + ->whereIn('id', $scoreGoodsIds); + + if ($type == 'select') { + // 普通结果 + $scoreGoods = $scoreGoods->select(); + $scoreGoods = collection($scoreGoods); + } else { + // 分页结果 + $scoreGoods = $scoreGoods->paginate($this->request->param('list_rows', 10)); + } + + $scoreGoods = $scoreGoods->each(function ($goods) { + $goods->score_price = $goods->score_price; + $goods->score_sales = $goods->score_sales; + $goods->score_stock = $goods->score_stock; + }); + + $this->success('获取成功', null, $scoreGoods); + } + + + + /** + * 删除积分商城商品 + * + * @param string $id 要删除的积分商城商品 id + * @return void + */ + public function delete($goods_id) + { + if (empty($goods_id)) { + $this->error(__('Parameter %s can not be empty', 'goods_id')); + } + + $goodsIds = explode(',', $goods_id); + $list = $this->model->whereIn('goods_id', $goodsIds)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $scoreGoodsIds = $this->model->onlyTrashed()->group('goods_id')->column('goods_id'); + $scoreGoods = $this->goodsModel->sheepFilter()->with(['del_score_sku_prices']) + ->whereIn('id', $scoreGoodsIds) + ->paginate($this->request->param('list_rows', 10))->each(function ($skuPrice) { + $deleteTimes = collection($skuPrice->del_score_sku_prices)->column('deletetime'); + $skuPrice->deletetime = $deleteTimes ? max($deleteTimes) : null; // 取用积分规格的删除时间 + }); + + $this->success('获取成功', null, $scoreGoods); + } + + + /** + * 还原(支持批量) + * + * @param $id + * @return \think\Response + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'goods_id')); + } + + $goodsIds = explode(',', $id); + Db::transaction(function () use ($goodsIds) { + foreach ($goodsIds as $goods_id) { + $count = $this->model->where('goods_id', $goods_id)->count(); + if ($count) { + error_stop('商品 ID 为 ' . $goods_id . ' 的商品已经是积分商品了,不可还原'); + } + + $list = $this->model->onlyTrashed()->whereIn('goods_id', $goods_id)->select(); + foreach ($list as $goods) { + $goods->restore(); + } + } + }); + + $this->success('还原成功'); + } + + + /** + * 销毁(支持批量) + * + * @param $id + * @return \think\Response + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'goods_id')); + } + + $goodsIds = explode(',', $id); + Db::transaction(function () use ($goodsIds) { + if (!in_array('all', $goodsIds)) { + foreach ($goodsIds as $goods_id) { + $list = $this->model->onlyTrashed()->whereIn('goods_id', $goods_id)->select(); + foreach ($list as $goods) { + $goods->delete(true); + } + } + } else { + $list = $this->model->onlyTrashed()->select(); + foreach ($list as $goods) { + $goods->delete(true); + } + } + }); + + $this->success('销毁成功'); + } + + + private function editSkuPrices($goods_id, $params) + { + //下架全部规格 + $this->model->where('goods_id', $goods_id)->update(['status' => 'down']); + + foreach ($params['sku_prices'] as $key => $skuPrice) { + if ($skuPrice['id'] == 0) { + unset($skuPrice['id']); + } + unset($skuPrice['sales']); //不更新销量 + unset($skuPrice['createtime'], $skuPrice['updatetime'], $skuPrice['deletetime']); // 不手动更新时间 + $skuPrice['goods_id'] = $goods_id; + + $model = new ScoreSkuPrice; + if (isset($skuPrice['id'])) { + $model = $this->model->find($skuPrice['id']); + $model = $model ? : new ScoreSkuPrice; + } + + $model->save($skuPrice); + } + } +} diff --git a/application/shopro/app/mplive/Goods.php b/application/shopro/app/mplive/Goods.php new file mode 100644 index 0000000..e569a67 --- /dev/null +++ b/application/shopro/app/mplive/Goods.php @@ -0,0 +1,163 @@ +request->isAjax()) { + return $this->view->fetch(); + } + + $model = new MpliveGoodsModel(); + $list = $model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + + // 批量更新直播商品状态 + // $this->updateAuditStatusByGoods($list); + + $this->success('获取成功', null, $list); + } + + // 直播间商品详情 + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $goods = (new MpliveGoodsModel)->findOrFail($id); + + $this->success('', null, $goods); + } + + // 创建直播间商品并提交审核 + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->param(); + + $data = [ + "coverImgUrl" => $this->uploadMedia($params['cover_img_url']), + "name" => $params['name'], + "priceType" => $params['price_type'], + "price" => $params['price'], + "price2" => $params['price_type'] === 1 ? "" : $params['price2'], // priceType为2或3时必填 + "url" => $params['url'], + ]; + + $res = $this->wechat->broadcast->create($data); + + $this->catchLiveError($res); + + $params['id'] = $res['goodsId']; + $params['audit_id'] = $res['auditId']; + $params['audit_status'] = 1; + + (new MpliveGoodsModel)->save($params); + + $this->success("操作成功"); + } + + // 直播商品审核 + public function audit($id) + { + $id = intval($id); + $act = $this->request->param('act'); + + $goods = MpliveGoodsModel::where('id', $id)->find(); + if (!$goods) { + error_stop('未找到该商品'); + } + // 撤回审核 + if ($act === 'reset') { + $auditId = $goods->audit_id; + if ($auditId) { + $res = $this->wechat->broadcast->resetAudit($auditId, $id); + $this->catchLiveError($res); + } + } + + // 重新审核 + if ($act === 'resubmit') { + $res = $this->wechat->broadcast->resubmitAudit($id); + $this->catchLiveError($res); + $goods->audit_id = $res['auditId']; + $goods->save(); + } + + return $this->status($id); + } + + // 删除直播商品 + public function delete($id) + { + $id = intval($id); + $res = $this->wechat->broadcast->delete($id); + + $this->catchLiveError($res); + + MpliveGoodsModel::where('id', $id)->delete(); + $this->success('操作成功'); + } + + // 更新直播商品 + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->param(); + + $data = [ + 'goodsId' => $id, + "coverImgUrl" => $this->uploadMedia($params['cover_img_url']), + "name" => $params['name'], + "priceType" => $params['price_type'], + "price" => $params['price'], + "price2" => $params['price_type'] === 1 ? "" : $params['price2'], // priceType为2或3时必填 + "url" => $params['url'], + ]; + + $res = $this->wechat->broadcast->update($data); + + $this->catchLiveError($res); + + $goods = MpliveGoodsModel::where('id', $id)->find(); + $goods->save($data); + + $this->success('操作成功'); + } + + // 更新直播商品状态 + public function status($id) + { + $res = $this->wechat->broadcast->getGoodsWarehouse([$id]); + + $this->catchLiveError($res); + + $list = $res['goods']; + + foreach ($list as $key => $goods) { + $mpliveGoods = MpliveGoodsModel::where('id', $goods['goods_id'])->find(); + if ($mpliveGoods) { + $mpliveGoods->audit_status = $goods['audit_status']; + $mpliveGoods->third_party_tag = $goods['third_party_tag']; + $mpliveGoods->save(); + } + } + + $this->success('操作成功'); + } +} diff --git a/application/shopro/app/mplive/Index.php b/application/shopro/app/mplive/Index.php new file mode 100644 index 0000000..5cff66e --- /dev/null +++ b/application/shopro/app/mplive/Index.php @@ -0,0 +1,81 @@ +wechat = Wechat::miniProgram(); + + (new ServiceProvider())->register($this->wechat); + } + + // 上传临时素材 + protected function uploadMedia($path) + { + $filesystem = config('filesystem.default'); + if ($filesystem !== 'local' || is_url($path)) { + $body = Http::get(cdnurl($path, true)); + $dir = RUNTIME_PATH . 'storage' . DS . 'temp'; + if (!is_dir($dir)) { + @mkdir($dir, 0755, true); + } + $temp_path = $dir . $this->getBaseName($path); + file_put_contents($temp_path, $body); + } else { + $temp_path = ROOT_PATH . 'public' . $path; + } + + if (!isset($temp_path) || empty($temp_path)) { + error_stop("上传失败,不是一个有效的文件: " . $path); + } + + $media = $this->wechat->media; + $res = $media->uploadImage($temp_path); + @unlink($temp_path); // 删除临时文件 + if (isset($res['media_id'])) { + return $res['media_id']; + } + return ''; + } + + // 解析图片文件名 + private function getBaseName($path) + { + if (strpos($path, 'mmbiz.qpic.cn') !== false) { + return DS . gen_random_str(8) . '.jpg'; + } + + return basename($path); + } + + // 转义直播错误信息 + protected function catchLiveError($response) + { + if (!isset($response['errcode'])) { + error_stop("未知错误"); + } + + $errorMap = MpliveRoomModel::ERR_CODE; + if (isset($response['errcode']) && ($response['errcode'] !== 0 && $response['errcode'] !== 1001)) { + if (isset($errorMap[$response['errcode']])) { + error_stop("{$errorMap[$response['errcode']]} [错误码: {$response['errcode']}]"); + } else { + error_stop("{$response['errmsg']} [错误码: {$response['errcode']}]"); + } + } + } +} diff --git a/application/shopro/app/mplive/Room.php b/application/shopro/app/mplive/Room.php new file mode 100644 index 0000000..c6d5a35 --- /dev/null +++ b/application/shopro/app/mplive/Room.php @@ -0,0 +1,201 @@ +request->isAjax()) { + return $this->view->fetch(); + } + + $list = (new MpliveRoomModel)->sheepFilter()->select(); + + $this->success('', null, $list); + } + + // 直播间详情 + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $room = (new MpliveRoomModel)->where('roomid', $id)->findOrFail(); + + $this->success('', null, $room); + } + + // 同步直播间列表 + public function sync() + { + $res = $this->wechat->broadcast->getRooms(); + $data = []; + + $this->catchLiveError($res); + + MpliveRoomModel::where('roomid', '>', 0)->delete(); + foreach ($res['room_info'] as $room) { + $room['status'] = $room['live_status']; + $room['type'] = $room['live_type']; + $data[] = $room; + } + + MpliveRoomModel::strict(false)->insertAll($data); + $list = MpliveRoomModel::select(); + + $this->success('', null, $list); + } + + // 创建直播间 + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->param(); + + $data = [ + 'name' => $params['name'], // 房间名字 + 'coverImg' => $this->uploadMedia($params['cover_img']), // 通过 uploadfile 上传,填写 mediaID + 'shareImg' => $this->uploadMedia($params['share_img']), //通过 uploadfile 上传,填写 mediaID + 'feedsImg' => $this->uploadMedia($params['feeds_img']), //通过 uploadfile 上传,填写 mediaID + 'startTime' => $params['start_time'], // 开始时间 + 'endTime' => $params['end_time'], // 结束时间 + 'anchorName' => $params['anchor_name'], // 主播昵称 + 'anchorWechat' => $params['anchor_wechat'], // 主播微信号 + 'subAnchorWechat' => $params['sub_anchor_wechat'], // 主播副号微信号 + 'isFeedsPublic' => $params['is_feeds_public'], // 是否开启官方收录,1 开启,0 关闭 + 'type' => $params['type'], // 直播类型,1 推流 0 手机直播 + 'closeLike' => $params['close_like'], // 是否关闭点赞 1:关闭 + 'closeGoods' => $params['close_goods'], // 是否关闭商品货架,1:关闭 + 'closeComment' => $params['close_comment'], // 是否开启评论,1:关闭 + 'closeReplay' => $params['close_replay'], // 是否关闭回放 1 关闭 + 'closeKf' => $params['close_kf'], // 是否关闭客服,1 关闭 + ]; + + $res = $this->wechat->broadcast->createLiveRoom($data); + + $this->catchLiveError($res); + + return $this->sync(); + } + + // 更新直播间 + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->param(); + + $data = [ + 'id' => $id, + 'name' => $params['name'], // 房间名字 + 'coverImg' => $this->uploadMedia($params['cover_img']), // 通过 uploadfile 上传,填写 mediaID + 'shareImg' => $this->uploadMedia($params['share_img']), //通过 uploadfile 上传,填写 mediaID + 'feedsImg' => $this->uploadMedia($params['feeds_img']), //通过 uploadfile 上传,填写 mediaID + 'startTime' => $params['start_time'], // 开始时间 + 'endTime' => $params['end_time'], // 结束时间 + 'anchorName' => $params['anchor_name'], // 主播昵称 + 'anchorWechat' => $params['anchor_wechat'], // 主播昵称 + 'isFeedsPublic' => $params['is_feeds_public'], // 是否开启官方收录,1 开启,0 关闭 + 'type' => $params['type'], // 直播类型,1 推流 0 手机直播 + 'closeLike' => $params['close_like'], // 是否关闭点赞 1:关闭 + 'closeGoods' => $params['close_goods'], // 是否关闭商品货架,1:关闭 + 'closeComment' => $params['close_comment'], // 是否开启评论,1:关闭 + 'closeReplay' => $params['close_replay'], // 是否关闭回放 1 关闭 + 'closeKf' => $params['close_kf'], // 是否关闭客服,1 关闭 + ]; + + $res = $this->wechat->broadcast->updateLiveRoom($data); + + $this->catchLiveError($res); + + return $this->sync(); + } + + // 删除直播间 + public function delete($id) + { + $res = $this->wechat->broadcast->deleteLiveRoom([ + 'id' => $id, + ]); + + $this->catchLiveError($res); + + MpliveRoomModel::where('roomid', $id)->delete(); + $this->success('操作成功'); + } + + // 推流地址 + public function pushUrl($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $res = $this->wechat->broadcast->getPushUrl([ + 'roomId' => $id + ]); + + $this->catchLiveError($res); + + $this->success('', null, ['pushAddr' => $res['pushAddr']]); + } + + // 分享二维码 + public function qrcode($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $res = $this->wechat->broadcast->getShareQrcode([ + 'roomId' => $id + ]); + + $this->catchLiveError($res); + + $this->success('', null, ['pagePath' => $res['pagePath'], 'cdnUrl' => $res['cdnUrl']]); + } + + // 查看回放 + public function playback($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $res = $this->wechat->broadcast->getPlaybacks((int)$id, (int)$start = 0, (int)$limit = 10); + + $this->catchLiveError($res); + + $data = $res['live_replay']; + + $this->success('', null, $data); + } + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = (new MpliveRoomModel)->sheepFilter()->select(); + + $this->success('', null, $list); + } +} diff --git a/application/shopro/chat/CommonWord.php b/application/shopro/chat/CommonWord.php new file mode 100644 index 0000000..ff03ee6 --- /dev/null +++ b/application/shopro/chat/CommonWord.php @@ -0,0 +1,129 @@ +model = new ChatCommonWord; + } + + /** + * 常用语列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $commonWords = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $commonWords); + } + + + /** + * 常用语添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['room_id', 'name', 'status', 'weigh']); + $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params, ".add"); + + $this->model->save($params); + $this->success('保存成功', null, $this->model); + } + + + + /** + * 常用语详情 + * + * @param $id + */ + public function detail($id) + { + $commonWord = $this->model->where('id', $id)->find(); + if (!$commonWord) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $commonWord); + } + + + + /** + * 常用语编辑 + * + * @param $id + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['room_id', 'name', 'status', 'weigh']); + $this->request->has('content') && $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params); + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + /** + * 删除(支持批量) + * + * @param $id + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/shopro/chat/CustomerService.php b/application/shopro/chat/CustomerService.php new file mode 100644 index 0000000..5285128 --- /dev/null +++ b/application/shopro/chat/CustomerService.php @@ -0,0 +1,205 @@ +model = new ChatCustomerService; + $this->adminModel = new Admin; + } + + /** + * 客服列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $customerService = $this->model->sheepFilter()->with('customer_service_user')->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $customerService); + } + + + /** + * 客服添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['name', 'avatar', 'room_id', 'max_num', 'auth', 'auth_id']); + $this->svalidate($params, ".add"); + + if ($this->checkHasAuthId($params['auth'], $params['auth_id'], $params['room_id'])) { + error_stop('该身份已绑定其他客服'); + } + + $data = Db::transaction(function () use ($params) { + $this->model->allowField(true)->save($params); + + $customerServiceUser = CustomerServiceUser::create([ + 'customer_service_id' => $this->model->id, + 'auth' => $params['auth'], + 'auth_id' => $params['auth_id'], + ]); + + return $customerServiceUser; + }); + $this->success('保存成功', null, $data); + } + + + + /** + * 客服详情 + * + * @param $id + */ + public function detail($id) + { + $customerService = $this->model->with(['customer_service_user'])->where('id', $id)->find(); + if (!$customerService) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $customerService); + } + + + + /** + * 客服编辑 + * + * @param $id + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['name', 'avatar', 'room_id', 'max_num', 'auth', 'auth_id']); + $this->svalidate($params); + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->with('customer_service_user')->select(); + Db::transaction(function () use ($list, $params) { + foreach ($list as $customerService) { + $customerService->allowField(true)->save($params); + + $customerServiceUser = $customerService['customer_service_user']; + + // 编辑了客服身份所有者 + if ($params['auth'] != $customerServiceUser['auth'] || $params['auth_id'] != $customerServiceUser['auth_id']) { + // 验证新的身份是否已经被绑定别的客服 + if ($this->checkHasAuthId($params['auth'], $params['auth_id'], $params['room_id'])) { + error_stop('该身份已绑定其他客服'); + } + + // 删除老的身份 + CustomerServiceUser::{'auth' . ucfirst($customerServiceUser['auth'])}($customerServiceUser['auth_id']) + ->where('customer_service_id', $customerService['id'])->delete(); + + // 添加新的身份 + $customerServiceUser = CustomerServiceUser::create([ + 'customer_service_id' => $customerService->id, + 'auth' => $params['auth'], + 'auth_id' => $params['auth_id'], + ]); + } + } + }); + + $this->success('更新成功'); + } + + + /** + * 客服用语 + * + * @param $id + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + Db::transaction(function () use ($list) { + foreach ($list as $customerService) { + // 删除客服的身份 + CustomerServiceUser::where('customer_service_id', $customerService['id'])->delete(); + + $customerService->delete(); + } + }); + + $this->success('删除成功'); + } + + + /** + * 检验是否已经被绑定了客服(一个管理员或者用户只能是一种客服) + * + * @param string $auth + * @param integer $auth_id + * @return void + */ + private function checkHasAuthId($auth, $auth_id, $room_id) + { + $customerServiceUser = CustomerServiceUser::{'auth' . ucfirst($auth)}($auth_id)->with('customer_service')->find(); + + if ($customerServiceUser) { + $customerService = $customerServiceUser['customer_service']; + if ($customerService && $customerService['room_id'] == $room_id) { + return true; + } + } + + return false; + } + + + /** + * 获取管理员列表 + * + * @return void + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $id = $this->request->param('id', 0); + $room_id = $this->request->param('room_id', 'admin'); + + // 已经被设置为客服的管理员 + $adminIds = CustomerServiceUser::whereExists(function ($query) use ($room_id) { + $table_name = $this->model->getQuery()->getTable(); + $query->table($table_name)->where('room_id', $room_id)->where('customer_service_id=id'); + })->where('auth', 'admin')->where('customer_service_id', '<>', $id)->column('auth_id'); + + // 正常的,并且排除了已经设置为客服的管理员 + $admins = $this->adminModel->where('status', 'normal')->whereNotIn('id', $adminIds)->select(); + + $this->success('获取成功', null, $admins); + } +} diff --git a/application/shopro/chat/Index.php b/application/shopro/chat/Index.php new file mode 100644 index 0000000..5488864 --- /dev/null +++ b/application/shopro/chat/Index.php @@ -0,0 +1,38 @@ +getUnifiedToken('admin:' . $admin['id']); // 统一验证 token + + // 客服配置 + $chatSystem = sheep_config('chat.system'); + + // 初始化 socket ssl 类型, 默认 cert + $ssl = $chatSystem['ssl'] ?? 'none'; + $chat_domain = ($ssl == 'none' ? 'http://' : 'https://') . request()->host(true) . ($ssl == 'reverse_proxy' ? '' : (':' . $chatSystem['port'])) . '/chat'; + + $data = [ + 'token' => $token, + 'chat_domain' => $chat_domain, + 'default_rooms' => CustomerService::defaultRooms() + ]; + $this->success('获取成功', null, $data); + } +} diff --git a/application/shopro/chat/Question.php b/application/shopro/chat/Question.php new file mode 100644 index 0000000..1180656 --- /dev/null +++ b/application/shopro/chat/Question.php @@ -0,0 +1,133 @@ +model = new ChatQuestion; + } + + /** + * 猜你想问列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $questions = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $questions); + } + + + /** + * 猜你想问添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['room_id', 'title', 'status', 'weigh']); + $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params, ".add"); + + $this->model->save($params); + $this->success('保存成功', null, $this->model); + } + + + + /** + * 猜你想问详情 + * + * @param $id + */ + public function detail($id) + { + $question = $this->model->where('id', $id)->find(); + if (!$question) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $question); + } + + + + /** + * 猜你想问编辑 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['room_id', 'title', 'status', 'weigh']); + $this->request->has('content') && $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params); + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + /** + * 删除猜你想问 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/shopro/chat/Record.php b/application/shopro/chat/Record.php new file mode 100644 index 0000000..69ea07d --- /dev/null +++ b/application/shopro/chat/Record.php @@ -0,0 +1,37 @@ +model = new ChatRecord; + } + + /** + * 聊天列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $records = $this->model->sheepFilter()->order('id desc')->paginate($this->request->param('list_rows', 10)); + + $morphs = [ + 'customer' => \app\admin\model\shopro\chat\User::class, + 'customer_service' => \app\admin\model\shopro\chat\CustomerService::class, + ]; + $records = morph_to($records, $morphs, ['sender_identify', 'sender_id']); + + $this->success('获取成功', null, $records); + } + +} diff --git a/application/shopro/chat/User.php b/application/shopro/chat/User.php new file mode 100644 index 0000000..5725ad5 --- /dev/null +++ b/application/shopro/chat/User.php @@ -0,0 +1,62 @@ +model = new ChatUser; + } + + /** + * 会话列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $user = $this->model->sheepFilter()->with(['user', 'customer_service'])->where('auth', 'user')->order('id desc')->paginate(request()->param('list_rows', 10)); + $this->success('获取成功', null, $user); + } + + + /**· + * 删除会话 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + Db::transaction(function () use ($list) { + foreach ($list as $user) { + // 删除这个会话的所有服务记录 + ServiceLog::where('chat_user_id', $user->id)->delete(); + + // 删除这个会话的所有聊天记录 + Record::where('chat_user_id', $user->id)->delete(); + + // 删除这个会话 + $user->delete(); + } + }); + + $this->success('删除成功'); + } +} diff --git a/application/shopro/commission/Agent.php b/application/shopro/commission/Agent.php new file mode 100644 index 0000000..aaf9c52 --- /dev/null +++ b/application/shopro/commission/Agent.php @@ -0,0 +1,250 @@ +model = new AgentModel(); + } + + /** + * 查看 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->with(['user.parent_user', 'level_info', 'level_status_info', 'upgrade_level'])->paginate($this->request->param('list_rows', 10)); + + $this->success('分销商列表', null, $list); + } + + /** + * 详情 + * + * @param $id + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $detail = $this->model->with(['user.parent_user', 'level_info', 'level_status_info', 'upgrade_level'])->where('user_id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + + $this->success('分销商详情', null, $detail); + } + + + /** + * 团队 + * + * @param $id + */ + public function team($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $detail = $this->model->with(['user.parent_user', 'level_info'])->where('user_id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + + $detail->agent_team = AgentModel::hasWhere('user', function ($query) use ($detail) { + return $query->where('parent_user_id', $detail->user_id); + })->with(['user', 'level_info'])->select(); + $this->success('分销商详情', null, $detail); + } + + // 选择分销商 + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $data = $this->model->sheepFilter()->with(['user', 'level_info', 'level_status_info', 'upgrade_level']) + ->paginate($this->request->param('list_rows', 10)); + + $this->success('选择分销商', null, $data); + } + + /** + * 编辑 + * + * @param $id + */ + public function edit($id = null) + { + $params = $this->request->only(['status', 'upgrade_lock', 'level_status', 'level', 'apply_info']); + + $result = Db::transaction(function () use ($id, $params) { + $row = $this->model->with(['user', 'level_info', 'level_status_info', 'upgrade_level'])->where('user_id', $id)->find(); + if (!$row) { + $this->error('未找到该分销商'); + } + + foreach ($params as $field => $value) { + switch ($field) { + case 'status': // 修改状态 + return $this->changeStatus($row, $value); + break; + case 'level_status': // 审核等级 + return $this->changeLevelStatus($row, $value); + break; + case 'level': // 修改等级 + return $this->changeLevel($row, $value); + break; + default: + return $row->save([$field => $value]); + } + } + }); + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } + + + // 修改状态 + private function changeStatus($row, $value) + { + $result = $row->save(['status' => $value]); + if ($result) { + LogModel::add($row->user_id, 'agent', ['type' => 'status', 'value' => $value]); + (new AgentService($row->user_id))->createAsyncAgentUpgrade(); + } + return $result; + } + + // 审核等级 + private function changeLevelStatus($row, $value) + { + if ($row->level_status == 0 && $value > 0) { + $this->error('非法操作'); + } + + if ($value == 0) { // 拒绝操作 + return $row->save(['level_status' => 0]); + } else { // 同意操作 + if ($row->upgrade_level) { + $result = $row->save(['level_status' => 0, 'level' => $row->upgrade_level->level]); + if ($result) { + LogModel::add($row->user_id, 'agent', ['type' => 'level', 'level' => $row->upgrade_level]); + (new AgentService($row->user_id))->createAsyncAgentUpgrade(); + } + return $result; + } + } + return false; + } + + // 修改等级 + private function changeLevel($row, $value) + { + $level = LevelModel::find($value); + if ($level) { + $result = $row->save(['level' => $level->level]); + if ($result) { + LogModel::add($row->user_id, 'agent', ['type' => 'level', 'level' => $level]); + (new AgentService($row->user_id))->createAsyncAgentUpgrade(); + } + return $result; + } else { + $this->error('未找到该等级'); + } + } + + // 更换推荐人 + public function changeParentUser($id) + { + $userAgent = new AgentService($id); + + if (!$userAgent->user) { + $this->error('未找到该用户'); + } + + $parentUserId = $this->request->param('parent_user_id', 0); + + // 更换推荐人检查 + if ($parentUserId != 0) { + $parentAgent = new AgentService($parentUserId); + if (!$parentAgent->isAgentAvaliable()) { + $this->error('选中用户暂未成为分销商,不能成为推荐人'); + } + if (!$this->checkChangeParentAgent($id, $parentUserId)) { + $this->error('不能绑定该上级'); + } + LogModel::add($parentUserId, 'share', ['user' => $userAgent->user]); + + if ($userAgent->isAgentAvaliable()) { + LogModel::add($id, 'bind', ['user' => $parentAgent->user ?? NULL]); + } + } + + $lastParentUserId = $userAgent->user->parent_user_id; + + $userAgent->user->parent_user_id = $parentUserId; + $userAgent->user->save(); + + if ($lastParentUserId > 0) { + $userAgent->createAsyncAgentUpgrade($lastParentUserId); + } + + if ($parentUserId > 0) { + $userAgent->createAsyncAgentUpgrade($parentUserId); + } + $this->success('绑定成功'); + } + + // 递归往上找推荐人,防止出现推荐循环 + private function checkChangeParentAgent($userId, $parentUserId) + { + if ($userId == $parentUserId) { + + $this->error('推荐人不能是本人'); + } + if ($parentUserId == 0) { + return true; + } + + $parentAgent = UserModel::find($parentUserId); + + if ($parentAgent) { + if ($parentAgent->parent_user_id == $userId) { + $this->error("已选中分销商的上级团队中已存在该用户"); + } + if ($parentAgent->parent_user_id == 0) { + return true; + } else { + return $this->checkChangeParentAgent($userId, $parentAgent->parent_user_id); + } + } + + return false; + } +} diff --git a/application/shopro/commission/Goods.php b/application/shopro/commission/Goods.php new file mode 100644 index 0000000..7d92d7f --- /dev/null +++ b/application/shopro/commission/Goods.php @@ -0,0 +1,89 @@ +model = new CommissionGoodsModel(); + $this->goodsModel = new GoodsModel(); + } + + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $data = $this->goodsModel->sheepFilter()->with('commission_goods')->paginate($this->request->param('list_rows', 10)); + $this->success('分销商品列表', null, $data); + } + + /** + * 详情 + * + * @param $id + */ + public function detail($id) + { + $goodsList = collection(GoodsModel::with(['commission_goods'])->whereIn('id', $id)->select())->each(function ($goods) { + $goods->skus = $goods->skus; + $goods->sku_prices = $goods->sku_prices; + }); + + $config = sheep_config('shop.commission'); + $this->success('分销商品详情', null, [ + 'goods' => $goodsList, + 'config' => $config + ]); + } + + /** + * 设置佣金(支持批量) + * + * @param $id + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + // 接受全部参数 + $params = $this->request->only(['status', 'self_rules', 'commission_order_status', 'commission_config', 'commission_rules']); + + $result = Db::transaction(function () use ($id, $params) { + $count = 0; + $ids = explode(',', $id); + + foreach ($ids as $goods_id) { + if ($row = $this->model->get($goods_id)) { + $row->save($params); + } else { + $model = new CommissionGoodsModel(); + $params['goods_id'] = $goods_id; + $model->save($params); + } + $count++; + } + + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } +} diff --git a/application/shopro/commission/Level.php b/application/shopro/commission/Level.php new file mode 100644 index 0000000..efe785b --- /dev/null +++ b/application/shopro/commission/Level.php @@ -0,0 +1,144 @@ +model = new LevelModel(); + } + + /** + * 查看 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $defaultLevel = $this->model->find(1); + if (!$defaultLevel) { + $this->model->save([ + 'name' => '默认等级', + 'level' => 1, + 'commission_rules' => [ + 'commission_1' => '0.00', + 'commission_2' => '0.00', + 'commission_3' => '0.00' + ] + ]); + } + $list = $this->model->sheepFilter()->select(); + + $this->success('全部等级', null, $list); + } + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['name', 'level', 'image', 'commission_rules', 'upgrade_type', 'upgrade_rules']); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + /** + * 编辑 + * + * @param $id + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['level', 'name', 'image', 'commission_rules', 'upgrade_type', 'upgrade_rules']); + + $result = Db::transaction(function () use ($id, $params) { + + $this->svalidate($params); + + $data = $this->model->where('level', $id)->find(); + if (!$data) { + $this->error(__('No Results were found')); + } + + return $data->save($params); + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->get($id); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('等级详情', null, $detail); + } + + /** + * 删除 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $result = Db::transaction(function () use ($id) { + return $this->model->where('level', $id)->delete(); + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + // 选择分销商等级 + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $data = $this->model->sheepFilter()->field('level, name, image, commission_rules')->select(); + $this->success('选择等级', null, $data); + } +} diff --git a/application/shopro/commission/Log.php b/application/shopro/commission/Log.php new file mode 100644 index 0000000..fadb92c --- /dev/null +++ b/application/shopro/commission/Log.php @@ -0,0 +1,50 @@ +model = new LogModel(); + } + + + /** + * 查看 + * + * @return Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $logs = $this->model->sheepFilter()->with(['agent'])->paginate($this->request->param('list_rows', 10)); + + $morphs = [ + 'user' => UserModel::class, + 'admin' => AdminModel::class, + 'system' => AdminModel::class + ]; + $logs = morph_to($logs, $morphs, ['oper_type', 'oper_id']); + $logs = $logs->toArray(); + + // 格式化操作人信息 + foreach ($logs['data'] as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + + $this->success('获取成功', null, $logs); + } +} diff --git a/application/shopro/commission/Order.php b/application/shopro/commission/Order.php new file mode 100644 index 0000000..2024917 --- /dev/null +++ b/application/shopro/commission/Order.php @@ -0,0 +1,175 @@ +model = new OrderModel(); + } + + /** + * 查看 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->with(['buyer', 'agent', 'order', 'rewards.agent', 'order_item'])->paginate($this->request->param('list_rows', 10)); + $list = $list->toArray(); + + // 统计数据 + $count = [ + 'total' => $list['total'], + 'total_amount' => 0, + 'total_commission' => 0, + 'total_commission_cancel' => 0, + 'total_commission_accounted' => 0, + 'total_commission_back' => 0, + 'total_commission_pending' => 0 + ]; + + $orders = $this->model->sheepFilter()->with(['rewards'])->select(); + collection($orders)->each(function ($order) use (&$count) { + $count['total_amount'] += $order['amount']; + foreach ($order['rewards'] as $reward) { + $count['total_commission'] += $reward['commission']; + switch ($reward['status']) { + case RewardModel::COMMISSION_REWARD_STATUS_ACCOUNTED: + $count['total_commission_accounted'] += $reward['commission']; + break; + case RewardModel::COMMISSION_REWARD_STATUS_BACK: + $count['total_commission_back'] += $reward['commission']; + break; + case RewardModel::COMMISSION_REWARD_STATUS_PENDING: + $count['total_commission_pending'] += $reward['commission']; + break; + case RewardModel::COMMISSION_REWARD_STATUS_CANCEL: + $count['total_commission_cancel'] += $reward['commission']; + break; + } + } + }); + + $this->success('', null, [ + 'list' => $list, + 'count' => $count + ]); + } + + /** + * 结算佣金 + * + * @return Response + */ + public function confirm() + { + $params = $this->request->only(['commission_reward_id', 'commission_order_id']); + + try { + Db::transaction(function () use ($params) { + $rewardService = new RewardService('admin'); + if (isset($params['commission_reward_id'])) { + return $rewardService->runCommissionReward($params['commission_reward_id']); + } elseif (isset($params['commission_order_id'])) { + return $rewardService->runCommissionRewardByOrder($params['commission_order_id']); + } + }); + } catch (HttpResponseException $e) { + $data = $e->getResponse()->getData(); + $message = $data ? ($data['msg'] ?? '') : $e->getMessage(); + $this->error($message); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + + $this->success('操作成功'); + } + + /** + * 取消结算 + * + * @return Response + */ + public function cancel() + { + $params = $this->request->only(['commission_reward_id', 'commission_order_id']); + + try { + + Db::transaction(function () use ($params) { + $rewardService = new RewardService('admin'); + if (isset($params['commission_reward_id'])) { + return $rewardService->cancelCommissionReward($params['commission_reward_id']); + } elseif (isset($params['commission_order_id'])) { + return $rewardService->backCommissionRewardByOrder($params['commission_order_id']); + } + }); + } catch (HttpResponseException $e) { + $data = $e->getResponse()->getData(); + $message = $data ? ($data['msg'] ?? '') : $e->getMessage(); + $this->error($message); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + $this->success('操作成功'); + } + + /** + * 退回已结算佣金 + */ + public function back() + { + $params = $this->request->only(['commission_reward_id', 'commission_order_id', 'deduct_order_money']); + + try { + Db::transaction(function () use ($params) { + $rewardService = new RewardService('admin'); + if (isset($params['commission_reward_id'])) { + return $rewardService->backCommissionReward($params['commission_reward_id']); + } elseif (isset($params['commission_order_id'])) { + return $rewardService->backCommissionRewardByOrder($params['commission_order_id'], $params['deduct_order_money']); + } + }); + } catch (HttpResponseException $e) { + $data = $e->getResponse()->getData(); + $message = $data ? ($data['msg'] ?? '') : $e->getMessage(); + $this->error($message); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + $this->success('操作成功'); + } + + /** + * 修改待结算佣金 + */ + public function edit($id = null) + { + $params = $this->request->only(['commission_reward_id', 'commission']); + $reward = RewardModel::get($params['commission_reward_id']); + if (!$reward) { + $this->error(__('No Results were found')); + } + + $reward->commission = $params['commission']; + $result = $reward->save(); + if ($result) { + $this->success('操作成功'); + } + $this->error('操作失败'); + } +} diff --git a/application/shopro/commission/Reward.php b/application/shopro/commission/Reward.php new file mode 100644 index 0000000..8ec966d --- /dev/null +++ b/application/shopro/commission/Reward.php @@ -0,0 +1,31 @@ +model = new RewardModel(); + } + + /** + * 查看 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->with(['buyer', 'agent', 'order', 'order_item'])->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $list); + } +} diff --git a/application/shopro/data/Area.php b/application/shopro/data/Area.php new file mode 100644 index 0000000..bd7fb5e --- /dev/null +++ b/application/shopro/data/Area.php @@ -0,0 +1,170 @@ +model = new \app\admin\model\shopro\data\Area; + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->with('children.children')->where('pid', 0)->select(); // 查询全部 + $this->success('', null, $list); + } + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $level = $this->request->param('level', 'all'); + $with = ['children.children']; + if ($level == 'city') { + $with = ['children']; + } else if ($level == 'province') { + $with = []; + } + + $list = $this->model->with($with)->where('pid', 0)->field('id, name, pid, level')->select(); // 查询全部 + $this->success('', null, $list); + } + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['id', 'pid', 'name']); + + $params['level'] = 'province'; + if (isset($params['pid']) && $params['pid']) { + $parent = $this->model->find($params['pid']); + if (!$parent) { + $this->error(__('No Results were found')); + } + if ($parent['level'] == 'province') { + $params['level'] = 'city'; + } else if ($parent['level'] == 'city') { + $params['level'] = 'district'; + } + } + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($old_id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['id', 'pid', 'name']); + $params['level'] = 'province'; + if (isset($params['pid']) && $params['pid']) { + $parent = $this->model->find($params['pid']); + if (!$parent) { + $this->error(__('No Results were found')); + } + if ($parent['level'] == 'province') { + $params['level'] = 'city'; + } else if ($parent['level'] == 'city') { + $params['level'] = 'district'; + } + } + + $area = $this->model->where('id', 'in', $old_id)->find(); + if (!$area) { + $this->error(__('No Results were found')); + } + + $area->save($params); + $this->success('更新成功', null, $area); + } + + /** + * 删除 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $area = $this->model->where('id', 'in', $id)->find(); + if (!$area) { + $this->error(__('No Results were found')); + } + + $children = $this->model->where('pid', $id)->count(); + if ($children) { + $this->error('请先删除下级地市'); + } + + $area->delete(); + $this->success('删除成功'); + } +} diff --git a/application/shopro/data/Express.php b/application/shopro/data/Express.php new file mode 100644 index 0000000..fa2f465 --- /dev/null +++ b/application/shopro/data/Express.php @@ -0,0 +1,156 @@ +model = new \app\admin\model\shopro\data\Express; + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->request('list_rows', 10)); + $this->success('', null, $list); + } + + + /** + * 选择快递公司 + * + * @return void + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('', null, $list); + } + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['name', 'code', 'weigh']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['name', 'code', 'weigh']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/shopro/data/FakeUser.php b/application/shopro/data/FakeUser.php new file mode 100644 index 0000000..e286a8e --- /dev/null +++ b/application/shopro/data/FakeUser.php @@ -0,0 +1,215 @@ +model = new \app\admin\model\shopro\data\FakeUser; + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('', null, $list); + } + + + /** + * 随机获取一个虚拟用户 + * + * @return void + */ + public function getRandom() + { + $userFake = $this->model->orderRaw('rand()')->find(); + + $userFake ? $this->success('获取成功', null, $userFake) : $this->error('请在数据维护中添加虚拟用户'); + } + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('', null, $list); + } + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['username', 'nickname', 'mobile', 'password', 'avatar', 'gender', 'email']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + /** + * 随机生成用户 + */ + public function random() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + set_time_limit(0); + $num = $this->request->param('num', 1); + + for ($i = 0; $i < $num; $i++) { + $style = [ + 'adventurer', + 'big-smile', + 'bottts', + 'croodles', + 'croodles-neutral', + 'identicon', + 'micah' + ]; + + $username = gen_random_str(mt_rand(6, 15), mt_rand(0, 1)); + $avatarSources = [ + // "https://joeschmoe.io/api/v1/random", // 生成的是 svg ,无法使用 + "https://avatars.dicebear.com/api/%s/" . $username . ".png", + "https://api.multiavatar.com/" . $username . ".png" + ]; + + $avatar_url = $avatarSources[array_rand($avatarSources)]; + $avatar_url = sprintf($avatar_url, $style[array_rand($style)]); + + $store_path = '/uploads/' . date('Ymd') . '/' . md5(time() . mt_rand(1000, 9999)) . '.png'; // 存数据库路径 + $save_path = ROOT_PATH . 'public' . $store_path; // 服务器绝对路径 + image_resize_save($avatar_url, $save_path); + + $fakeUser = new \app\admin\model\shopro\data\FakeUser(); + $fakeUser->username = $username; + $fakeUser->nickname = $username; + $fakeUser->mobile = random_mobile(); + $fakeUser->password = gen_random_str(); + $fakeUser->avatar = cdnurl($store_path, true); // 这里存了完整路径 + $fakeUser->gender = mt_rand(0, 1); + $fakeUser->email = random_email($fakeUser->mobile); + $fakeUser->save(); + } + + $this->success('生成成功'); + } + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['username', 'nickname', 'mobile', 'password', 'avatar', 'gender', 'email']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/shopro/data/Faq.php b/application/shopro/data/Faq.php new file mode 100644 index 0000000..c23378d --- /dev/null +++ b/application/shopro/data/Faq.php @@ -0,0 +1,138 @@ +model = new \app\admin\model\shopro\data\Faq; + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->request('list_rows', 10)); + $this->success('', null, $list); + } + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['title', 'content', 'status']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['title', 'content', 'status']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/shopro/data/Page.php b/application/shopro/data/Page.php new file mode 100644 index 0000000..1609edc --- /dev/null +++ b/application/shopro/data/Page.php @@ -0,0 +1,156 @@ +model = new \app\admin\model\shopro\data\Page; + + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->request('list_rows', 10)); + $this->success('', null, $list); + } + + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->group('group')->with(['children'])->field('group')->select(); + $this->success('', null, $list); + } + + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['name', 'path', 'group']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['name', 'path', 'group']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + +} diff --git a/application/shopro/data/Richtext.php b/application/shopro/data/Richtext.php new file mode 100644 index 0000000..645ebad --- /dev/null +++ b/application/shopro/data/Richtext.php @@ -0,0 +1,170 @@ +model = new \app\admin\model\shopro\data\Richtext; + + } + + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->paginate($this->request->request('list_rows', 10)); + $this->success('', null, $list); + } + + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type', 'page'); + + $list = $this->model->sheepFilter(); + + if ($type == 'select') { + // 普通结果 + $list = $list->select(); + } elseif ($type == 'find') { + $list = $list->find(); + } else { + // 分页结果 + $list = $list->paginate($this->request->request('list_rows', 10)); + } + + $this->success('', null, $list); + } + + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['title', 'content']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only(['title', 'content']); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + +} diff --git a/application/shopro/decorate/Designer.php b/application/shopro/decorate/Designer.php new file mode 100644 index 0000000..4c8d8bd --- /dev/null +++ b/application/shopro/decorate/Designer.php @@ -0,0 +1,47 @@ +request->param(); + Db::transaction(function () use ($params) { + $decorate = DecorateModel::create([ + 'name' => $params['name'], + 'memo' => $params['memo'], + 'platform' => $params['platform'], + 'status' => 'disabled' + ]); + $pageList = []; + $params['page'] = json_decode($params['page'], true); + foreach ($params['page'] as $page) { + array_push($pageList, [ + 'decorate_id' => $decorate->id, + 'image' => $page['image'] ?? '', + 'type' => $page['type'], + 'page' => json_encode($page['page'], JSON_UNESCAPED_UNICODE) + ]); + } + + PageModel::insertAll($pageList); + $this->downLoadDecorateImages($params['imageList']); + }); + $this->success(); + } + + private function downLoadDecorateImages($imageList) + { + \think\Queue::push('\addons\shopro\job\Designer@redeposit', ['imageList' => $imageList], 'shopro'); + } +} diff --git a/application/shopro/decorate/Page.php b/application/shopro/decorate/Page.php new file mode 100644 index 0000000..60c20dd --- /dev/null +++ b/application/shopro/decorate/Page.php @@ -0,0 +1,76 @@ +model = new PageModel(); + } + /** + * 页面列表 + */ + public function index() + { + return $this->view->fetch(); + } + + /** + * 页面详情 + * + * @param int $id + */ + public function detail($id) + { + $type = $this->request->param('type'); + $page = $this->model->where(['decorate_id' => $id, 'type' => $type])->find(); + $this->success('获取成功', null, $page); + } + + /** + * 编辑 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + $type = $this->request->param('type'); + $page = $this->request->param('page'); + $image = $this->request->param('image'); + + $pageRow = PageModel::where(['decorate_id' => $id, 'type' => $type])->find(); + + operate_filter(); + if ($pageRow) { + $pageRow->page = $page; + $pageRow->image = $image; + $pageRow->save(); + } else { + PageModel::create([ + 'decorate_id' => $id, + 'type' => $type, + 'page' => $page, + 'image' => $image + ]); + } + $this->success('保存成功'); + } + + /** + * 预览 + */ + public function preview() + { + return $this->view->fetch(); + } +} diff --git a/application/shopro/decorate/Template.php b/application/shopro/decorate/Template.php new file mode 100644 index 0000000..229301b --- /dev/null +++ b/application/shopro/decorate/Template.php @@ -0,0 +1,286 @@ +model = new DecorateModel(); + } + + /** + * 查看 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->sheepFilter()->with(['page' => function ($query) { + return $query->where('type', 'in', ['home', 'user', 'diypage'])->field('decorate_id, type, image'); + }])->select(); + $this->success('获取成功', null, $list); + } + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['type', 'name', 'memo', 'platform']); + $this->svalidate($params, '.add'); + + $this->model->save($params); + + $this->success('保存成功', null, $this->model); + } + + + + /** + * 详情 + * + * @param $id + */ + public function detail($id) + { + $detail = $this->model->where('id', $id)->find(); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + operate_filter(); + $params = $this->request->only([ + 'type', 'name', 'memo', 'platform' + ]); + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $this->svalidate($params); + $count += $item->save($params); + } + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + + /** + * 复制 + * + * @param $id + */ + public function copy($id) + { + $template = $this->model->where('id', $id)->find(); + if (!$template) { + $this->error(__('No Results were found')); + } + + Db::transaction(function () use ($template) { + $params = [ + 'name' => '复制 ' . $template->name, + 'type' => $template->type, + 'memo' => $template->memo, + 'platform' => $template->platform, + 'status' => 'disabled' + ]; + $newTemplate = $this->model->create($params); + + $pageList = PageModel::where('decorate_id', $template->id)->select(); + + $newPageList = []; + foreach ($pageList as $page) { + $newPageList[] = [ + 'decorate_id' => $newTemplate->id, + 'type' => $page['type'], + 'page' => json_encode($page['page']), + 'image' => $page['image'] + ]; + } + if (count($newPageList) > 0) { + PageModel::insertAll($newPageList); + } + }); + + $this->success('复制成功'); + } + + /** + * 启用/禁用 + * + * @param $id + */ + public function status($id) + { + $status = $this->request->param('status'); + $template = $this->model->where('id', $id)->find(); + if (!$template) { + $this->error(__('No Results were found')); + } + + operate_filter(); + $template->status = $status; + $template->save(); + $this->success('操作成功', null, $template); + } + + + /** + * 删除(支持批量) + * + * @param $id + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + operate_filter(); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $templates = $this->model->onlyTrashed()->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $templates); + } + + + /** + * 还原(支持批量) + * + * @param $id + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $items = $this->model->onlyTrashed()->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + $count += $item->restore(); + } + + return $count; + }); + + if ($result) { + $this->success('还原成功', null, $result); + } else { + $this->error(__('No rows were updated')); + } + } + + + + /** + * 销毁(支持批量) + * + * @param $id + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + if ($id !== 'all') { + $items = $this->model->onlyTrashed()->whereIn('id', $id)->select(); + } else { + $items = $this->model->onlyTrashed()->select(); + } + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + PageModel::where('decorate_id', $item->id)->delete(); + + // 删除商品 + $count += $item->delete(true); + } + return $count; + }); + + if ($result) { + $this->success('销毁成功', null, $result); + } + $this->error('销毁失败'); + } + + + /** + * 选择自定义页面 + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list = $this->model->where('type', 'diypage')->with(['page' => function ($query) { + return $query->field('decorate_id, type, image'); + }])->select(); + + $this->success('获取成功', null, $list); + } +} diff --git a/application/shopro/dispatch/Dispatch.php b/application/shopro/dispatch/Dispatch.php new file mode 100644 index 0000000..056b557 --- /dev/null +++ b/application/shopro/dispatch/Dispatch.php @@ -0,0 +1,232 @@ +model = new DispatchModel; + $this->expressModel = new DispatchExpressModel; + $this->autosendModel = new DispatchAutosendModel; + + $this->dispatch_type = $this->request->param('type', 'express'); + } + + + + /** + * 配送方式列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $dispatchs = $this->model->sheepFilter()->with([$this->dispatch_type])->where('type', $this->dispatch_type)->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $dispatchs); + } + + + + + /** + * 添加配送方式 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'name', 'type', 'status', 'express', 'autosend' + ]); + $this->svalidate($params, ".add"); + $data = $params[$this->dispatch_type] ?? []; + unset($params['express'], $params['autosend']); + + if ($this->dispatch_type == 'express') { + // 验证 express + foreach ($data as $key => $express) { + $this->svalidate($express, '.express'); + } + } else if ($this->dispatch_type == 'autosend') { + // 验证 autosend + $this->svalidate($data, '.autosend'); + } + + Db::transaction(function () use ($params, $data) { + unset($params['createtime'], $params['updatetime'], $params['id']); // 删除时间 + $this->model->allowField(true)->save($params); + + if ($this->dispatch_type == 'express') { + foreach ($data as $key => $express) { + $express['dispatch_id'] = $this->model->id; + $expressModel = new DispatchExpressModel(); + unset($express['createtime'], $express['updatetime'], $express['id']); // 删除时间 + $expressModel->allowField(true)->save($express); + } + } else if ($this->dispatch_type == 'autosend') { + $data['dispatch_id'] = $this->model->id; + $autosendModel = new DispatchAutosendModel(); + unset($data['createtime'], $data['updatetime'], $data['id']); // 删除时间 + $autosendModel->allowField(true)->save($data); + } + }); + + $this->success('保存成功'); + } + + + /** + * 配送方式详情 + * + * @param $id + */ + public function detail($id) + { + $dispatch = $this->model->with([$this->dispatch_type])->where('type', $this->dispatch_type)->where('id', $id)->find(); + if (!$dispatch) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $dispatch); + } + + + + /** + * 修改配送方式 + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'name', 'type', 'status', 'express', 'autosend' + ]); + $this->svalidate($params); + $data = $params[$this->dispatch_type] ?? []; + unset($params['express'], $params['autosend']); + + if ($this->dispatch_type == 'express') { + // 验证 express + foreach ($data as $key => $express) { + $this->svalidate($express, '.express'); + } + } else if ($this->dispatch_type == 'autosend') { + // 验证 autosend + $this->svalidate($data, '.autosend'); + } + + $id = explode(',', $id); + $lists = $this->model->whereIn('id', $id)->select(); + Db::transaction(function () use ($lists, $params, $data) { + foreach ($lists as $dispatch) { + $dispatch->allowField(true)->save($params); + if ($data) { + if ($this->dispatch_type == 'express') { + // 修改,不是只更新状态 + $expressIds = array_column($data, 'id'); + DispatchExpressModel::where('dispatch_id', $dispatch->id)->whereNotIn('id', $expressIds)->delete(); // 先删除被删除的记录 + foreach ($data as $key => $express) { + if (isset($express['id']) && $express['id']) { + $expressModel = $this->expressModel->find($express['id']); + } else { + $expressModel = new DispatchExpressModel(); + $express['dispatch_id'] = $dispatch->id; + } + $express['weigh'] = count($data) - $key; // 权重 + unset($express['createtime'], $express['updatetime']); + $expressModel && $expressModel->allowField(true)->save($express); + } + } else if ($this->dispatch_type == 'autosend') { + if (isset($data['id']) && $data['id']) { + $autosendModel = $this->autosendModel->find($data['id']); + } else { + $autosendModel = new DispatchAutosendModel(); + $data['dispatch_id'] = $dispatch->id; + } + + unset($data['createtime'], $data['updatetime']); // 删除时间 + $autosendModel->allowField(true)->save($data); + } + } + } + }); + $this->success('更新成功'); + } + + + + /** + * 删除配送方式 + * + * @param string $id 要删除的配送方式列表 + * @return void + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + $id = explode(',', $id); + $list = $this->model->with([$this->dispatch_type])->where('type', $this->dispatch_type)->where('id', 'in', $id)->select(); + Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + if ($this->dispatch_type == 'express') { + // 删除相关的 express 数据 + foreach ($item->express as $express) { + $express->delete(); + } + } else if ($this->dispatch_type == 'autosend') { + $item->{$this->dispatch_type}->delete(); + } + + $count += $item->delete(); + } + + return $count; + }); + + $this->success('删除成功'); + } + + + /** + * 获取所有配送模板 + */ + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $dispatchs = $this->model->sheepFilter()->field('id, name, type, status')->normal()->where('type', $this->dispatch_type)->select(); + + $this->success('获取成功', null, $dispatchs); + } +} diff --git a/application/shopro/goods/Comment.php b/application/shopro/goods/Comment.php new file mode 100644 index 0000000..996efaa --- /dev/null +++ b/application/shopro/goods/Comment.php @@ -0,0 +1,247 @@ +model = new CommentModel; + } + + /** + * 商品评价列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $comments = $this->model->sheepFilter()->with(['goods' => function ($query) { + $query->field('id,image,title'); + }, 'order' => function ($query) { + $query->removeOption('soft_delete'); + }, 'order_item'])->paginate($this->request->param('list_rows', 10)); + + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'fake_user' => \app\admin\model\shopro\data\FakeUser::class + ]; + $comments = morph_to($comments, $morphs, ['user_type', 'user_id']); + + $this->success('获取成功', null, $comments); + } + + + + public function detail($id) + { + $comment = $this->model->with(['admin', 'goods' => function ($query) { + $query->field('id,image,title,price'); + }, 'order' => function ($query) { + $query->removeOption('soft_delete'); + }, 'order_item'])->where('id', $id)->find(); + + if (!$comment) { + $this->error(__('No Results were found')); + } + + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'fake_user' => \app\admin\model\shopro\data\FakeUser::class + ]; + $comments = morph_to([$comment], $morphs, ['user_type', 'user_id']); + + $this->success('获取成功', null, $comments->all()[0]); + } + + + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'goods_id', 'user_id', 'level', 'content', 'images', 'status' + ]); + $params['user_type'] = 'fake_user'; + $this->svalidate($params, ".add"); + $fakeUser = FakeUserModel::find($params['user_id']); + $params['user_nickname'] = $fakeUser ? $fakeUser->nickname : null; + $params['user_avatar'] = $fakeUser ? $fakeUser->avatar : null; + + Db::transaction(function () use ($params) { + $this->model->save($params); + }); + $this->success('保存成功'); + } + + + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'status' + ]); + + $id = explode(',', $id); + $list = $this->model->whereIn('id', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $comment) { + $comment->status = $params['status'] ?? 'hidden'; + $count += $comment->save(); + } + + return $count; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + public function reply($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'content' + ]); + $this->svalidate($params, '.reply'); + + $comment = $this->model->noReply()->find($id); + if (!$comment) { + $this->error(__('No Results were found')); + } + + $comment->reply_content = $params['content']; + $comment->reply_time = time(); + $comment->admin_id = $this->auth->id; + $comment->save(); + + $this->success('回复成功'); + } + + + /** + * 删除商品评价 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + $this->success('删除成功'); + } + + + + /** + * 评价回收站 + * + * @return void + */ + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $comments = $this->model->onlyTrashed()->sheepFilter()->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $comments); + } + + + /** + * 还原(支持批量) + * + * @param $id + * @return \think\Response + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + $items = $this->model->onlyTrashed()->where('id', 'in', $id)->select(); + Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + $count += $item->restore(); + } + + return $count; + }); + + $this->success('还原成功'); + } + + + /** + * 销毁(支持批量) + * + * @param $id + * @return \think\Response + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + if ($id !== 'all') { + $items = $this->model->onlyTrashed()->whereIn('id', $id)->select(); + } else { + $items = $this->model->onlyTrashed()->select(); + } + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $comment) { + // 删除评价 + $count += $comment->delete(true); + } + return $count; + }); + + if ($result) { + $this->success('销毁成功', null, $result); + } + $this->error('销毁失败'); + } + +} diff --git a/application/shopro/goods/Goods.php b/application/shopro/goods/Goods.php new file mode 100644 index 0000000..9f5982d --- /dev/null +++ b/application/shopro/goods/Goods.php @@ -0,0 +1,443 @@ +model = new GoodsModel; + $this->activityModel = new ActivityModel; + } + + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $goodsTableName = $this->model->getQuery()->getTable(); + + $goods = $this->model->sheepFilter()->with(['max_sku_price']); + + // 聚合库存 (包含下架的规格) + $skuSql = SkuPriceModel::field('sum(stock) as stock, goods_id as sku_goods_id')->group('goods_id')->buildSql(); + $goods = $goods->join([$skuSql => 'sp'], $goodsTableName . '.id = sp.sku_goods_id', 'left') + ->field("$goodsTableName.*, sp.stock") // ,score.* + ->paginate($this->request->param('list_rows', 10))->each(function ($goods) { + // 获取活动信息 + $goods->activities = $goods->activities; + $goods->promos = $goods->promos; + + $data_type = request()->param('data_type', ''); // 特殊 type 需要处理的数据 + if ($data_type == 'score_shop') { + $goods->is_score_shop = $goods->is_score_shop; + } + }); + + $this->success('获取成功', null, $goods); + } + + + // 获取数据类型 + public function getType() + { + $activityTypes = $this->activityModel->typeList(); + $statusList = $this->model->statusList(); + + $result = [ + 'activity_type' => $activityTypes, + 'status' => $statusList + ]; + + $data = []; + foreach ($result as $key => $list) { + $data[$key][] = ['name' => '全部', 'type' => 'all']; + + foreach ($list as $k => $v) { + $data[$key][] = [ + 'name' => $v, + 'type' => $k + ]; + } + } + + $this->success('获取成功', null, $data); + } + + + + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'type', 'title', 'subtitle', 'category_ids', 'image', 'images', 'params', + 'original_price', 'price', 'is_sku', 'limit_type', 'limit_num', 'sales_show_type', + 'stock_show_type', 'show_sales', 'service_ids', 'dispatch_type', 'dispatch_id', 'is_offline', 'status', 'weigh', + ]); // likes, views, sales, + $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params, ".add"); + if (!$params['is_sku']) { + // 校验单规格属性 + $sku_params = $this->request->only(['stock', 'stock_warning', 'sn', 'weight', 'cost_price', 'original_price', 'price']); + $this->svalidate($sku_params, '.sku_params'); + } + + $data = Db::transaction(function () use ($params) { + $this->model->save($params); + + $this->editSku($this->model, 'add'); + }); + $this->success('保存成功', null, $data); + } + + + + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $goods = $this->model->where('id', $id)->find(); + if (!$goods) { + $this->error(__('No Results were found')); + } + $goods->category_ids_arr = $goods->category_ids_arr; + + if ($goods->is_sku) { + $goods->skus = $goods->skus; + $goods->sku_prices = $goods->sku_prices; + } else { + // 将单规格的部分数据直接放到 row 上 + $goodsSkuPrice = SkuPriceModel::where('goods_id', $id)->order('id', 'asc')->find(); + + $goods->stock = $goodsSkuPrice->stock; + $goods->sn = $goodsSkuPrice->sn; + $goods->weight = $goodsSkuPrice->weight; + $goods->stock_warning = $goodsSkuPrice->stock_warning; + $goods->cost_price = $goodsSkuPrice->cost_price; + } + + $content = $goods['content']; + $goods = $goods->toArray(); + $goods['content'] = $content; + $this->success('保存成功', null, $goods); + } + + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'type', 'title', 'subtitle', 'image', 'images', 'params', + 'original_price', 'price', 'is_sku', 'limit_type', 'limit_num', 'sales_show_type', + 'stock_show_type', 'show_sales', 'service_ids', 'dispatch_type', 'dispatch_id', 'is_offline', 'status', 'weigh', + ]); // likes, views, sales, + $this->request->has('content') && $params['content'] = $this->request->param('content', '', null); // content 不经过全局过滤 + $this->svalidate($params); + $params['category_ids'] = $this->request->param('category_ids', ''); // 分类不判空 + if (isset($params['is_sku']) && !$params['is_sku']) { + // 校验单规格属性 + $sku_params = $this->request->only(['stock_warning', 'sn', 'weight', 'cost_price', 'original_price', 'price']); + $this->svalidate($sku_params, 'sku_params'); + } + + $id = explode(',', $id); + + $items = $this->model->whereIn('id', $id)->select(); + Db::transaction(function () use ($items, $params) { + foreach ($items as $goods) { + $goods->save($params); + + if (isset($params['is_sku'])) { + // 编辑商品(如果没有 is_sku 就是批量编辑上下架等) + $this->editSku($goods, 'edit'); + } + } + }); + + $this->success('更新成功', null); + } + + + public function addStock($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $goods = $this->model->where('id', $id)->find(); + if (!$goods) { + $this->error(__('No Results were found')); + } + if ($goods->is_sku) { + // 多规格 + $skuPrices = $this->request->post('sku_prices/a', []); + foreach ($skuPrices as $skuPrice) { + if (isset($skuPrice['add_stock']) && $skuPrice['add_stock'] != 0 && $skuPrice['id']) { + $skuPriceModel = SkuPriceModel::where('goods_id', $id)->order('id', 'asc')->find($skuPrice['id']); + if ($skuPriceModel) { + Db::transaction(function () use ($skuPriceModel, $skuPrice) { + $this->addStockToSkuPrice($skuPriceModel, $skuPrice['add_stock'], 'goods'); + }); + } + } + } + } else { + $add_stock = $this->request->param('add_stock', 0); + $skuPriceModel = SkuPriceModel::where('goods_id', $id)->order('id', 'asc')->find(); + + if ($skuPriceModel) { + Db::transaction(function () use ($skuPriceModel, $add_stock) { + $this->addStockToSkuPrice($skuPriceModel, $add_stock, 'goods'); + }); + } + } + + $this->success('补货成功'); + } + + + + public function select() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $type = $this->request->param('type', 'page'); + $goodsTableName = $this->model->getQuery()->getTable(); + + $goods = $this->model->sheepFilter()->with(['max_sku_price']); + + // 聚合库存 (包含下架的规格) + $skuSql = SkuPriceModel::field('sum(stock) as stock, goods_id as sku_goods_id')->group('goods_id')->buildSql(); + $goods = $goods->join([$skuSql => 'sp'], $goodsTableName . '.id = sp.sku_goods_id', 'left') + ->field("$goodsTableName.*, sp.stock"); // ,score.* + + if ($type == 'select') { + // 普通结果 + $goods = collection($goods->select()); + } else { + // 分页结果 + $goods = $goods->paginate($this->request->param('list_rows', 10)); + } + + $goods = $goods->each(function ($goods) { + // 获取活动信息 + $goods->activities = $goods->activities; + $goods->promos = $goods->promos; + + $data_type = $this->request->param('data_type', ''); // 特殊 type 需要处理的数据 + if ($data_type == 'score_shop') { + $goods->is_score_shop = $goods->is_score_shop; + } + }); + + $this->success('获取成功', null, $goods); + } + + + + /** + * 获取指定活动相关商品 + * + * @param Request $request + * @return void + */ + public function activitySelect() + { + $activity_id = $this->request->param('activity_id'); + $need_buyers = $this->request->param('need_buyers', 0); // 需要查询哪些人在参与活动 + $activity = $this->activityModel->where('id', $activity_id)->find(); + if (!$activity) { + $this->error(__('No Results were found')); + } + $goodsIds = $activity->goods_ids ? explode(',', $activity->goods_ids) : []; + + // 存一下,获取器获取指定活动的时候会用到 + foreach ($goodsIds as $id) { + session('goods-activity_id:' . $id, $activity_id); + } + $service = new GoodsService(function ($goods) use ($need_buyers) { + if ($need_buyers) { + $goods->buyers = $goods->buyers; + } + $goods->activity = $goods->activity; + return $goods; + }); + + $goods = $service->activity($activity_id)->whereIds($goodsIds)->show()->select(); + $goods = collection($goods)->toArray(); // 可以将里面的单个 model也转为数组 + foreach ($goods as &$gd) { + unset($gd['new_sku_prices'], $gd['activity']); + } + $this->success('获取成功', null, $goods); + } + + + + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + // 删除相关库存预警记录 + StockWarningModel::destroy(function ($query) use ($item) { + $query->where('goods_id', $item->id); + }); + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + + public function recyclebin() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $goods = $this->model->onlyTrashed()->sheepFilter()->paginate($this->request->param('list_rows', 10)); + $this->success('获取成功', null, $goods); + } + + + /** + * 还原(支持批量) + * + * @param $id + * @return \think\Response + */ + public function restore($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $items = $this->model->onlyTrashed()->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $item) { + $count += $item->restore(); + } + + return $count; + }); + + if ($result) { + $this->success('还原成功', null, $result); + } else { + $this->error(__('No rows were updated')); + } + } + + + /** + * 销毁(支持批量) + * + * @param $id + * @return \think\Response + */ + public function destroy($id = null) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + if ($id !== 'all') { + $items = $this->model->onlyTrashed()->whereIn('id', $id)->select(); + } else { + $items = $this->model->onlyTrashed()->select(); + } + $result = Db::transaction(function () use ($items) { + $count = 0; + foreach ($items as $goods) { + // 删除商品相关的规格,规格记录 + SkuModel::where('goods_id', $goods->id)->delete(); + SkuPriceModel::where('goods_id', $goods->id)->delete(); + + // 删除商品 + $count += $goods->delete(true); + } + return $count; + }); + + if ($result) { + $this->success('销毁成功', null, $result); + } + $this->error('销毁失败'); + } +} diff --git a/application/shopro/goods/Service.php b/application/shopro/goods/Service.php new file mode 100644 index 0000000..75cc72f --- /dev/null +++ b/application/shopro/goods/Service.php @@ -0,0 +1,157 @@ +model = new ServiceModel; + } + + + /** + * 服务保障列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $services = $this->model->sheepFilter()->paginate(request()->param('list_rows', 10)); + + $this->success('获取成功', null, $services); + } + + + + + /** + * 添加服务保障 + * + * @return \think\Response + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only([ + 'name', 'image', 'description' + ]); + $this->svalidate($params, ".add"); + + Db::transaction(function () use ($params) { + $this->model->save($params); + }); + $this->success('保存成功'); + } + + + /** + * 服务保障详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $service = $this->model->where('id', $id)->find(); + + if (!$service) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $service); + } + + + + /** + * 修改服务保障 + * + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $params = $this->request->only([ + 'name', 'image', 'description' + ]); + $this->svalidate($params, ".edit"); + + $id = explode(',', $id); + $list = $this->model->whereIn('id', $id)->select(); + $result = Db::transaction(function () use ($list, $params) { + $count = 0; + foreach ($list as $item) { + $params['id'] = $item->id; + $count += $item->save($params); + } + return $count; + }); + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败,未改变任何记录'); + } + } + + + + /** + * 删除服务标签 + * + * @param string $id 要删除的服务保障列表 + * @return void + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + $this->success('删除成功'); + } + + + /** + * 获取所有服务列表 + * + * @return \think\Response + */ + public function select() + { + $services = $this->model->field('id, name')->select(); + + $this->success('获取成功', null, $services); + } +} diff --git a/application/shopro/goods/SkuPrice.php b/application/shopro/goods/SkuPrice.php new file mode 100644 index 0000000..8f10124 --- /dev/null +++ b/application/shopro/goods/SkuPrice.php @@ -0,0 +1,57 @@ +model = new SkuPriceModel; + } + + /** + * skuPrices列表 + * + * @return \think\Response + */ + public function index() + { + $goods_id = $this->request->param('goods_id'); + $skuPrices = $this->model->where('goods_id', $goods_id)->select(); + + $this->success('获取成功', null, $skuPrices); + } + + + + /** + * skuPrices编辑 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + $params = $this->request->only([ + 'status', + ]); + + $id = explode(',', $id); + $items = $this->model->whereIn('id', $id)->select(); + Db::transaction(function () use ($items, $params) { + foreach ($items as $skuPrice) { + $skuPrice->save($params); + } + }); + + $this->success('更新成功'); + } +} diff --git a/application/shopro/goods/StockLog.php b/application/shopro/goods/StockLog.php new file mode 100644 index 0000000..d3c4bd8 --- /dev/null +++ b/application/shopro/goods/StockLog.php @@ -0,0 +1,48 @@ +model = new StockLogModel; + } + + + /** + * 库存补货列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $skuPriceTableName = (new SkuPriceModel())->getQuery()->getTable(); + $stockLogs = $this->model->sheepFilter()->alias('g')->with(['goods' => function ($query) { + $query->removeOption('soft_delete'); + }, 'oper']) + ->join($skuPriceTableName . ' sp', 'g.goods_sku_price_id = sp.id', 'left') + ->field('g.*,sp.stock as total_stock') + ->paginate($this->request->param('list_rows', 10))->toArray(); + // 解析操作人信息 + foreach ($stockLogs['data'] as &$log) { + $log['oper'] = Operator::info('admin', $log['oper'] ?? null); + } + $this->success('获取成功', null, $stockLogs); + } +} diff --git a/application/shopro/goods/StockWarning.php b/application/shopro/goods/StockWarning.php new file mode 100644 index 0000000..54d22d0 --- /dev/null +++ b/application/shopro/goods/StockWarning.php @@ -0,0 +1,120 @@ +model = new StockWarningModel; + } + + + /** + * 库存预警列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $skuPriceTableName = (new SkuPriceModel())->getQuery()->getTable(); + $stockWarnings = $this->model->sheepFilter()->alias('g')->with(['goods' => function ($query) { + $query->removeOption('soft_delete'); + }]) + ->join($skuPriceTableName . ' sp', 'g.goods_sku_price_id = sp.id', 'left') + ->field('g.*,sp.stock') + ->paginate($this->request->param('list_rows', 10)); + + $warning_total = $this->model->sheepFilter(false, function ($filters) { + $filters['stock_type'] = 'no_enough'; + return $filters; + })->alias('g') + ->join($skuPriceTableName . ' sp', 'g.goods_sku_price_id = sp.id', 'left') + ->field('g.*,sp.stock') + ->count(); + $over_total = $this->model->sheepFilter(false, function ($filters) { + $filters['stock_type'] = 'over'; + return $filters; + })->alias('g') + ->join($skuPriceTableName . ' sp', 'g.goods_sku_price_id = sp.id', 'left') + ->field('g.*,sp.stock') + ->count(); + + $result = [ + 'rows' => $stockWarnings, + 'warning_total' => $warning_total, + 'over_total' => $over_total, + ]; + + $this->success('获取成功', null, $result); + } + + + + + /** + * 补货 + * + * @param [type] $ids + * @param [type] $stock + * @return void + */ + public function addStock ($id) { + if ($this->request->isAjax()) { + $params = $this->request->only(['stock']); + $this->svalidate($params, ".add"); + + $stockWarning = $this->model->with(['sku_price'])->where('id', $id)->find(); + if (!$stockWarning) { + $this->error(__('No Results were found')); + } + if (!$stockWarning->sku_price) { + $this->error('库存规格不存在'); + } + + Db::transaction(function () use ($stockWarning, $params) { + // 补货 + $this->addStockToSkuPrice($stockWarning->sku_price, $params['stock'], 'stock_warning'); + }); + + $this->success('补货成功'); + } + + return $this->view->fetch(); + + } + + + public function recyclebin() + { + if ($this->request->isAjax()) { + $skuPriceTableName = (new SkuPriceModel())->getQuery()->getTable(); + $stockWarnings = $this->model->onlyTrashed()->sheepFilter()->alias('g')->with(['goods' => function ($query) { + $query->removeOption('soft_delete'); + }]) + ->join($skuPriceTableName . ' sp', 'g.goods_sku_price_id = sp.id', 'left') + ->field('g.*,sp.stock') + ->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $stockWarnings); + } + + return $this->view->fetch(); + } +} diff --git a/application/shopro/notification/Config.php b/application/shopro/notification/Config.php new file mode 100644 index 0000000..52bf0e6 --- /dev/null +++ b/application/shopro/notification/Config.php @@ -0,0 +1,211 @@ +model = new ConfigModel; + } + + + + /** + * 消息通知配置 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $receiver_type = $this->request->param('receiver_type'); + + $notifications = $this->getNotificationsByReceiverType($receiver_type); + + $groupConfigs = $this->getGroupConfigs(); + foreach ($notifications as $key => &$notification) { + $currentConfigs = $groupConfigs[$notification['event']] ?? []; + foreach ($notification['channels'] as $channel) { + $notification['configs'][$channel] = [ + 'status' => isset($currentConfigs[$channel]) ? $currentConfigs[$channel]['status'] : 'disabled', + 'send_num' => isset($currentConfigs[$channel]) ? $currentConfigs[$channel]['send_num'] : 0, + ]; + } + } + + $this->success('获取成功', null, $notifications); + } + + + + public function detail() + { + $event = $this->request->param('event'); + $channel = $this->request->param('channel'); + if (!$event || !$channel) { + error_stop('参数错误'); + } + + $notification = $this->getNotificationByEvent($event); + + $notification = $this->formatNotification($notification, $event, $channel); + + $this->success('获取成功', null, $notification); + } + + + // 编辑配置 + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $event = $this->request->param('event'); + $channel = $this->request->param('channel'); + if ($channel == 'Email') { + $content = $this->request->param('content', ''); + } else { + $content = $this->request->param('content/a', []); + } + $type = $this->request->param('type', 'default'); + if (!$event || !$channel) { + error_stop('参数错误'); + } + + $config = $this->model->where('event', $event)->where('channel', $channel)->find(); + + if (!$config) { + $config = $this->model; + $config->event = $event; + $config->channel = $channel; + } + + if (in_array($channel, ['WechatOfficialAccount', 'WechatMiniProgram']) && $type == 'default') { + // 自动组装微信默认模板 + $content['fields'] = $this->formatWechatTemplateFields($event, $channel, $content['fields']); + } + + $config->type = $type; + $config->content = $content; + $config->save(); + + $this->success('设置成功'); + } + + + + // 配置状态 + public function setStatus($event, $channel) + { + $event = $this->request->param('event'); + $channel = $this->request->param('channel'); + $status = $this->request->param('status', 'disabled'); + + if (!$event || !$channel) { + $this->error('参数错误'); + } + + $config = $this->model->where('event', $event)->where('channel', $channel)->find(); + if (!$config) { + $config = $this->model; + $config->event = $event; + $config->channel = $channel; + $config->type = 'default'; + } + $config->status = $status; + $config->save(); + + $this->success('设置成功'); + } + + + /** + * 自动获取微信模板 id + */ + public function getTemplateId() + { + $event = $this->request->param('event'); + $channel = $this->request->param('channel'); + $is_delete = $this->request->param('is_delete', 0); + $template_id = $this->request->param('template_id', ''); + if (!$event || !$channel) { + error_stop('参数错误'); + } + + $notification = $this->getNotificationByEvent($event); + + $template = $notification['template'][$channel] ?? null; + if (!$template) { + $this->error('模板不存在'); + } + + // 请求微信接口 + switch ($channel) { + case 'WechatMiniProgram': // 小程序订阅消息 + $requestParams['tid'] = $template['tid']; + $requestParams['kid'] = $template['kid']; + $requestParams['sceneDesc'] = $template['scene_desc']; + if (!$requestParams['tid'] || !$requestParams['kid']) { + $this->error('缺少模板参数'); + } + $wechat = Wechat::miniProgram()->subscribe_message; + $delete_method = 'deleteTemplate'; + $result_key = 'priTmplId'; + break; + case 'WechatOfficialAccount': // 公众号模板消息 + $requestParams['template_id'] = $template['temp_no']; + if (!$requestParams['template_id']) { + $this->error('缺少模板参数,获取失败'); + } + $wechat = Wechat::officialAccount()->template_message; // 微信管理 + $result_key = 'template_id'; + $delete_method = 'deletePrivateTemplate'; + break; + case 'WechatOfficialAccountBizsend': // 公众号订阅消息(待补充) + $requestParams['tid'] = $template['tid']; + $requestParams['kid'] = $template['kid']; + if (!$requestParams['tid'] || !$requestParams['kid']) { + $this->error('缺少模板参数,获取失败'); + } + $wechat = Wechat::officialAccount()->subscribe_message; // 微信管理 + $result_key = 'priTmplId'; + $delete_method = 'deleteTemplate'; + break; + default: + $this->error('当前发送渠道不能获取模板'); + break; + } + + $result = $wechat->addTemplate(...array_values($requestParams)); + + if ($result['errcode'] != 0) { + $this->error('获取失败: errcode:' . $result['errcode'] . '; errmsg:' . $result['errmsg']); + } else { + if ($is_delete) { + // 删除传入的老模板 + if ($template_id) { + $deleteResult = $wechat->{$delete_method}($template_id); + } + // 删除数据库的老模板 + $config = $this->model->where('event', $event)->where('channel', $channel)->find(); + $template_id = $config ? ($config->content['template_id'] ?? null) : null; + if ($template_id) { + $deleteResult = $wechat->{$delete_method}($template_id); + } + } + } + + $this->success('获取成功', null, ($result[$result_key] ?? null)); + } +} \ No newline at end of file diff --git a/application/shopro/notification/Notification.php b/application/shopro/notification/Notification.php new file mode 100644 index 0000000..c4b6774 --- /dev/null +++ b/application/shopro/notification/Notification.php @@ -0,0 +1,128 @@ +model = new NotificationModel; + } + + + /** + * 获取管理员的消息列表 + * + * @return \think\Response + */ + public function index() + { + $admin = auth_admin(); + $admin = Admin::where('id', $admin['id'])->find(); + + $notifiable_type = $admin->getNotifiableType(); + $notifications = NotificationModel::sheepFilter(false) + ->where('notifiable_type', $notifiable_type) + ->where('notifiable_id', $admin['id']) + ->order('createtime', 'desc') + ->paginate($this->request->param('list_rows', 10)); + + $this->success('消息列表', null, $notifications); + } + + + /** + * 指定消息标记已读 + * + * @param string $id + * @return void + */ + public function read($id) + { + $admin = auth_admin(); + $admin = Admin::where('id', $admin['id'])->find(); + + $notifiable_type = $admin->getNotifiableType(); + $notification = NotificationModel::sheepFilter() + ->where('notifiable_type', $notifiable_type) + ->where('notifiable_id', $admin['id']) + ->where('id', $id) + ->find(); + + if (!$notification) { + $this->error(__('No Results were found')); + } + + $notification->read_time = time(); + $notification->save(); + + $this->success('已读成功', null, $notification); + } + + + /** + * 删除已读消息 + * + * @return void + */ + public function delete() + { + $admin = auth_admin(); + $admin = Admin::where('id', $admin['id'])->find(); + + // 将已读的消息全部删除 + $notifiable_type = $admin->getNotifiableType(); + NotificationModel::sheepFilter() + ->where('notifiable_type', $notifiable_type) + ->where('notifiable_id', $admin['id']) + ->whereNotNull('read_time') + ->delete(); + + $this->success('删除成功'); + } + + + + + /** + * 消息类别,以及未读消息数 + * + * @return void + */ + public function notificationType() + { + $admin = auth_admin(); + + $notificationType = NotificationModel::$notificationType; + + $newType = []; + foreach ($notificationType as $type => $name) { + // 未读消息数 + $unread_num = NotificationModel::where('notifiable_type', 'admin')->where('notifiable_id', $admin['id']) + ->notificationType($type) + ->where('read_time', null) + ->order('createtime', 'desc')->count(); + + $newType[] = [ + 'label' => $name, + 'value' => $type, + 'unread_num' => $unread_num + ]; + } + + $result = [ + 'unread_num' => array_sum(array_column($newType, 'unread_num')), + 'notification_type' => $newType + ]; + + $this->success('消息类型', null, $result); + } +} \ No newline at end of file diff --git a/application/shopro/notification/traits/Notification.php b/application/shopro/notification/traits/Notification.php new file mode 100644 index 0000000..7ab3f1c --- /dev/null +++ b/application/shopro/notification/traits/Notification.php @@ -0,0 +1,183 @@ +getNotifications(); + + $receiverNotifications = []; + foreach ($notifications as $notification) { + if (in_array($notification['receiver_type'], $receiverType)) { + $receiverNotifications[] = $notification; + } + } + + return $receiverNotifications; + } + + + + /** + * 根据事件类型获取消息 + * + * @param string $event + * @return void + */ + protected function getNotificationByEvent($event) + { + $notifications = $this->getNotifications(); + + $notifications = array_column($notifications, null, 'event'); + return $notifications[$event] ?? null; + } + + + + /** + * 按照事件类型获取配置分组 + * + * @param string $event + * @return array + */ + protected function getGroupConfigs($event = null) + { + // 获取所有配置 + $configs = $this->model->select(); + $newConfigs = []; + foreach ($configs as $config) { + $newConfigs[$config['event']][$config['channel']] = $config; + } + + return $event ? ($newConfigs[$event] ?? []) : $newConfigs; + } + + + + /** + * 获取所有消息类型 + * + * @return array + */ + protected function getNotifications() + { + $types = []; + foreach ($this->notificationTypes as $key => $class_name) { + $class = new $class_name(); + $currentFields = $class->returnField; + $currentFields['event'] = $class->event; + $currentFields['receiver_type'] = $class->receiver_type; + $currentFields['template'] = $class->template; + + $types[] = $currentFields; + } + + return $types; + } + + + + /** + * 格式化详情返回结果 + * + * @param array $notification + * @param string $event + * @param string $channel + * @return array + */ + protected function formatNotification($notification, $event, $channel) + { + $currentConfigs = $this->getGroupConfigs($event); + $currentConfig = $currentConfigs[$channel] ?? null; + + if (in_array($channel, ['WechatOfficialAccount', 'WechatMiniProgram', 'WechatOfficialAccountBizsend'])) { + $currentTemplate = $notification['template'][$channel] ?? []; + unset($notification['template']); + $notification['wechat'] = $currentTemplate; + } + + $notification['type'] = $currentConfig['type'] ?? 'default'; + $content = $currentConfig['content'] ?? null; + if (!is_array($content)) { + $notification['content_text'] = $content; + } + if ($content && is_array($content)) { + $contentFields = []; + if (isset($content['fields']) && $content['fields']) { // 判断数组是否存在 fields 设置 + $contentFields = array_column($content['fields'], null, 'field'); + } + + $tempFields = array_column($notification['fields'], null, 'field'); + $configField = array_merge($tempFields, $contentFields); + + $content['fields'] = array_values($configField); + $notification['content'] = $content; + } else { + $notification['content'] = [ + 'template_id' => '', + 'fields' => $notification['fields'] + ]; + } + + unset($notification['fields']); + + return $notification; + } + + + + /** + * 格式化微信公众号,小程序默认模板时 自动配置 模板字段 + * + * @return void + */ + protected function formatWechatTemplateFields($event, $channel, $fields) + { + $notification = $this->getNotificationByEvent($event); + + $channelFields = $notification['template'][$channel]['fields'] ?? []; + $channelFields = array_column($channelFields, null, 'field'); + + foreach ($fields as $key => &$field) { + $field_name = $field['field'] ?? ''; + if ($field_name && isset($channelFields[$field_name])) { + $field['template_field'] = $channelFields[$field_name]['template_field'] ?? ''; + } + } + + return $fields; + } +} diff --git a/application/shopro/order/Aftersale.php b/application/shopro/order/Aftersale.php new file mode 100644 index 0000000..aacddd0 --- /dev/null +++ b/application/shopro/order/Aftersale.php @@ -0,0 +1,338 @@ +model = new OrderAftersaleModel; + $this->orderModel = new OrderModel; + } + + /** + * 售后单列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + // 查询主表是订单表 + $orders = $this->orderModel->withTrashed()->sheepFilter()->with(['user', 'aftersales' => function ($query) { + $query->removeOption('soft_delete'); + }])->paginate(request()->param('list_rows', 10)); + + $this->success('获取成功', null, $orders); + } + + + // 获取数据类型 + public function getType() + { + $type = $this->model->typeList(); + $dispatchStatus = $this->model->dispatchStatusList(); + $aftersaleStatus = $this->model->aftersaleStatusList(); + $refundStatus = $this->model->refundStatusList(); + + $result = [ + 'type' => $type, + 'dispatch_status' => $dispatchStatus, + 'aftersale_status' => $aftersaleStatus, + 'refund_status' => $refundStatus, + ]; + + $data = []; + foreach ($result as $key => $list) { + $data[$key][] = ['name' => '全部', 'type' => 'all']; + + foreach ($list as $k => $v) { + $data[$key][] = [ + 'name' => $v, + 'type' => $k + ]; + } + } + + $this->success('获取成功', null, $data); + } + + + + /** + * 售后单详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $aftersale = $this->model->withTrashed()->with(['user', 'order' => function ($query) { + $query->removeOption('soft_delete'); + }, 'logs'])->where('id', $id)->find(); + if (!$aftersale) { + $this->error(__('No Results were found')); + } + + // 建议退款金额 + $aftersale->suggest_refund_fee = $aftersale->suggest_refund_fee; + + // 多态关联 oper + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'admin' => \app\admin\model\Admin::class, + 'system' => \app\admin\model\Admin::class, + ]; + $aftersale['logs'] = morph_to($aftersale['logs'], $morphs, ['oper_type', 'oper_id']); + + $aftersale = $aftersale->toArray(); + foreach ($aftersale['logs'] as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + $this->success('获取成功', null, $aftersale); + } + + + + /** + * 完成售后 + */ + public function completed($id) + { + $admin = $this->auth->getUserInfo(); + + $aftersale = $this->model->withTrashed()->canOper()->where('id', $id)->find(); + if (!$aftersale) { + $this->error('售后单不存在或不可完成'); + } + + $order = $this->orderModel->withTrashed()->find($aftersale->order_id); + $orderItem = OrderItemModel::find($aftersale->order_item_id); + if (!$order || !$orderItem) { + $this->error('订单或订单商品不存在'); + } + + $aftersale = Db::transaction(function () use ($aftersale, $order, $orderItem, $admin) { + $aftersale->aftersale_status = OrderAftersaleModel::AFTERSALE_STATUS_COMPLETED; // 售后完成 + $aftersale->save(); + // 增加售后单变动记录、 + OrderAftersaleLogModel::add($order, $aftersale, $admin, 'admin', [ + 'log_type' => 'completed', + 'content' => '售后订单已完成', + 'images' => [] + ]); + + $orderItem->aftersale_status = OrderItemModel::AFTERSALE_STATUS_COMPLETED; + $orderItem->save(); + OrderActionModel::add($order, $orderItem, $admin, 'admin', '管理员完成售后'); + + // 售后单完成之后 + $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; + \think\Hook::listen('order_aftersale_completed', $data); + + return $aftersale; + }); + + $this->success('操作成功', null, $aftersale); + } + + + /** + * 拒绝售后 + */ + public function refuse($id = 0) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.refuse'); + + $aftersale = $this->model->withTrashed()->canOper()->where('id', $id)->find(); + if (!$aftersale) { + $this->error('售后单不存在或不可拒绝'); + } + + $order = $this->orderModel->withTrashed()->find($aftersale->order_id); + $orderItem = OrderItemModel::find($aftersale->order_item_id); + if (!$order || !$orderItem) { + $this->error('订单或订单商品不存在'); + } + + $aftersale = Db::transaction(function () use ($aftersale, $order, $orderItem, $params, $admin) { + $aftersale->aftersale_status = OrderAftersaleModel::AFTERSALE_STATUS_REFUSE; // 售后拒绝 + $aftersale->save(); + // 增加售后单变动记录 + OrderAftersaleLogModel::add($order, $aftersale, $admin, 'admin', [ + 'log_type' => 'refuse', + 'content' => $params['refuse_msg'], + 'images' => [] + ]); + + $orderItem->aftersale_status = OrderItemModel::AFTERSALE_STATUS_REFUSE; // 拒绝售后 + $orderItem->save(); + + OrderActionModel::add($order, $orderItem, $admin, 'admin', '管理员拒绝订单售后:' . $params['refuse_msg']); + + // 售后单拒绝后 + $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; + \think\Hook::listen('order_aftersale_refuse', $data); + + return $aftersale; + }); + + $this->success('操作成功', null, $aftersale); + } + + + /** + * 同意退款 + */ + public function refund($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.refund'); + + $refund_money = round(floatval($params['refund_money']), 2); + $refund_type = $params['refund_type'] ?? 'back'; + if ($refund_money <= 0) { + $this->error('请输入正确的退款金额'); + } + + $aftersale = $this->model->withTrashed()->canOper()->where('id', $id)->find(); + if (!$aftersale) { + $this->error('售后单不存在或不可退款'); + } + + $order = $this->orderModel->withTrashed()->with('items')->find($aftersale->order_id); + if (!$order) { + $this->error('订单不存在'); + } + $items = $order->items; + $items = array_column($items, null, 'id'); + + // 当前订单已退款总金额 + $refunded_money = array_sum(array_column($items, 'refund_fee')); + // 剩余可退款金额 + $refund_surplus_money = bcsub($order->pay_fee, (string)$refunded_money, 2); + // 如果退款金额大于订单支付总金额 + if (bccomp((string)$refund_money, $refund_surplus_money, 2) === 1) { + $this->error('退款总金额不能大于实际支付金额'); + } + + $orderItem = $items[$aftersale['order_item_id']]; + + if (!$orderItem || in_array($orderItem['refund_status'], [ + OrderItemModel::REFUND_STATUS_AGREE, + OrderItemModel::REFUND_STATUS_COMPLETED, + ])) { + $this->error('订单商品已退款,不能重复退款'); + } + + $aftersale = Db::transaction(function () use ($aftersale, $order, $orderItem, $refund_money, $refund_type, $refund_surplus_money, $admin) { + $aftersale->aftersale_status = OrderAftersaleModel::AFTERSALE_STATUS_COMPLETED; // 售后同意 + $aftersale->refund_status = OrderAftersaleModel::REFUND_STATUS_AGREE; // 同意退款 + $aftersale->refund_fee = $refund_money; // 退款金额 + $aftersale->save(); + + // 增加售后单变动记录 + OrderAftersaleLogModel::add($order, $aftersale, $admin, 'admin', [ + 'log_type' => 'refund', + 'content' => '售后订单已退款', + 'images' => [] + ]); + + $orderItem->aftersale_status = OrderItemModel::AFTERSALE_STATUS_COMPLETED; + $orderItem->save(); + OrderActionModel::add($order, $orderItem, $admin, 'admin', '管理员同意售后退款'); + + // 开始退款 + $orderRefund = new OrderRefund($order); + $orderRefund->refund($orderItem, $refund_money, $admin, [ + 'refund_type' => $refund_type, + 'remark' => '管理员同意售后退款' + ]); + + $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; + \think\Hook::listen('order_aftersale_completed', $data); + + return $aftersale; + }); + + $this->success('操作成功', null, $aftersale); + } + + + /** + * 留言 + */ + public function addLog($id = 0) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.add_log'); + + $aftersale = $this->model->withTrashed()->where('id', $id)->find(); + if (!$aftersale) { + $this->error('售后单不存在'); + } + + $order = $this->orderModel->withTrashed()->with('items')->find($aftersale->order_id); + if (!$order) { + $this->error('订单不存在'); + } + + $aftersale = Db::transaction(function () use ($order, $aftersale, $params, $admin) { + if ($aftersale['aftersale_status'] == 0) { + $aftersale->aftersale_status = OrderAftersaleModel::AFTERSALE_STATUS_ING; // 售后处理中 + $aftersale->save(); + } + + // 增加售后单变动记录 + OrderAftersaleLogModel::add($order, $aftersale, $admin, 'admin', [ + 'log_type' => 'add_log', + 'content' => $params['content'], + 'images' => $params['images'] ?? [] + ]); + + return $aftersale; + }); + + $this->success('操作成功', null, $aftersale); + } +} diff --git a/application/shopro/order/Invoice.php b/application/shopro/order/Invoice.php new file mode 100644 index 0000000..750641e --- /dev/null +++ b/application/shopro/order/Invoice.php @@ -0,0 +1,62 @@ +model = new OrderInvoiceModel; + } + + /** + * 发票列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $invoices = $this->model->sheepFilter()->with(['user', 'order', 'order_items']) + ->paginate(request()->param('list_rows', 10))->each(function ($invoice) { + $invoice->order_status = $invoice->order_status; + $invoice->order_status_text = $invoice->order_status_text; + $invoice->order_fee = $invoice->order_fee; + }); + + $this->success('获取成功', null, $invoices); + } + + + + public function confirm($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->param(); + $invoice = $this->model->waiting()->whereIn('id', $id)->find(); + if (!$invoice) { + $this->error(__('No Results were found')); + } + + $invoice->download_urls = $params['download_urls'] ?? null; + $invoice->invoice_amount = $params['invoice_amount']; + $invoice->status = 'finish'; + $invoice->finish_time = time(); + $invoice->save(); + + $this->success('开具成功', null, $invoice); + } +} diff --git a/application/shopro/order/Order.php b/application/shopro/order/Order.php new file mode 100644 index 0000000..f2dafbd --- /dev/null +++ b/application/shopro/order/Order.php @@ -0,0 +1,961 @@ +model = new OrderModel; + } + + /** + * 订单列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + $exportConfig = (new \addons\shopro\library\Export())->getConfig(); + $this->assignconfig("save_type", $exportConfig['save_type'] ?? 'download'); + return $this->view->fetch(); + } + + $orders = $this->model->withTrashed()->sheepFilter()->with(['user', 'items', 'address', 'activity_orders']) + ->paginate(request()->param('list_rows', 10))->each(function ($order) { + $order->pay_types = $order->pay_types; + $order->pay_types_text = $order->pay_types_text; + $order->items = collection($order->items); + $order->items->each(function ($item) use ($order) { + // 处理每个商品的 activity_order + $item->activity_orders = new \think\Collection; + foreach ($order->activity_orders as $activityOrder) { + if ($activityOrder->goods_ids && in_array($item->goods_id, $activityOrder->goods_ids)) { + $item->activity_orders->push($activityOrder); + } + } + + return $item; + }); + })->toArray(); + + foreach ($orders['data'] as &$order) { + $order = $this->model->setOrderItemStatusByOrder($order); + } + + $result = [ + 'orders' => $orders, + ]; + + // 查询各个状态下的订单数量 + $searchStatus = $this->model->searchStatusList(); + // 所有的数量 + $result['all'] = $this->model->withTrashed()->sheepFilter(true, function ($filters) { + unset($filters['status']); + return $filters; + })->count(); + foreach ($searchStatus as $status => $text) { + $result[$status] = $this->model->withTrashed()->sheepFilter(true, function ($filters) use ($status) { + $filters['status'] = $status; + return $filters; + })->count(); + } + + $this->success('获取成功', null, $result); + } + + + // 获取数据类型 + public function getType() + { + $type = $this->model->typeList(); + $payType = (new PayModel)->payTypeList(); + $platform = $this->model->platformList(); + $classify = (new \app\admin\model\shopro\activity\Activity)->classifies(); + $activityType = $classify['activity']; + $promoType = $classify['promo']; + $applyRefundStatus = $this->model->applyRefundStatusList(); + $status = $this->model->searchStatusList(); + + $result = [ + 'type' => $type, + 'pay_type' => $payType, + 'platform' => $platform, + 'activity_type' => $activityType, + 'promo_types' => $promoType, + 'apply_refund_status' => $applyRefundStatus, + 'status' => $status + ]; + + $data = []; + foreach ($result as $key => $list) { + $data[$key][] = ['name' => '全部', 'type' => 'all']; + + foreach ($list as $k => $v) { + $data[$key][] = [ + 'name' => $v, + 'type' => $k + ]; + } + } + + $this->success('获取成功', null, $data); + } + + + + + + /** + * 订单详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + // 更新包裹信息(5分钟缓存) + (new ExpressLib)->updateOrderExpress($id); + + $order = $this->model->withTrashed()->with(['user', 'items', 'address', 'activity_orders', 'pays', 'invoice'])->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + $order->express = $order->express; + if ($order->invoice) { + $order->invoice->order_status = $order->invoice->order_status; + $order->invoice->order_status_text = $order->invoice->order_status_text; + $order->invoice->order_fee = $order->invoice->order_fee; + } + + foreach ($order->activity_orders as $activityOrder) { + // 处理每个活动中参与的商品 + $activityOrder->items = new \think\Collection(); + foreach ($order->items as $item) { + if ($activityOrder->goods_ids && in_array($item->goods_id, $activityOrder->goods_ids)) { + $activityOrder->items->push($item); + } + } + } + + foreach ($order->items as $item) { + // 处理 order_item 建议退款金额 + $item->suggest_refund_fee = $item->suggest_refund_fee; + } + + // 处理未支付订单 item status_code + $order = $order->setOrderItemStatusByOrder($order); + + $this->success('获取成功', null, $order); + } + + + /** + * 批量发货渲染模板 + * + * @return void + */ + public function batchDispatch() + { + return $this->view->fetch(); + } + + + /** + * 批量发货渲染模板 + * + * @return void + */ + public function dispatchList() + { + return $this->view->fetch(); + } + + + + /** + * 手动发货 + * + * @return void + */ + public function customDispatch() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['order_id', 'order_item_ids', 'custom_type', 'custom_content']); + $this->svalidate($params, '.custom_dispatch'); + + Db::transaction(function () use ($params) { + $service = new OrderDispatchService($params); + $service->customDispatch($params); + }); + + $this->success('发货成功'); + } + + + /** + * 发货 + * + * @description + * 支持分包裹发货 + * 支持手动发货 + * 支持上传发货单发货 + * 支持推送api运单发货 默认使用配置项 + * 支持修改发货信息 + * 支持取消发货 + * + * @remark 此处接口设计如此复杂是因为考虑到权限的问题,订单发货权限可以完成所有发货行为 + * + * @param array $action 发货行为(默认:confirm=确认发货, cancel=取消发货, change=修改运单, multiple=解析批量发货单) + * @param int $order_id 订单id + * @param array $order_item_ids 订单商品id + * @param string $method 发货方式(input=手动发货, api=推送运单, upload=上传发货单) + * @param array $sender 发货人信息 + * @param array $express 物流信息 + * + * @return \think\Response + */ + public function dispatch() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['action', 'order_id', 'order_ids', 'order_item_ids', 'method', 'sender', 'express', 'order_express_id']); + + $action = $params['action'] ?? 'confirm'; + if (!in_array($action, ['confirm', 'cancel', 'change', 'multiple'])) { + $this->error('发货参数错误'); + } + + $service = new OrderDispatchService($params); + switch ($action) { + case 'confirm': + $express = $service->confirm($params); + $this->success('发货成功', null, $express); + break; + case 'cancel': + $result = $service->cancel($params); + if ($result) { + $this->success('取消发货成功'); + } + break; + case 'change': + $express = $service->change($params); + $this->success('修改成功', null, $express); + break; + case 'multiple': + $params['file'] = $this->request->file('file'); + $result = $service->multiple($params); + $this->success('待发货列表', null, $result); + break; + } + + $this->error('操作失败'); + } + + + /** + * 获取物流快递信息 + */ + public function updateExpress($order_express_id = 0) + { + $type = $this->request->param('type'); + + // 获取包裹 + $orderExpress = OrderExpressModel::where('id', $order_express_id)->find(); + if (!$orderExpress) { + $this->error('包裹不存在'); + } + + $expressLib = new ExpressLib(); + + try { + if ($type == 'subscribe') { + // 重新订阅 + $expressLib->subscribe([ + 'express_code' => $orderExpress['express_code'], + 'express_no' => $orderExpress['express_no'] + ]); + } else { + // 手动查询 + $result = $expressLib->search([ + 'order_id' => $orderExpress['order_id'], + 'express_code' => $orderExpress['express_code'], + 'express_no' => $orderExpress['express_no'] + ], $orderExpress); + } + } catch (HttpResponseException $e) { + $data = $e->getResponse()->getData(); + $message = $data ? ($data['msg'] ?? '') : $e->getMessage(); + $this->error($message); + } catch (\Exception $e) { + $this->error(($type == 'subscribe' ? '订阅失败:' : '刷新失败:') . $e->getMessage()); + } + + $this->success(($type == 'subscribe' ? '订阅成功' : '刷新成功')); + } + + + /** + * 线下付款,确认收货 + */ + public function offlineConfirm($id) + { + $admin = $this->auth->getUserInfo(); + + $order = OrderModel::offline()->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + + $order = Db::transaction(function () use ($order, $admin) { + // 加锁读订单 + $order = $order->lock(true)->find($order->id); + $user = User::get($order->user_id); + + $payOper = new PayOper($user); + $order = $payOper->offline($order, $order->remain_pay_fee, 'order'); // 订单会变为已支付 paid + + // 触发订单支付完成事件 + $data = ['order' => $order, 'user' => $user]; + \think\Hook::listen('order_offline_paid_after', $data); + + $orderOper = new OrderOper(); + // 确认收货 + $order = $orderOper->confirm($order, [], $admin, 'admin'); + + return $order; + }); + + $this->success('收货成功', $order); + } + + + + /** + * 线下付款,拒收 + */ + public function offlineRefuse($id) + { + $admin = $this->auth->getUserInfo(); + + $order = OrderModel::offline()->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + + $order = Db::transaction(function () use ($order, $admin) { + // 加锁读订单 + $order = $order->lock(true)->find($order->id); + + // 拒收 + $orderOper = new OrderOper(); + $order = $orderOper->refuse($order, $admin, 'admin'); + + // 交易关闭 + $order = $orderOper->close($order, $admin, 'admin', '用户拒绝收货,管理员关闭订单', ['closed_type' => 'refuse']); + return $order; + }); + + $this->success('收货成功', $order); + } + + + /** + * 订单改价,当剩余应支付金额为 0 时,订单将自动支付 + * + * @param int $id + * @return void + */ + public function changeFee($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.change_fee'); + + $order = $this->model->unpaid()->where('id', $id)->find(); + if (!$order) { + $this->error('订单不可改价'); + } + + Db::transaction(function () use ($order, $admin, $params) { + $pay_fee = $params['pay_fee']; + $change_msg = $params['change_msg']; + + $payOper = new PayOper($order->user_id); + $payed_fee = $payOper->getPayedFee($order, 'order'); + + if ($pay_fee < $payed_fee) { + $this->error('改价金额不能低于订单已支付金额'); + } + + // 记录原始值 + $last_pay_fee = $order->pay_fee; // 上次pay_fee,非原始 pay_fee + $order->pay_fee = $pay_fee; + $order->save(); + + OrderActionModel::add($order, null, $admin, 'admin', "应支付金额由 ¥" . $last_pay_fee . " 改为 ¥" . $pay_fee . ",改价原因:" . $change_msg); + + // 检查订单支付状态, 改价可以直接将订单变为已支付 + $order = $payOper->checkAndPaid($order, 'order'); + }); + + $this->success('改价成功'); + } + + + + /** + * 修改收货人信息 + * + * @param Request $request + * @param integer $id + * @return void + */ + public function editConsignee($id) + { + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.edit_consignee'); + + $order = $this->model->withTrashed()->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + + Db::transaction(function () use ($admin, $order, $params) { + $orderAddress = OrderAddressModel::where('order_id', $order->id)->find(); + if (!$orderAddress) { + $this->error(__('No Results were found')); + } + $orderAddress->consignee = $params['consignee']; + $orderAddress->mobile = $params['mobile']; + $orderAddress->province_name = $params['province_name']; + $orderAddress->city_name = $params['city_name']; + $orderAddress->district_name = $params['district_name']; + $orderAddress->address = $params['address']; + $orderAddress->province_id = $params['province_id']; + $orderAddress->city_id = $params['city_id']; + $orderAddress->district_id = $params['district_id']; + $orderAddress->save(); + + OrderActionModel::add($order, null, $admin, 'admin', "修改订单收货人信息"); + }); + + $this->success('收货人信息修改成功'); + } + + + + /** + * 编辑商家备注 + * + * @param Request $request + * @param integer $id + * @return void + */ + public function editMemo($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.edit_memo'); + + $order = $this->model->withTrashed()->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + Db::transaction(function () use ($admin, $order, $params) { + $order->memo = $params['memo']; + $order->save(); + + OrderActionModel::add($order, null, $admin, 'admin', "修改卖家备注:" . $params['memo']); + }); + + $this->success('卖家备注修改成功'); + } + + + /** + * 拒绝用户全额退款申请 + * + * @param Request $request + * @param integer $id + * @return void + */ + public function applyRefundRefuse($id) + { + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + // $this->svalidate($params, '.apply_refund_refuse'); + + $order = $this->model->withTrashed()->paid()->applyRefundIng()->where('id', $id)->find(); + if (!$order) { + $this->error('订单未找到或不可拒绝申请'); + } + Db::transaction(function () use ($admin, $order, $params) { + $order->apply_refund_status = OrderModel::APPLY_REFUND_STATUS_REFUSE; + $order->save(); + + OrderActionModel::add($order, null, $admin, 'admin', "拒绝用户申请全额退款"); + }); + + $this->success('拒绝申请成功'); + } + + + /** + * 全额退款 (必须没有进行过任何退款才能使用) + * + * @param Request $request + * @param integer $id + * @return void + */ + public function fullRefund($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + $admin = Admin::where('id', $admin['id'])->find(); + + $params = $this->request->param(); + $refund_type = $params['refund_type'] ?? 'back'; + + Db::transaction(function () use ($admin, $id, $refund_type) { + $order = $this->model->paid()->where('id', $id)->lock(true)->find(); + if (!$order) { + $this->error('订单不存在或不可退款'); + } + + $orderRefund = new OrderRefund($order); + $orderRefund->fullRefund($admin, [ + 'refund_type' => $refund_type, + 'remark' => '平台主动全额退款' + ]); + }); + + $this->success('全额退款成功'); + } + + + + /** + * 订单单商品退款 + * + * @param Request $request + * @param integer $id + * @param integer $item_id + * @return void + */ + public function refund($id, $item_id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $admin = $this->auth->getUserInfo(); + + $params = $this->request->param(); + $this->svalidate($params, '.refund'); + + $refund_money = round(floatval($params['refund_money']), 2); + $refund_type = $params['refund_type'] ?? 'back'; + if ($refund_money <= 0) { + $this->error('请输入正确的退款金额'); + } + + $order = $this->model->paid()->where('id', $id)->find(); + if (!$order) { + $this->error('订单不存在或不可退款'); + } + + $item = OrderItem::where('order_id', $order->id)->where('id', $item_id)->find(); + if (!$item) { + $this->error(__('No Results were found')); + } + + if (in_array($item['refund_status'], [ + OrderItem::REFUND_STATUS_AGREE, + OrderItem::REFUND_STATUS_COMPLETED, + ])) { + $this->error('订单商品已退款,不能重复退款'); + } + + $payOper = new PayOper($order->user_id); + // 获取订单最大可退款金额(不含积分抵扣金额) + $remain_max_refund_money = $payOper->getRemainRefundMoney($order->id); + + // 如果退款金额大于订单支付总金额 + if (bccomp((string)$refund_money, $remain_max_refund_money, 2) === 1) { + $this->error('退款总金额不能大于实际支付金额'); + } + + Db::transaction(function () use ($admin, $order, $item, $refund_money, $refund_type) { + // 重新锁定读查询 orderItem + $item = OrderItem::where('order_id', $order->id)->lock(true)->where('id', $item->id)->find(); + if (!$item) { + $this->error('订单不存在或不可退款'); + } + + $orderRefund = new OrderRefund($order); + $orderRefund->refund($item, $refund_money, $admin, [ + 'refund_type' => $refund_type, + 'remark' => '平台主动退款' + ]); + }); + + $this->success('退款成功'); + } + + + /** + * 获取订单操作记录 + * + * @param integer $id + * @return void + */ + public function action($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $actions = OrderActionModel::where('order_id', $id)->order('id', 'desc')->select(); + + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'admin' => \app\admin\model\Admin::class, + 'system' => \app\admin\model\Admin::class, + ]; + $actions = morph_to($actions, $morphs, ['oper_type', 'oper_id']); + + foreach ($actions as &$action) { + $action['oper'] = Operator::info($action['oper_type'], $action['oper'] ?? null); + } + + $this->success('获取成功', null, $actions); + } + + + + + public function export() + { + $cellTitles = [ + // 订单表字段 + 'order_id' => 'Id', + 'order_sn' => '订单号', + 'type_text' => '订单类型', + 'user_nickname' => '下单用户', + 'user_mobile' => '手机号', + 'status_text' => '订单状态', + 'pay_text' => '支付状态', + 'pay_types_text' => '支付类型', + 'remark' => '用户备注', + 'memo' => '卖家备注', + 'order_amount' => '订单总金额', + 'score_amount' => '积分支付数量', + 'dispatch_amount' => '运费', + 'pay_fee' => '应付总金额', + 'real_pay_fee' => '实付总金额', + 'remain_pay_fee' => '剩余支付金额', + 'total_discount_fee' => '总优惠金额', + 'coupon_discount_fee' => '优惠券金额', + 'promo_discount_fee' => '营销优惠金额', + 'paid_time' => '支付完成时间', + 'platform_text' => '交易平台', + 'consignee_info' => '收货信息', + 'createtime' => '下单时间', + + // 订单商品表字段 + 'activity_type_text' => '活动', + 'promo_types' => '促销', + 'goods_title' => '商品名称', + 'goods_sku_text' => '商品规格', + 'goods_num' => '购买数量', + 'goods_original_price' => '商品原价', + 'goods_price' => '商品价格', + 'goods_weight' => '商品重量', + 'discount_fee' => '优惠金额', + 'goods_pay_fee' => '商品支付金额', + 'dispatch_type_text' => '发货方式', + 'dispatch_status_text' => '发货状态', + 'aftersale_refund' => '售后/退款', + 'comment_status_text' => '评价状态', + 'refund_fee' => '退款金额', + 'refund_msg' => '退款原因', + 'express_name' => '快递公司', + 'express_no' => '快递单号', + ]; + + // 数据总条数 + $total = $this->model->withTrashed()->sheepFilter()->count(); + if ($total <= 0) { + $this->error('导出数据为空'); + } + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => '订单列表', + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => true, + 'sub_start_cell' => 'activity_type_text', + 'sub_field' => 'items' + ]; + + $total_order_amount = 0; + $total_pay_fee = 0; + $total_real_pay_fee = 0; + $total_discount_fee = 0; + $total_score_amount = 0; + $result = $export->export($params, function ($pages) use (&$total_order_amount, &$total_pay_fee, &$total_real_pay_fee, &$total_discount_fee, &$total_score_amount, $total) { + $datas = $this->model->withTrashed()->sheepFilter()->with(['user', 'items' => function ($query) { + $query->with(['express']); + }, 'address']) + ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows']) + ->select(); + + $datas = collection($datas); + $datas = $datas->each(function ($order) { + $order->pay_types_text = $order->pay_types_text; + })->toArray(); + + $newDatas = []; + foreach ($datas as &$order) { + $order = $this->model->setOrderItemStatusByOrder($order); + + // 收货人信息 + $consignee_info = ''; + if ($order['address']) { + $address = $order['address']; + $consignee_info = ($address['consignee'] ? ($address['consignee'] . ':' . $address['mobile'] . '-') : '') . ($address['province_name'] . '-' . $address['city_name'] . '-' . $address['district_name']) . ' ' . $address['address']; + } + + $data = [ + 'order_id' => $order['id'], + 'order_sn' => $order['order_sn'], + 'type_text' => $order['type_text'], + 'user_nickname' => $order['user'] ? $order['user']['nickname'] : '-', + 'user_mobile' => $order['user'] ? $order['user']['mobile'] . ' ' : '-', + 'status_text' => $order['status_text'], + 'pay_text' => in_array($order['status'], [OrderModel::STATUS_PAID, OrderModel::STATUS_COMPLETED]) ? '已支付' : '未支付', + 'pay_types_text' => is_array($order['pay_types_text']) ? join(',', $order['pay_types_text']) : ($order['pay_types_text'] ?: ''), + 'remark' => $order['remark'], + 'memo' => $order['memo'], + 'order_amount' => $order['order_amount'], + 'score_amount' => $order['score_amount'], + 'dispatch_amount' => $order['dispatch_amount'], + 'pay_fee' => $order['pay_fee'], + 'real_pay_fee' => bcsub($order['pay_fee'], $order['remain_pay_fee'], 2), + 'remain_pay_fee' => $order['remain_pay_fee'], + 'total_discount_fee' => $order['total_discount_fee'], + 'coupon_discount_fee' => $order['coupon_discount_fee'], + 'promo_discount_fee' => $order['promo_discount_fee'], + 'paid_time' => $order['paid_time'], + 'platform_text' => $order['platform_text'], + 'consignee_info' => $consignee_info, + 'createtime' => $order['createtime'], + ]; + + $items = []; + foreach ($order['items'] as $item) { + $items[] = [ + 'activity_type_text' => $item['activity_type_text'], + 'promo_types_text' => is_array($item['promo_types_text']) ? join(',', $item['promo_types_text']) : ($item['promo_types_text'] ?: '-'), + 'goods_title' => $item['goods_title'], + 'goods_sku_text' => $item['goods_sku_text'], + 'goods_num' => $item['goods_num'], + 'goods_original_price' => $item['goods_original_price'], + 'goods_price' => $item['goods_price'], + 'goods_weight' => $item['goods_weight'], + 'discount_fee' => $item['discount_fee'], + 'goods_pay_fee' => $item['pay_fee'], + 'dispatch_type_text' => $item['dispatch_type_text'], + 'dispatch_status_text' => $item['dispatch_status_text'], + 'aftersale_refund' => $item['aftersale_status_text'] . '/' . $item['refund_status_text'], + 'comment_status_text' => $item['comment_status_text'], + 'refund_fee' => $item['refund_fee'], + 'refund_msg' => $item['refund_msg'], + 'express_name' => $item['express'] ? $item['express']['express_name'] : '-', + 'express_no' => $item['express'] ? $item['express']['express_no'] . ' ' : '-', + ]; + } + + $data['items'] = $items; + + $newDatas[] = $data; + } + + $total_order_amount += array_sum(array_column($newDatas, 'order_amount')); + $total_score_amount += array_sum(array_column($newDatas, 'score_amount')); + $total_pay_fee += array_sum(array_column($newDatas, 'pay_fee')); + $total_real_pay_fee += array_sum(array_column($newDatas, 'real_pay_fee')); + $total_discount_fee += array_sum(array_column($newDatas, 'discount_fee')); + + if ($pages['is_last_page']) { + $newDatas[] = ['order_id' => "订单总数:" . $total . ";订单总金额:¥" . $total_order_amount . ";优惠总金额:¥" . $total_discount_fee . ";应付总金额:¥" . $total_pay_fee . ";实付总金额:¥" . $total_real_pay_fee . ";支付总积分:" . $total_score_amount]; + } + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } + + + public function exportDelivery() + { + $cellTitles = [ + // 订单表字段 + 'order_id' => 'Id', + 'order_sn' => '订单号', + 'type_text' => '订单类型', + 'consignee_info' => '收货信息', + 'remark' => '用户备注', + 'memo' => '商家备注', + 'createtime' => '下单时间', + + // 订单商品表字段 + 'order_item_id' => '子订单Id', + 'goods_title' => '商品名称', + 'goods_sku_text' => '商品规格', + 'goods_num' => '购买数量', + // 'dispatch_fee' => '发货费用', + 'dispatch_type_text' => '发货方式', + 'dispatch_status_text' => '发货状态', + 'aftersale_refund' => '售后/退款', + 'express_no' => '快递单号', + ]; + + // 数据总条数 + $total = $this->model->sheepFilter()->count(); // nosend 加了 noApplyRefund + if ($total <= 0) { + $this->error('导出数据为空'); + } + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => '订单发货单列表', + 'cell_titles' => $cellTitles, + 'total' => $total, + 'is_sub_cell' => true, + 'sub_start_cell' => 'order_item_id', + 'sub_field' => 'items' + ]; + + $result = $export->export($params, function ($pages) use (&$total) { + // 未申请全额退款的 + $datas = $this->model->sheepFilter()->with(['user', 'items' => function ($query) { // nosend 加了 noApplyRefund + $query->with(['express']); + }, 'address']) + ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows']) + ->select(); + $datas = collection($datas)->toArray(); + + $newDatas = []; + foreach ($datas as &$order) { + $order = $this->model->setOrderItemStatusByOrder($order); + + if (in_array($order['status_code'], ['groupon_ing', 'groupon_invalid'])) { + // 拼团正在进行中,不发货 + $total--; // total 减少 1 + continue; + } + + // 收货人信息 + $consignee_info = ''; + if ($order['address']) { + $address = $order['address']; + $consignee_info = ($address['consignee'] ? ($address['consignee'] . ':' . $address['mobile'] . '-') : '') . ($address['province_name'] . '-' . $address['city_name'] . '-' . $address['district_name']) . ' ' . $address['address']; + } + + $data = [ + 'order_id' => $order['id'], + 'order_sn' => $order['order_sn'], + 'type_text' => $order['type_text'], + 'consignee_info' => $consignee_info, + 'remark' => $order['remark'], + 'memo' => $order['memo'], + 'createtime' => $order['createtime'] + ]; + + $items = []; + foreach ($order['items'] as $k => $item) { + // 未发货,并且未退款,并且未在申请售后中,并且是快递物流的 + if ( + $item['dispatch_status'] == OrderItem::DISPATCH_STATUS_NOSEND + && !in_array($item['refund_status'], [OrderItem::REFUND_STATUS_AGREE, OrderItem::REFUND_STATUS_COMPLETED]) + && $item['aftersale_status'] != OrderItem::AFTERSALE_STATUS_ING + && $item['dispatch_type'] == 'express' + ) { + $items[] = [ + 'order_item_id' => $item['id'], + 'goods_title' => strpos($item['goods_title'], '=') === 0 ? ' ' . $item['goods_title'] : $item['goods_title'], + 'goods_sku_text' => $item['goods_sku_text'], + 'goods_num' => $item['goods_num'], + // 'dispatch_fee' => $item['dispatch_fee'], + 'dispatch_type_text' => $item['dispatch_type_text'], + 'dispatch_status_text' => $item['dispatch_status_text'], + 'aftersale_refund' => $item['aftersale_status_text'] . '/' . $item['refund_status_text'], + 'express_no' => $item['express'] ? $item['express']['express_no'] . ' ' : '', + ]; + } + } + + $data['items'] = $items; + $newDatas[] = $data; + }; + + if ($pages['is_last_page']) { + $newDatas[] = ['order_id' => "订单总数(仅快递物流的待发货订单):" . $total . ";备注:订单中同一包裹请填写相同运单号"]; + } + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } +} diff --git a/application/shopro/trade/Order.php b/application/shopro/trade/Order.php new file mode 100644 index 0000000..395baca --- /dev/null +++ b/application/shopro/trade/Order.php @@ -0,0 +1,199 @@ +model = new TradeOrderModel; + } + + /** + * 订单列表 + * + * @return \think\Response + */ + public function index() + { + if (!$this->request->isAjax()) { + $exportConfig = (new \addons\shopro\library\Export())->getConfig(); + $this->assignconfig("save_type", $exportConfig['save_type'] ?? 'download'); + return $this->view->fetch(); + } + + $orders = $this->model->withTrashed()->sheepFilter()->with(['user']) + ->paginate(request()->param('list_rows', 10))->each(function ($order) { + $order->pay_type_text = $order->pay_type_text; + $order->pay_type = $order->pay_type; + })->toArray(); + + $result = [ + 'orders' => $orders, + ]; + + // 查询各个状态下的订单数量 + $searchStatus = $this->model->searchStatusList(); + // 所有的数量 + $result['all'] = $this->model->withTrashed()->sheepFilter(true, function ($filters) { + unset($filters['status']); + return $filters; + })->count(); + foreach ($searchStatus as $status => $text) { + $result[$status] = $this->model->withTrashed()->sheepFilter(true, function ($filters) use ($status) { + $filters['status'] = $status; + return $filters; + })->count(); + } + + $this->success('获取成功', null, $result); + } + + + // 获取数据类型 + public function getType() + { + $type = $this->model->typeList(); + $payType = (new PayModel)->payTypeList(); + $platform = $this->model->platformList(); + $status = $this->model->searchStatusList(); + + $result = [ + 'type' => $type, + 'pay_type' => $payType, + 'platform' => $platform, + 'status' => $status + ]; + + $data = []; + foreach ($result as $key => $list) { + $data[$key][] = ['name' => '全部', 'type' => 'all']; + + foreach ($list as $k => $v) { + $data[$key][] = [ + 'name' => $v, + 'type' => $k + ]; + } + } + + $this->success('获取成功', null, $data); + } + + + + /** + * 订单详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $order = $this->model->withTrashed()->with(['user', 'pays'])->where('id', $id)->find(); + if (!$order) { + $this->error(__('No Results were found')); + } + + $order->pay_type = $order->pay_type; + $order->pay_type_text = $order->pay_type_text; + + $this->success('获取成功', null, $order); + } + + + + public function export() + { + $cellTitles = [ + // 订单表字段 + 'order_id' => 'Id', + 'order_sn' => '订单号', + 'type_text' => '订单类型', + 'user_nickname' => '下单用户', + 'user_mobile' => '手机号', + 'status_text' => '订单状态', + 'pay_type_text' => '支付类型', + 'remark' => '用户备注', + 'order_amount' => '订单总金额', + 'pay_fee' => '应付总金额', + 'real_pay_fee' => '实付总金额', + 'remain_pay_fee' => '剩余支付金额', + 'paid_time' => '支付完成时间', + 'platform_text' => '交易平台', + 'createtime' => '下单时间', + ]; + + // 数据总条数 + $total = $this->model->withTrashed()->sheepFilter()->count(); + if ($total <= 0) { + $this->error('导出数据为空'); + } + + $export = new \addons\shopro\library\Export(); + $params = [ + 'file_name' => '交易订单列表', + 'cell_titles' => $cellTitles, + 'total' => $total, + ]; + + $total_order_amount = 0; + $total_pay_fee = 0; + $total_real_pay_fee = 0; + $result = $export->export($params, function ($pages) use (&$total_order_amount, &$total_pay_fee, &$total_real_pay_fee, $total) { + $datas = $this->model->withTrashed()->sheepFilter()->with(['user']) + ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows']) + ->select(); + + $datas = collection($datas); + $datas = $datas->each(function ($order) { + $order->pay_type_text = $order->pay_type_text; + })->toArray(); + + $newDatas = []; + foreach ($datas as &$order) { + $data = [ + 'order_id' => $order['id'], + 'order_sn' => $order['order_sn'], + 'type_text' => $order['type_text'], + 'user_nickname' => $order['user'] ? $order['user']['nickname'] : '-', + 'user_mobile' => $order['user'] ? $order['user']['mobile'] . ' ' : '-', + 'status_text' => $order['status_text'], + 'pay_type_text' => is_array($order['pay_type_text']) ? join(',', $order['pay_type_text']) : ($order['pay_type_text'] ?: ''), + 'remark' => $order['remark'], + 'order_amount' => $order['order_amount'], + 'pay_fee' => $order['pay_fee'], + 'real_pay_fee' => bcsub($order['pay_fee'], $order['remain_pay_fee'], 2), + 'remain_pay_fee' => $order['remain_pay_fee'], + 'paid_time' => $order['paid_time'], + 'platform_text' => $order['platform_text'], + 'createtime' => $order['createtime'], + ]; + $newDatas[] = $data; + } + + $total_order_amount += array_sum(array_column($newDatas, 'order_amount')); + $total_pay_fee += array_sum(array_column($newDatas, 'pay_fee')); + $total_real_pay_fee += array_sum(array_column($newDatas, 'real_pay_fee')); + + if ($pages['is_last_page']) { + $newDatas[] = ['order_id' => "订单总数:" . $total . ";订单总金额:¥" . $total_order_amount . ";应付总金额:¥" . $total_pay_fee . ";实付总金额:¥" . $total_real_pay_fee]; + } + return $newDatas; + }); + + $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result); + } +} diff --git a/application/shopro/traits/SkuPrice.php b/application/shopro/traits/SkuPrice.php new file mode 100644 index 0000000..a76f13b --- /dev/null +++ b/application/shopro/traits/SkuPrice.php @@ -0,0 +1,303 @@ +editMultSku($goods, $type); + } else { + $this->editSimSku($goods, $type); + } + } + + + /** + * 添加编辑单规格 + * + * @param GoodsModel $goods + * @param string $type + * @return void + */ + protected function editSimSku($goods, $type = 'add') + { + $params = $this->request->only([ + 'stock', 'stock_warning', 'sn', 'weight', 'cost_price', 'original_price', 'price' + ]); + + $data = [ + "goods_sku_ids" => null, + "goods_sku_text" => null, + "image" => null, + "goods_id" => $goods->id, + "stock" => $params['stock'] ?? 0, + "stock_warning" => isset($params['stock_warning']) && is_numeric($params['stock_warning']) + ? $params['stock_warning'] : null, + "sn" => $params['sn'] ?? "", + "weight" => isset($params['weight']) ? floatval($params['weight']) : 0, + "cost_price" => $params['cost_price'] ?? 0, + "original_price" => $params['original_price'] ?? 0, + "price" => $params['price'] ?? 0, + "status" => 'up' + ]; + + if ($type == 'edit') { + // 查询 + $skuPrice = SkuPriceModel::where('goods_id', $goods->id)->order('id', 'asc')->find(); + if ($skuPrice) { + // 删除多余的这个商品的其他规格以及规格项(防止多规格改为了单规格,遗留一批多余的 sku_price) + SkuPriceModel::where('goods_id', $goods->id)->where('id', '<>', $skuPrice->id)->delete(); + SkuModel::where('goods_id', $goods->id)->delete(); + } + + unset($data['stock']); // 移除库存(库存只能通过补货增加) + } + + if (!isset($skuPrice) || !$skuPrice) { + $skuPrice = new SkuPriceModel(); + } + + $skuPrice->save($data); + if ($type == 'add') { + // 增加补货记录 + $this->addStockLog($skuPrice, 0, $data['stock'], $type); + + // 检测库存预警 + $this->checkStockWarning($skuPrice, $type); + } + + } + + + /** + * 添加编辑多规格 + * + * @param GoodsModel $goods + * @param string $type + * @return void + */ + protected function editMultSku($goods, $type = 'add') + { + $params = $this->request->only([ + 'skus', 'sku_prices' + ]); + $skus = $params['skus'] ?? []; + $skuPrices = $params['sku_prices'] ?? []; + + $this->checkMultSku($skus, $skuPrices); + + // 编辑保存规格项 + $allChildrenSku = $this->saveSkus($goods, $skus, $type); + + if ($type == 'edit') { + // 编辑旧商品,先删除老的不用的 skuPrice + $oldSkuPriceIds = array_column($skuPrices, 'id'); + // 删除当前商品老的除了在基础上修改的skuPrice + SkuPriceModel::where('goods_id', $goods->id) + ->whereNotIn('id', $oldSkuPriceIds)->delete(); + + // 删除失效的库存预警记录 + $this->delNotStockWarning($oldSkuPriceIds, $goods->id); + } + + $min_key = null; // 最小加个对应的键值 + $min_price = min(array_column($skuPrices, 'price')); // 规格最小价格 + $originPrices = array_filter(array_column($skuPrices, 'original_price')); + $min_original_price = $originPrices ? min($originPrices) : 0; // 规格最小原始价格 + foreach ($skuPrices as $key => &$skuPrice) { + $skuPrice['goods_sku_ids'] = $this->getRealSkuIds($skuPrice['goods_sku_temp_ids'], $allChildrenSku); + $skuPrice['goods_id'] = $goods->id; + $skuPrice['goods_sku_text'] = is_array($skuPrice['goods_sku_text']) ? join(',', $skuPrice['goods_sku_text']) : $skuPrice['goods_sku_text']; + $skuPrice['stock_warning'] = isset($skuPrice['stock_warning']) && is_numeric($skuPrice['stock_warning']) + ? $skuPrice['stock_warning'] : null; // null 为关闭商品库存预警, 采用默认库存预警 + + // 移除无用 属性 + if ($type == 'add') { + // 添加直接移除 id + unset($skuPrice['id']); + } + unset($skuPrice['temp_id']); // 前端临时 id + unset($skuPrice['goods_sku_temp_ids']); // 前端临时规格 id,查找真实 id 用 + unset($skuPrice['createtime'], $skuPrice['updatetime']); // 删除时间 + + $skuPriceModel = new SkuPriceModel(); + if (isset($skuPrice['id']) && $skuPrice['id']) { + // type == 'edit' + unset($skuPrice['stock']); // 编辑商品 不能编辑库存,只能通过补货 + $skuPriceModel = $skuPriceModel->find($skuPrice['id']); + } + + if ($skuPriceModel) { + $skuPriceModel->allowField(true)->save($skuPrice); + + if ($type == 'add') { + // 增加补货记录 + $this->addStockLog($skuPriceModel, 0, $skuPrice['stock'], 'add'); // 记录库存记录 + + // 检测库存预警 + $this->checkStockWarning($skuPriceModel, $type); + } + } + + if (is_null($min_key) && $min_price == $skuPrice['price']) { + $min_key = $key; + } + } + + // 重新赋值最小价格和原价 + $goods->original_price = $skuPrices[$min_key]['original_price'] ?? $min_original_price; // 最小价格规格对应的原价 + $goods->price = $min_price; + $goods->save(); + } + + + /** + * 校验多规格是否填写完整 + * + * @param array $skus + * @param array $skuPrices + * @return void + */ + private function checkMultSku($skus, $skuPrices) + { + if (count($skus) < 1) { + error_stop('请填写规格列表'); + } + foreach ($skus as $key => $sku) { + if (count($sku['children']) <= 0) { + error_stop('主规格至少要有一个子规格'); + } + + // 验证子规格不能为空 + foreach ($sku['children'] as $k => $child) { + if (!isset($child['name']) || empty(trim($child['name']))) { + error_stop('子规格不能为空'); + } + } + } + + if (count($skuPrices) < 1) { + error_stop('请填写规格价格'); + } + + foreach ($skuPrices as &$price) { + // 校验多规格属性 + $this->svalidate($price, '.sku_params'); + } + } + + + /** + * 根据前端临时 temp_id 获取真实的数据库 id + * + * @param array $newGoodsSkuIds + * @param array $allChildrenSku + * @return string + */ + private function getRealSkuIds($newGoodsSkuIds, $allChildrenSku) + { + $newIdsArray = []; + foreach ($newGoodsSkuIds as $id) { + $newIdsArray[] = $allChildrenSku[$id]; + } + return join(',', $newIdsArray); + } + + + /** + * 差异更新 规格规格项(多的删除,少的添加) + * + * @param GoodsModel $goods + * @param array $skus + * @param string $type + * @return array + */ + private function saveSkus($goods, $skus, $type = 'add') + { + $allChildrenSku = []; + + if ($type == 'edit') { + // 删除无用老规格 + // 拿出需要更新的老规格 + $oldSkuIds = []; + foreach ($skus as $key => $sku) { + $oldSkuIds[] = $sku['id']; + + $childSkuIds = []; + if ($sku['children']) { + // 子项 id + $childSkuIds = array_column($sku['children'], 'id'); + } + + $oldSkuIds = array_merge($oldSkuIds, $childSkuIds); + $oldSkuIds = array_unique($oldSkuIds); + } + + // 删除老的除了在基础上修改的规格项 + SkuModel::where('goods_id', $goods->id)->whereNotIn('id', $oldSkuIds)->delete(); + } + + foreach ($skus as $s1 => &$k1) { + //添加主规格 + $current_id = $k1['id'] ?? 0; + if ($k1['id']) { + // 编辑 + SkuModel::where('id', $k1['id'])->update([ + 'name' => $k1['name'], + ]); + } else { + // 新增 + $k1Model = new SkuModel(); + $k1Model->save([ + 'name' => $k1['name'], + 'parent_id' => 0, + 'goods_id' => $goods->id + ]); + $k1['id'] = $current_id = $k1Model->id; + } + + foreach ($k1['children'] as $s2 => &$k2) { + $current_child_id = $k2['id'] ?? 0; + if ($k2['id']) { + // 编辑 + SkuModel::where('id', $k2['id'])->update([ + 'name' => $k2['name'], + ]); + } else { + // 新增 + $k2Model = new SkuModel(); + $k2Model->save([ + 'name' => $k2['name'], + 'parent_id' => $current_id, + 'goods_id' => $goods->id + ]); + $current_child_id = $k2Model->id; + } + + $allChildrenSku[$k2['temp_id']] = $current_child_id; + $k2['id'] = $current_child_id; + $k2['parent_id'] = $current_id; + } + } + + return $allChildrenSku; + } +} \ No newline at end of file diff --git a/application/shopro/user/Coupon.php b/application/shopro/user/Coupon.php new file mode 100644 index 0000000..d595971 --- /dev/null +++ b/application/shopro/user/Coupon.php @@ -0,0 +1,33 @@ +model = new UserCouponModel; + } + + + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + $coupon_id = $this->request->param('coupon_id'); + + $coupons = $this->model->sheepFilter()->with(['user', 'order']) + ->where('coupon_id', $coupon_id) + ->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $coupons); + } +} diff --git a/application/shopro/user/Group.php b/application/shopro/user/Group.php new file mode 100644 index 0000000..631972f --- /dev/null +++ b/application/shopro/user/Group.php @@ -0,0 +1,27 @@ +model = new GroupModel; + } + + + public function select() + { + $list = \app\admin\model\UserGroup::field('id,name,status')->select(); + $this->success('', null, $list); + } +} diff --git a/application/shopro/user/User.php b/application/shopro/user/User.php new file mode 100644 index 0000000..9aecd65 --- /dev/null +++ b/application/shopro/user/User.php @@ -0,0 +1,138 @@ +model = new UserModel; + } + + /** + * 用户列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $data = $this->model->sheepFilter()->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $data); + } + + /** + * 用户详情 + * + * @param $id + */ + public function detail($id) + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $user = $this->model->with(['third_oauth', 'parent_user'])->where('id', $id)->find(); + if (!$user) { + $this->error(__('No Results were found')); + } + + $this->success('获取成功', null, $user); + } + + /** + * 更新用户 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + $params = $this->request->only(['username', 'nickname', 'mobile', 'password', 'avatar', 'gender', 'email', 'status']); + + if (empty($params['password'])) unset($params['password']); + if (empty($params['username'])) unset($params['username']); + + $params['id'] = $id; + $this->svalidate($params, '.edit'); + unset($params['id']); + + $user = $this->model->where('id', $id)->find(); + $user->save($params); + + $this->success('更新成功', null, $user); + } + + /** + * 删除用户(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $id = explode(',', $id); + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } + + public function recharge() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['id', 'type', 'amount', 'memo']); + if (!in_array($params['type'], ['money', 'score'])) { + error_stop('参数错误'); + } + + $result = Db::transaction(function () use ($params) { + return WalletService::change($params['id'], $params['type'], $params['amount'], 'admin_recharge', [], $params['memo']); + }); + if ($result) { + $this->success('充值成功'); + } + $this->error('充值失败'); + } + + + /** + * 用户优惠券列表 + */ + public function coupon($id) + { + $userCoupons = UserCouponModel::sheepFilter()->with('coupon')->where('user_id', $id) + ->order('id', 'desc')->paginate($this->request->param('list_rows', 10)); + + $this->success('获取成功', null, $userCoupons); + } +} diff --git a/application/shopro/user/WalletLog.php b/application/shopro/user/WalletLog.php new file mode 100644 index 0000000..80d21dc --- /dev/null +++ b/application/shopro/user/WalletLog.php @@ -0,0 +1,90 @@ +model = new WalletLogModel; + } + + /** + * 余额记录 + */ + public function money($id) + { + $list_rows = $this->request->param('list_rows', 10); + $walletLogs = WalletLogModel::where('user_id', $id)->money()->order('id', 'desc')->paginate($list_rows); + + // 多态关联 oper + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'admin' => \app\admin\model\Admin::class, + 'system' => \app\admin\model\Admin::class, + ]; + $walletLogs = morph_to($walletLogs, $morphs, ['oper_type', 'oper_id']); + $walletLogs = $walletLogs->toArray(); + + // 解析操作人信息 + foreach ($walletLogs['data'] as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + $this->success('', null, $walletLogs); + } + + /** + * 积分记录 + */ + public function score($id) + { + $list_rows = $this->request->param('list_rows', 10); + $walletLogs = WalletLogModel::where('user_id', $id)->score()->order('id', 'desc')->paginate($list_rows); + + // 多态关联 oper + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'admin' => \app\admin\model\Admin::class, + 'system' => \app\admin\model\Admin::class, + ]; + $walletLogs = morph_to($walletLogs, $morphs, ['oper_type', 'oper_id']); + $walletLogs = $walletLogs->toArray(); + + // 解析操作人信息 + foreach ($walletLogs['data'] as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + $this->success('', null, $walletLogs); + } + + /** + * 佣金记录 + */ + public function commission($id) + { + $list_rows = $this->request->param('list_rows', 10); + $walletLogs = WalletLogModel::where('user_id', $id)->commission()->order('id', 'desc')->paginate($list_rows); + + // 多态关联 oper + $morphs = [ + 'user' => \app\admin\model\shopro\user\User::class, + 'admin' => \app\admin\model\Admin::class, + 'system' => \app\admin\model\Admin::class, + ]; + $walletLogs = morph_to($walletLogs, $morphs, ['oper_type', 'oper_id']); + $walletLogs = $walletLogs->toArray(); + + // 解析操作人信息 + foreach ($walletLogs['data'] as &$log) { + $log['oper'] = Operator::info($log['oper_type'], $log['oper'] ?? null); + } + $this->success('', null, $walletLogs); + } +} diff --git a/application/shopro/wechat/Admin.php b/application/shopro/wechat/Admin.php new file mode 100644 index 0000000..b2bd004 --- /dev/null +++ b/application/shopro/wechat/Admin.php @@ -0,0 +1,139 @@ +wechat = Wechat::officialAccountManage(); + + } + + // 获取公众号二维码 + public function getQrcode() + { + + $event = $this->request->param('event'); + + if (!in_array($event, ['bind'])) { + $this->error('参数错误'); + } + + $adminId = $this->auth->id; + $thirdOauth = ThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'admin_id' => $adminId + ])->find(); + + if ($thirdOauth) { + error_stop('已绑定微信账号', -2, $thirdOauth); + } + + // 二维码和缓存过期时间 + $expireTime = 1 * 60; + + // 事件唯一标识 + $eventId = Random::uuid(); + + $cacheKey = "wechatAdmin.{$event}.{$eventId}"; + + cache($cacheKey, ['id' => 0], $expireTime); + + try { + $result = $this->wechat->qrcode->temporary($cacheKey, $expireTime); + $qrcode = $this->wechat->qrcode->url($result['ticket']); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + + $this->success('', null, [ + 'url' => $qrcode, + 'eventId' => $eventId + ]); + } + + // 检查扫码结果 + public function checkScan() + { + $event = $this->request->param('event'); + $eventId = $this->request->param('eventId'); + + if (!in_array($event, ['bind'])) { + error_stop('参数错误'); + } + + $cacheKey = "wechatAdmin.{$event}.{$eventId}"; + + $cacheValue = cache($cacheKey); + + if (empty($cacheValue)) { + error_stop('二维码已过期, 请重新扫码'); + } + + if ($cacheValue['id'] === 0) { + error_stop('等待扫码', -1); + } + + if ($cacheValue['id'] !== 0) { + switch ($event) { + case 'bind': + $adminId = $this->auth->id; + + $thirdOauth = ThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'openid' => $cacheValue['id'], + ])->find(); + + if ($thirdOauth && $thirdOauth->admin_id !== 0) { + error_stop('该微信账号已被绑定'); + } + + if (!$thirdOauth) { + $thirdOauth = ThirdOauth::create([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'openid' => $cacheValue['id'], + 'admin_id' => $adminId + ]); + } else { + $thirdOauth->admin_id = $adminId; + $thirdOauth->save(); + } + break; + } + $this->success(); + } + } + + // 解绑 + public function unbind() + { + $adminId = $this->auth->id; + + $thirdOauth = ThirdOauth::where([ + 'provider' => 'wechat', + 'platform' => 'admin', + 'admin_id' => $adminId + ])->find(); + + if ($thirdOauth) { + $thirdOauth->admin_id = 0; + $thirdOauth->save(); + } + $this->success(); + } +} diff --git a/application/shopro/wechat/Config.php b/application/shopro/wechat/Config.php new file mode 100644 index 0000000..d49d100 --- /dev/null +++ b/application/shopro/wechat/Config.php @@ -0,0 +1,30 @@ +request->isAjax()) { + return $this->view->fetch(); + } + + if ('GET' === $this->request->method()) { + + $configs = ShoproConfig::getConfigs('wechat.officialAccount', false); + $configs['server_url'] = request()->domain() . '/addons/shopro/wechat.serve'; + } elseif ('POST' === $this->request->method()) { + + $configs = ShoproConfig::setConfigs('wechat.officialAccount', $this->request->param()); + } + + $this->success('操作成功', null, $configs); + } +} diff --git a/application/shopro/wechat/Material.php b/application/shopro/wechat/Material.php new file mode 100644 index 0000000..3905e5b --- /dev/null +++ b/application/shopro/wechat/Material.php @@ -0,0 +1,178 @@ +wechat = Wechat::officialAccountManage(); + $this->model = new \app\admin\model\shopro\wechat\Material; + } + + /** + * 素材列表 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $list_rows = intval($this->request->param('list_rows', 10)); + $page = intval($this->request->param('page', 1)); + $type = $this->request->param('type', 'news'); + $offset = intval(($page - 1) * $list_rows); + + if (in_array($type, ['text', 'link'])) { + $data = $this->model->sheepFilter()->where('type', $type)->paginate(request()->param('list_rows', 10)); + } else { + // 使用微信远程素材列表 + try { + $res = $this->wechat->material->list($type, $offset, $list_rows); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + + $data = [ + 'current_page' => $page, + 'data' => $res['item'], + 'last_page' => intval(ceil($res['total_count'] / $list_rows)), + 'per_page' => $list_rows, + 'total' => intval($res['total_count']), + ]; + } + + $this->success('', null, $data); + } + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->get($id); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + /** + * 添加 + * + * @return \think\Response + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['type', 'content']); + + if ($params['type'] === 'text') { + $params['content'] = urldecode($params['content']); + } + + Db::transaction(function () use ($params) { + $this->model->save($params); + }); + $this->success('保存成功'); + } + + /** + * 编辑 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $material = $this->model->get($id); + $params = $this->request->only(['type', 'content']); + if ($params['type'] === 'text') { + $params['content'] = urldecode($params['content']); + } + + Db::transaction(function () use ($params, $material) { + $material->save($params); + }); + + $this->success('更新成功'); + } + + + /** + * 删除 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + $is_real = $this->request->param('is_real', 0); + Db::transaction(function () use ($id, $is_real) { + $menu = $this->model->get($id); + if ($is_real) { + $menu->force()->delete(); + } else { + $menu->delete(); + } + }); + + $this->success('删除成功'); + } + + /** + * 菜单列表 + * + * @return Response + */ + public function select() + { + $list_rows = intval($this->request->param('list_rows', 10)); + $page = intval($this->request->param('page', 1)); + $type = $this->request->param('type', 'news'); + $offset = intval(($page - 1) * $list_rows); + + if (in_array($type, ['text', 'link'])) { + $data = $this->model->where('type', $type)->order('id desc')->paginate(request()->param('list_rows', 10)); + } else { + // 使用微信远程素材列表 + try { + $res = $this->wechat->material->list($type, $offset, $list_rows); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + + $data = [ + 'current_page' => $page, + 'data' => $res['item'], + 'last_page' => intval(ceil($res['total_count'] / $list_rows)), + 'per_page' => $list_rows, + 'total' => intval($res['total_count']), + ]; + } + $this->success('获取成功', null, $data); + } +} diff --git a/application/shopro/wechat/Menu.php b/application/shopro/wechat/Menu.php new file mode 100644 index 0000000..7a7bbab --- /dev/null +++ b/application/shopro/wechat/Menu.php @@ -0,0 +1,223 @@ +wechat = Wechat::officialAccountManage(); + $this->model = new \app\admin\model\shopro\wechat\Menu; + } + + /** + * 公众号配置 + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $current = $this->getCurrentMenu(); + $data = $this->model->sheepFilter()->paginate(request()->param('list_rows', 10)); + + $this->success('操作成功', null, ['current' => $current, 'list' => $data]); + } + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->get($id); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + /** + * 添加菜单 + * + * @param int $publish 发布状态:0=不发布,1=直接发布 + * @return \think\Response + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $publish = $this->request->param('publish', 0); + $params = $this->request->only(['name', 'rules']); + // 参数校验 + $this->svalidate($params, '.add'); + + Db::transaction(function () use ($params, $publish) { + $menu = $this->model->save($params); + if ($menu && $publish) { + $this->publishMenu($this->model->id); + } + return $menu; + }); + + $this->success('保存成功'); + } + + /** + * 编辑 + * + * @param $id + * @return \think\Response + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + + $menu = $this->model->get($id); + + $publish = $this->request->param('publish', 0); + $params = $this->request->only(['name', 'rules']); + + // 参数校验 + $this->svalidate($params); + + $menu = Db::transaction(function () use ($params, $menu, $publish) { + $menu->save($params); + if ($publish) { + $this->publishMenu($menu->id); + } + return $menu; + }); + + $this->success('更新成功'); + } + + + /** + * 删除 + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + Db::transaction(function () use ($id) { + $menu = $this->model->get($id); + $menu->delete(); + }); + + $this->success('删除成功'); + } + + + /** + * 发布菜单 + * + * @param $id + * @return \think\Response + */ + public function publish($id) + { + Db::transaction(function () use ($id) { + return $this->publishMenu($id); + }); + + $this->success('发布成功'); + } + + /** + * 复制菜单 + * + * @return Response + */ + public function copy($id = 0) + { + if ($id == 0) { + $data = [ + 'name' => '复制 当前菜单', + 'rules' => $this->getCurrentMenu(), + ]; + } else { + $menu = $this->model->get($id); + $data = [ + 'name' => '复制 ' . $menu->name, + 'rules' => $menu->rules + ]; + } + + $menu = $this->model->save($data); + $this->success('复制成功'); + } + + // 发布菜单 + private function publishMenu($id) + { + $menu = $this->model->get($id); + + if ($this->setCurrentMenu($menu->rules)) { + $this->model->where('id', '<>', $menu->id)->update(['status' => 0]); + + return $menu->save([ + 'status' => 1, + 'publishtime' => time() + ]); + } + return false; + } + + + // 获取当前菜单 + private function getCurrentMenu() + { + try { + $currentMenu = $this->wechat->menu->current(); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + if (isset($currentMenu['selfmenu_info']['button'])) { + $buttons = $currentMenu['selfmenu_info']['button']; + foreach ($buttons as &$button) { + if (isset($button['sub_button'])) { + $button['sub_button'] = $button['sub_button']['list']; + } + } + return $buttons; + } else { + return []; + } + } + + // 设置菜单 + private function setCurrentMenu($rules) + { + try { + $result = $this->wechat->menu->create($rules); + } catch (\Exception $e) { + $this->error($e->getMessage()); + } + + if (isset($result['errcode']) && $result['errcode'] === 0) { + return true; + } else { + $this->error($result['errmsg'] ?? '发布失败'); + } + } +} diff --git a/application/shopro/wechat/Reply.php b/application/shopro/wechat/Reply.php new file mode 100644 index 0000000..65856b8 --- /dev/null +++ b/application/shopro/wechat/Reply.php @@ -0,0 +1,143 @@ +model = new \app\admin\model\shopro\wechat\Reply; + } + + /** + * 查看 + * + * @return string|Json + * @throws \think\Exception + * @throws DbException + */ + public function index() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $group = $this->request->param('group', 'keywords'); + $data = $this->model->sheepFilter()->where('group', $group)->select(); + $this->success('操作成功', null, $data); + } + + /** + * 添加 + */ + public function add() + { + if (!$this->request->isAjax()) { + return $this->view->fetch(); + } + + $params = $this->request->only(['group', 'keywords', 'type', 'status', 'content']); + + Db::transaction(function () use ($params) { + $result = $this->model->save($params); + if($result) { + $reply = $this->model; + if($reply->group !== 'keywords' && $reply->status === 'enable') { + $this->model->where('group', $reply->group)->where('id', '<>', $reply->id)->enable()->update(['status' => 'disabled']); + } + } + return $result; + }); + + $this->success('保存成功'); + } + + /** + * 详情 + * + * @param $id + * @return \think\Response + */ + public function detail($id) + { + $detail = $this->model->get($id); + if (!$detail) { + $this->error(__('No Results were found')); + } + $this->success('获取成功', null, $detail); + } + + /** + * 编辑(支持批量) + */ + public function edit($id = null) + { + if (!$this->request->isAjax()) { + return $this->view->fetch('add'); + } + $reply = $this->model->get($id); + $params = $this->request->only(['keywords', 'type', 'status', 'content']); + + // 参数校验 + // $this->svalidate($params); + + $result = Db::transaction(function () use ($params, $reply) { + $result = $reply->save($params); + if($result) { + if($reply->group !== 'keywords' && $reply->status === 'enable') { + $this->model->where('group', $reply->group)->where('id', '<>', $reply->id)->enable()->update(['status' => 'disabled']); + } + } + return $result; + }); + + if ($result) { + $this->success('更新成功', null, $result); + } else { + $this->error('更新失败'); + } + } + + /** + * 删除(支持批量) + * + * @param $id + * @return \think\Response + */ + public function delete($id) + { + if (empty($id)) { + $this->error(__('Parameter %s can not be empty', 'id')); + } + + $list = $this->model->where('id', 'in', $id)->select(); + $result = Db::transaction(function () use ($list) { + $count = 0; + foreach ($list as $item) { + $count += $item->delete(); + } + + return $count; + }); + + if ($result) { + $this->success('删除成功', null, $result); + } else { + $this->error(__('No rows were deleted')); + } + } +} diff --git a/application/tags.php b/application/tags.php new file mode 100644 index 0000000..40f076f --- /dev/null +++ b/application/tags.php @@ -0,0 +1,40 @@ + +// +---------------------------------------------------------------------- +// 应用行为扩展定义文件 +return [ + // 应用初始化 + 'app_init' => [ + 'app\\common\\behavior\\Common', + ], + // 应用开始 + 'app_begin' => [], + // 应用调度 + 'app_dispatch' => [ + 'app\\common\\behavior\\Common', + ], + // 模块初始化 + 'module_init' => [ + 'app\\common\\behavior\\Common', + ], + // 插件开始 + 'addon_begin' => [ + 'app\\common\\behavior\\Common', + ], + // 操作开始执行 + 'action_begin' => [], + // 视图内容过滤 + 'view_filter' => [], + // 日志写入 + 'log_write' => [], + // 应用结束 + 'app_end' => [], +]; diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..7850980 --- /dev/null +++ b/bower.json @@ -0,0 +1,34 @@ +{ + "name": "fastadmin", + "description": "the fastest admin framework", + "main": "", + "license": "Apache2.0", + "homepage": "https://www.fastadmin.net", + "private": true, + "dependencies": { + "jquery": "^2.1.4", + "bootstrap": "^3.3.7", + "font-awesome": "^4.6.1", + "bootstrap-table": "fastadmin-bootstraptable#~1.11.5", + "jstree": "~3.3.2", + "moment": "~2.29.0", + "toastr": "~2.1.3", + "eonasdan-bootstrap-datetimepicker": "~4.17.43", + "bootstrap-select": "~1.13.18", + "require-css": "~0.1.8", + "tableExport.jquery.plugin": "~1.10.3", + "jquery-slimscroll": "~1.3.8", + "jquery.cookie": "~1.4.1", + "Sortable": "~1.10.0", + "nice-validator": "~1.1.1", + "art-template": "~3.1.3", + "bootstrap-daterangepicker": "~2.1.25", + "fastadmin-citypicker": "~1.3.1", + "fastadmin-cxselect": "~1.4.0", + "fastadmin-dragsort": "~1.0.0", + "fastadmin-addtabs": "~1.0.5", + "fastadmin-selectpage": "~1.0.6", + "fastadmin-layer": "~3.5.1", + "bootstrap-slider": "*" + } +} diff --git a/cert/fpvone.key b/cert/fpvone.key new file mode 100644 index 0000000..28dacf8 --- /dev/null +++ b/cert/fpvone.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtWX1xypaq6iKyi4xu2MMOciAZAynE8KTKbnxnM6vx4hvCvLU +0k7R2jpLuB08pUovKd7qjRHd1dfe40aIr5qf2xqURh+kr85yjrcG2g4/qd3OLf24 +z20DHjhf4nnvBWxRaK9lp6ewWNtQQO31rCCOkaM1UlhKphJ5RfGgHzciNbSpW9KJ +EGxHHw3C0XSC0rMnDvt5+fAisfhFxf2Qw+UuvHtuRaJPsoexY6eFgXPJMU48bAA8 +hPEZhM3LSUUZ8qvLH7ljfeTh4uqaypnSGC1QXsAY/2QhSvBNnfVB0vuuoqWGRg7k +25l2YLL9o9h+pN4TeuhqZdL8nFRIAoqVRZnXPwIDAQABAoIBACterk2pBbam/E/s +jX1k6AZktrW1BLlHuxGt4GOCUyqcpxfyCQojSC8p4vKpItfODcqv/louHGDFVQf3 +1DRP/spO54aAiEp0TJVskxnNcFE3s9TxJZ+KS8WM6vfl+UzNogUqcl6MTDBidySf +AC7gNdYCNHx0mZxtLEuZwpPUBbzbBtXKMYXlzHU4BceRaBCcnXC9xldoJNtSh5f3 ++9OlGgqxxm8ut/DCOOByFqimOa7bdO3lv4FxZVLQ3snJNrgHTw8pCSusMFYmlfOV +HtOMHvMJrL93X1DtxnrPuVC8CjER97rXp6Qca3aoBoER9uoY+4P2sgPAkLSQ3R1n +kxKdD3kCgYEA2/V3v7H+5scuO/HgLItJayB7rTz/e5cIz3tfuDzTji7ke/R2cMb3 +quSItk4rJwSRSu4bkarRB9WBq0Y4OYfp4UN5yyLe/guRRrlQvsdHLWgv99x6JgTP +h+5BL42xeej76SkZsQaqv9UXqKYc2dmOHd1+1KSbXA+iNbUnMfeuXV0CgYEA0x8B +k3fklr+cjrS4yweq20MYBPyeG+GuRl0u9pRJlwPY1efvlOKKlz2B7iYVox76q/fD +enSvLWgomKxxTEr2miO97nH41fb211+LEwLSEL5h/pRIBs27Wwnm8iBYB3NsRJB2 +DWRtlaOuVJjz3O42RB6s2U4XH5d4KJU7QQYxoUsCgYABxoR17qjhEgCEiVpG4cTw +8W0uI8zoIXr3ucY3BX6rqiM8AOgn1uNO5NNZrV3ZzcaidTORxHA1gPqv+Dwh8r/n +ipTkF9726/77NCm7sH4Zlrq0ZHoz7lwCe+DY0mr5UfEem69SZ3xfWgf724NmGsRz +50LL0pxSbOnhRxoO5DHrHQKBgCw0P8Y6wjf2IreWi2KnVlTWdX8Fza0U0cKV3DFw +1v+xpIMVLwrDbDVCgaP1gW5IHEBJfjpzmAIv3uAoqwhIYd3gQfJdFrbmIMTzklEG +tCkhU69UKMiT5vim2B42O3hD6JVqke036+H7BZNc+yJ1FhOpQJO3UAwjUNtec6un +uliFAoGBAKg0Eeq+svjxQwQjKYl55gDyGE9NJfyN1oQ+wg2wMsVSSEfPMse4lAgN +8U+ewV8kDw0DXA0Yy4ZC4ODYVjfCAM1qlvtqGrj2tYpcPLZtFz77//imW3fsTp7o +oHes2+QIWEF/H+fp4JevCaPjZw+tjEP5CgA94yJi0JLNZ1/tKF6H +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/cert/fpvone.pem b/cert/fpvone.pem new file mode 100644 index 0000000..18a2419 --- /dev/null +++ b/cert/fpvone.pem @@ -0,0 +1,86 @@ +-----BEGIN CERTIFICATE----- +MIIGOzCCBSOgAwIBAgIQFDo//oGiOvBw82dnrPZCbDANBgkqhkiG9w0BAQsFADBj +MQswCQYDVQQGEwJDTjE2MDQGA1UECgwtQmVpamluZyBYaW5jaGFjaGEgQ3JlZGl0 +IE1hbmFnZW1lbnQgQ28uLCBMdGQuMRwwGgYDVQQDDBNYY2MgVHJ1c3QgRFYgU1NM +IENBMB4XDTI0MDMwNjA0NDA1NFoXDTI1MDQwNTA0NDA1M1owFjEUMBIGA1UEAwwL +Ki5mcHZvbmUuY24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1ZfXH +KlqrqIrKLjG7Yww5yIBkDKcTwpMpufGczq/HiG8K8tTSTtHaOku4HTylSi8p3uqN +Ed3V197jRoivmp/bGpRGH6SvznKOtwbaDj+p3c4t/bjPbQMeOF/iee8FbFFor2Wn +p7BY21BA7fWsII6RozVSWEqmEnlF8aAfNyI1tKlb0okQbEcfDcLRdILSsycO+3n5 +8CKx+EXF/ZDD5S68e25Fok+yh7Fjp4WBc8kxTjxsADyE8RmEzctJRRnyq8sfuWN9 +5OHi6prKmdIYLVBewBj/ZCFK8E2d9UHS+66ipYZGDuTbmXZgsv2j2H6k3hN66Gpl +0vycVEgCipVFmdc/AgMBAAGjggM2MIIDMjAMBgNVHRMBAf8EAjAAMEMGA1UdHwQ8 +MDowOKA2oDSGMmh0dHA6Ly94aW5jaGFjaGEyZHYuY3JsLmNlcnR1bS5wbC94aW5j +aGFjaGEyZHYuY3JsMHkGCCsGAQUFBwEBBG0wazAvBggrBgEFBQcwAYYjaHR0cDov +L3hpbmNoYWNoYTJkdi5vY3NwLWNlcnR1bS5jb20wOAYIKwYBBQUHMAKGLGh0dHA6 +Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC94aW5jaGFjaGEyZHYuY2VyMB8GA1UdIwQY +MBaAFKFDjgA0CeV7NTOudYkE8jJhD6jnMB0GA1UdDgQWBBTjfJcmc7jYRYOa6uUv +6HJjJeHFrDBMBgNVHSAERTBDMAgGBmeBDAECATA3BgwqhGgBhvZ3AgUBFQMwJzAl +BggrBgEFBQcCARYZaHR0cHM6Ly93d3cuY2VydHVtLnBsL0NQUzAdBgNVHSUEFjAU +BggrBgEFBQcDAQYIKwYBBQUHAwIwDgYDVR0PAQH/BAQDAgWgMCEGA1UdEQQaMBiC +CyouZnB2b25lLmNugglmcHZvbmUuY24wggGABgorBgEEAdZ5AgQCBIIBcASCAWwB +agB3AE51oydcmhDDOFts1N8/Uusd8OCOG41pwLH6ZLFimjnfAAABjhIQjmMAAAQD +AEgwRgIhAN83/5KqmJdSzNpghJTlhmsZLc2EbRcPcL89rR492SuMAiEA6zjbL6d6 +vYzO/gHhn60/ZC8h6fvQUdTwH6dXp0RjJS4AdgCi4wrkRe+9rZt+OO1HZ3dT14Jb +hJTXK14bLMS5UKRH5wAAAY4SEI+LAAAEAwBHMEUCIQD5jEshZNEHPzeMX8pw+HaR +3Vd6DPldhDuuBgfqg5Y5yQIgGF7AuBtY+/NGB7K0TFXFyxd9xLJgBuCEQuHZxkOF +1wgAdwB9WR4S4XgqexxhZ3xe/fjQh1wUoE6VnrkDL9kOjC55uAAAAY4SEJAoAAAE +AwBIMEYCIQD3QUudfLwRdIalw03BR04YRUOnab+pNiokDuYZPfrDmgIhAIBNHJL1 +RZKBiDxtbILa1pCjlcgIeWHObCmhrKi2+WnmMA0GCSqGSIb3DQEBCwUAA4IBAQAu +VMRaga7UxAF6uaDfzvCQmBbynFRyNMbsQiR8vFjR5hnF1ULQYvT1SuQ5AMbVPNe1 +psXRm8xoN65nu7NEszoEFGnsoJlbSBl4UI6P8Z+NLv62gK98y1Eb5Q9V1/2kwGHw +wPXsm0RDbcSrudByz3BWY1YnAcNwDpAzdXlFbE+10FV4OzBImCn8WesBS233Rc+U ++ohVc8c0EMVUblRVqT0AfPvaIDQ3c8pXeTr9RJIo/OoTwQFrnTYiRjq0vP56GHvz +JBXt86EUoCIgM3MmpVjE+QyTEIOWuapGFTAIdyuLzARnbroU7cLTqqNCcFSeV/K/ +vejX9+UCSOmmjJJ3hwuR +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEzzCCA7egAwIBAgIRAMK3VduTvUcsGNNRC9juoN4wDQYJKoZIhvcNAQELBQAw +fjELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEiMCAG +A1UEAxMZQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQTAeFw0yMjA3MDEwNzUxMDFa +Fw0yNzA2MzAwNzUxMDFaMGMxCzAJBgNVBAYTAkNOMTYwNAYDVQQKDC1CZWlqaW5n +IFhpbmNoYWNoYSBDcmVkaXQgTWFuYWdlbWVudCBDby4sIEx0ZC4xHDAaBgNVBAMM +E1hjYyBUcnVzdCBEViBTU0wgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCpa5zapltXVizFjCctkL/jY/pycE4gR4m+Nd8B1aqUcolQrlD2pWL785f1 +2/AFVAxlZuAsMfqT4qQL+M0y9jfg7qRhH80CLg5picFn8ZONR5SthRvOdBuG/COb +1YS6IFa7M7mSWQcAdnLfJVlzdzJLWh4fCW+4I4ymV0AQvjpomOHv2UWyBwckKgbh +obgR9mx65zduNFWFNC60A+7NY++6BIY5qv/Vafo1exBas1X/e/pdDgqEM9GZYCHR +1+uDcssGA6qsis7tsDjdLOS4Y+0nQYtE/dxPpdyEdyXvJprnR7rgYCS8Xmi5Kgj4 +vUFOTeg+/bpH7Z/JRPH8aKPMD6TrAgMBAAGjggFhMIIBXTASBgNVHRMBAf8ECDAG +AQH/AgEAMB0GA1UdDgQWBBShQ44ANAnlezUzrnWJBPIyYQ+o5zAfBgNVHSMEGDAW +gBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0lBBYw +FAYIKwYBBQUHAwEGCCsGAQUFBwMCMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9j +cmwuY2VydHVtLnBsL2N0bmNhLmNybDBrBggrBgEFBQcBAQRfMF0wKAYIKwYBBQUH +MAGGHGh0dHA6Ly9zdWJjYS5vY3NwLWNlcnR1bS5jb20wMQYIKwYBBQUHMAKGJWh0 +dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9jdG5jYS5jZXIwOgYDVR0gBDMwMTAv +BgRVHSAAMCcwJQYIKwYBBQUHAgEWGWh0dHBzOi8vd3d3LmNlcnR1bS5wbC9DUFMw +DQYJKoZIhvcNAQELBQADggEBAA9Nm/PoxLgAQhGx+C73ueZFFsIMtI/3j+OBgopN +jeDTcLwB97EZW75xgY1JeapByljUyF/ccjHmGg89pSqiOoznrd84H/cvfVXZlY+f +0k98JdzAdkeTg1Kca2ppqTBg1kX565gYOUwj0kXyP+AjVuyKwFht7RJpV5BE4Ejo +fQOaOtv5PFyIE+TlqOQjzBZWzWyt7EUUpKbhhhfoiX0CfneyNhP9805n2RMG79UR +MVtpz8D8yUKgQ6q+V4lZ2flN+p1lLcBOOf79+yRWtE9E/ntSc4DjExnIg5eLiIVD ++huf+lzpbZOq7IhL/XI3A3TTOhw5FCXfq0uPtC5z/MYXW1c= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..53d0aa8 --- /dev/null +++ b/composer.json @@ -0,0 +1,47 @@ +{ + "name": "karsonzhang/fastadmin", + "description": "the fastest admin framework", + "type": "project", + "keywords": [ + "fastadmin", + "thinkphp" + ], + "homepage": "https://www.fastadmin.net/", + "license": "Apache-2.0", + "authors": [ + { + "name": "Karson", + "email": "karson@fastadmin.net" + } + ], + "require": { + "php": ">=7.2.0", + "topthink/framework": "dev-master", + "topthink/think-captcha": "^1.0", + "topthink/think-installer": "^1.0.14", + "topthink/think-queue": "v1.1.6", + "topthink/think-helper": "^1.0.7", + "karsonzhang/fastadmin-addons": "~1.3.2", + "overtrue/pinyin": "^3.0", + "phpoffice/phpspreadsheet": "1.19", + "overtrue/wechat": "^4.6", + "nelexa/zip": "^3.3", + "ext-json": "*", + "ext-curl": "*", + "ext-pdo": "*", + "ext-bcmath": "*", + "txthinking/mailer": "^2.0", + "workerman/phpsocket.io": "^2.0", + "alibabacloud/dysmsapi-20180501": "1.0.10", + "alibabacloud/dysmsapi-20170525": "^3.1" + }, + "config": { + "preferred-install": "dist" + }, + "repositories": [ + { + "type": "git", + "url": "https://gitee.com/fastadminnet/framework.git" + } + ] +} diff --git a/extend/.htaccess b/extend/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/extend/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/extend/fast/Auth.php b/extend/fast/Auth.php new file mode 100644 index 0000000..ecc3708 --- /dev/null +++ b/extend/fast/Auth.php @@ -0,0 +1,265 @@ + +// +---------------------------------------------------------------------- +// | 修改者: anuo (本权限类在原3.2.3的基础上修改过来的) +// +---------------------------------------------------------------------- + +namespace fast; + +use think\Db; +use think\Config; +use think\Session; +use think\Request; + +/** + * 权限认证类 + * 功能特性: + * 1,是对规则进行认证,不是对节点进行认证。用户可以把节点当作规则名称实现对节点进行认证。 + * $auth=new Auth(); $auth->check('规则名称','用户id') + * 2,可以同时对多条规则进行认证,并设置多条规则的关系(or或者and) + * $auth=new Auth(); $auth->check('规则1,规则2','用户id','and') + * 第三个参数为and时表示,用户需要同时具有规则1和规则2的权限。 当第三个参数为or时,表示用户值需要具备其中一个条件即可。默认为or + * 3,一个用户可以属于多个用户组(think_auth_group_access表 定义了用户所属用户组)。我们需要设置每个用户组拥有哪些规则(think_auth_group 定义了用户组权限) + * 4,支持规则表达式。 + * 在think_auth_rule 表中定义一条规则,condition字段就可以定义规则表达式。 如定义{score}>5 and {score}<100 + * 表示用户的分数在5-100之间时这条规则才会通过。 + */ +class Auth +{ + + /** + * @var object 对象实例 + */ + protected static $instance; + protected $rules = []; + + /** + * 当前请求实例 + * @var Request + */ + protected $request; + //默认配置 + protected $config = [ + 'auth_on' => 1, // 权限开关 + 'auth_type' => 1, // 认证方式,1为实时认证;2为登录认证。 + 'auth_group' => 'auth_group', // 用户组数据表名 + 'auth_group_access' => 'auth_group_access', // 用户-用户组关系表 + 'auth_rule' => 'auth_rule', // 权限规则表 + 'auth_user' => 'user', // 用户信息表 + ]; + + public function __construct() + { + if ($auth = Config::get('auth')) { + $this->config = array_merge($this->config, $auth); + } + // 初始化request + $this->request = Request::instance(); + } + + /** + * 初始化 + * @access public + * @param array $options 参数 + * @return Auth + */ + public static function instance($options = []) + { + if (is_null(self::$instance)) { + self::$instance = new static($options); + } + + return self::$instance; + } + + /** + * 检查权限 + * @param string|array $name 需要验证的规则列表,支持逗号分隔的权限规则或索引数组 + * @param int $uid 认证用户的id + * @param string $relation 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证 + * @param string $mode 执行验证的模式,可分为url,normal + * @return bool 通过验证返回true;失败返回false + */ + public function check($name, $uid, $relation = 'or', $mode = 'url') + { + if (!$this->config['auth_on']) { + return true; + } + // 获取用户需要验证的所有有效规则列表 + $rulelist = $this->getRuleList($uid); + if (in_array('*', $rulelist)) { + return true; + } + + if (is_string($name)) { + $name = strtolower($name); + if (strpos($name, ',') !== false) { + $name = explode(',', $name); + } else { + $name = [$name]; + } + } + $list = []; //保存验证通过的规则名 + if ('url' == $mode) { + $REQUEST = unserialize(strtolower(serialize($this->request->param()))); + } + foreach ($rulelist as $rule) { + $query = preg_replace('/^.+\?/U', '', $rule); + if ('url' == $mode && $query != $rule) { + parse_str($query, $param); //解析规则中的param + $intersect = array_intersect_assoc($REQUEST, $param); + $rule = preg_replace('/\?.*$/U', '', $rule); + if (in_array($rule, $name) && $intersect == $param) { + //如果节点相符且url参数满足 + $list[] = $rule; + } + } else { + if (in_array($rule, $name)) { + $list[] = $rule; + } + } + } + if ('or' == $relation && !empty($list)) { + return true; + } + $diff = array_diff($name, $list); + if ('and' == $relation && empty($diff)) { + return true; + } + + return false; + } + + /** + * 根据用户id获取用户组,返回值为数组 + * @param int $uid 用户id + * @return array 用户所属的用户组 array( + * array('uid'=>'用户id','group_id'=>'用户组id','name'=>'用户组名称','rules'=>'用户组拥有的规则id,多个,号隔开'), + * ...) + */ + public function getGroups($uid) + { + static $groups = []; + if (isset($groups[$uid])) { + return $groups[$uid]; + } + + // 执行查询 + $user_groups = Db::name($this->config['auth_group_access']) + ->alias('aga') + ->join('__' . strtoupper($this->config['auth_group']) . '__ ag', 'aga.group_id = ag.id', 'LEFT') + ->field('aga.uid,aga.group_id,ag.id,ag.pid,ag.name,ag.rules') + ->where("aga.uid='{$uid}' and ag.status='normal'") + ->select(); + $groups[$uid] = $user_groups ?: []; + return $groups[$uid]; + } + + /** + * 获得权限规则列表 + * @param int $uid 用户id + * @return array + */ + public function getRuleList($uid) + { + static $_rulelist = []; //保存用户验证通过的权限列表 + if (isset($_rulelist[$uid])) { + return $_rulelist[$uid]; + } + if (2 == $this->config['auth_type'] && Session::has('_rule_list_' . $uid)) { + return Session::get('_rule_list_' . $uid); + } + + // 读取用户规则节点 + $ids = $this->getRuleIds($uid); + if (empty($ids)) { + $_rulelist[$uid] = []; + return []; + } + + // 筛选条件 + $where = [ + 'status' => 'normal' + ]; + if (!in_array('*', $ids)) { + $where['id'] = ['in', $ids]; + } + //读取用户组所有权限规则 + $this->rules = Db::name($this->config['auth_rule'])->where($where)->field('id,pid,condition,icon,name,title,ismenu')->select(); + + //循环规则,判断结果。 + $rulelist = []; // + if (in_array('*', $ids)) { + $rulelist[] = "*"; + } + foreach ($this->rules as $rule) { + //超级管理员无需验证condition + if (!empty($rule['condition']) && !in_array('*', $ids)) { + //根据condition进行验证 + $user = $this->getUserInfo($uid); //获取用户信息,一维数组 + $nums = 0; + $condition = str_replace(['&&', '||'], "\r\n", $rule['condition']); + $condition = preg_replace('/\{(\w*?)\}/', '\\1', $condition); + $conditionArr = explode("\r\n", $condition); + foreach ($conditionArr as $index => $item) { + preg_match("/^(\w+)\s?([\>\<\=]+)\s?(.*)$/", trim($item), $matches); + if ($matches && isset($user[$matches[1]]) && version_compare($user[$matches[1]], $matches[3], $matches[2])) { + $nums++; + } + } + if ($conditionArr && ((stripos($rule['condition'], "||") !== false && $nums > 0) || count($conditionArr) == $nums)) { + $rulelist[$rule['id']] = strtolower($rule['name']); + } + } else { + //只要存在就记录 + $rulelist[$rule['id']] = strtolower($rule['name']); + } + } + $_rulelist[$uid] = $rulelist; + //登录验证则需要保存规则列表 + if (2 == $this->config['auth_type']) { + //规则列表结果保存到session + Session::set('_rule_list_' . $uid, $rulelist); + } + return array_unique($rulelist); + } + + public function getRuleIds($uid) + { + //读取用户所属用户组 + $groups = $this->getGroups($uid); + $ids = []; //保存用户所属用户组设置的所有权限规则id + foreach ($groups as $g) { + $ids = array_merge($ids, explode(',', trim($g['rules'], ','))); + } + $ids = array_unique($ids); + return $ids; + } + + /** + * 获得用户资料 + * @param int $uid 用户id + * @return mixed + */ + public function getUserInfo($uid) + { + static $user_info = []; + + $user = Db::name($this->config['auth_user']); + // 获取用户表主键 + $_pk = is_string($user->getPk()) ? $user->getPk() : 'uid'; + if (!isset($user_info[$uid])) { + $user_info[$uid] = $user->where($_pk, $uid)->find(); + } + + return $user_info[$uid]; + } +} diff --git a/extend/fast/Date.php b/extend/fast/Date.php new file mode 100644 index 0000000..8491925 --- /dev/null +++ b/extend/fast/Date.php @@ -0,0 +1,226 @@ +. + * + * @param string $remote timezone that to find the offset of + * @param string $local timezone used as the baseline + * @param mixed $now UNIX timestamp or date string + * @return integer + */ + public static function offset($remote, $local = null, $now = null) + { + if ($local === null) { + // Use the default timezone + $local = date_default_timezone_get(); + } + if (is_int($now)) { + // Convert the timestamp into a string + $now = date(DateTime::RFC2822, $now); + } + // Create timezone objects + $zone_remote = new DateTimeZone($remote); + $zone_local = new DateTimeZone($local); + // Create date objects from timezones + $time_remote = new DateTime($now, $zone_remote); + $time_local = new DateTime($now, $zone_local); + // Find the offset + $offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local); + return $offset; + } + + /** + * 计算两个时间戳之间相差的时间 + * + * $span = self::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2) + * $span = self::span(60, 182, 'minutes'); // 2 + * + * @param int $remote timestamp to find the span of + * @param int $local timestamp to use as the baseline + * @param string $output formatting string + * @return string when only a single output is requested + * @return array associative list of all outputs requested + * @from https://github.com/kohana/ohanzee-helpers/blob/master/src/Date.php + */ + public static function span($remote, $local = null, $output = 'years,months,weeks,days,hours,minutes,seconds') + { + // Normalize output + $output = trim(strtolower((string)$output)); + if (!$output) { + // Invalid output + return false; + } + // Array with the output formats + $output = preg_split('/[^a-z]+/', $output); + // Convert the list of outputs to an associative array + $output = array_combine($output, array_fill(0, count($output), 0)); + // Make the output values into keys + extract(array_flip($output), EXTR_SKIP); + if ($local === null) { + // Calculate the span from the current time + $local = time(); + } + // Calculate timespan (seconds) + $timespan = abs($remote - $local); + if (isset($output['years'])) { + $timespan -= self::YEAR * ($output['years'] = (int)floor($timespan / self::YEAR)); + } + if (isset($output['months'])) { + $timespan -= self::MONTH * ($output['months'] = (int)floor($timespan / self::MONTH)); + } + if (isset($output['weeks'])) { + $timespan -= self::WEEK * ($output['weeks'] = (int)floor($timespan / self::WEEK)); + } + if (isset($output['days'])) { + $timespan -= self::DAY * ($output['days'] = (int)floor($timespan / self::DAY)); + } + if (isset($output['hours'])) { + $timespan -= self::HOUR * ($output['hours'] = (int)floor($timespan / self::HOUR)); + } + if (isset($output['minutes'])) { + $timespan -= self::MINUTE * ($output['minutes'] = (int)floor($timespan / self::MINUTE)); + } + // Seconds ago, 1 + if (isset($output['seconds'])) { + $output['seconds'] = $timespan; + } + if (count($output) === 1) { + // Only a single output was requested, return it + return array_pop($output); + } + // Return array + return $output; + } + + /** + * 格式化 UNIX 时间戳为人易读的字符串 + * + * @param int Unix 时间戳 + * @param mixed $local 本地时间 + * + * @return string 格式化的日期字符串 + */ + public static function human($remote, $local = null) + { + $time_diff = (is_null($local) || $local ? time() : $local) - $remote; + $tense = $time_diff < 0 ? 'after' : 'ago'; + $time_diff = abs($time_diff); + $chunks = [ + [60 * 60 * 24 * 365, 'year'], + [60 * 60 * 24 * 30, 'month'], + [60 * 60 * 24 * 7, 'week'], + [60 * 60 * 24, 'day'], + [60 * 60, 'hour'], + [60, 'minute'], + [1, 'second'] + ]; + $name = 'second'; + $count = 0; + + for ($i = 0, $j = count($chunks); $i < $j; $i++) { + $seconds = $chunks[$i][0]; + $name = $chunks[$i][1]; + if (($count = floor($time_diff / $seconds)) != 0) { + break; + } + } + return __("%d $name%s $tense", $count, ($count > 1 ? 's' : '')); + } + + /** + * 获取一个基于时间偏移的Unix时间戳 + * + * @param string $type 时间类型,默认为day,可选minute,hour,day,week,month,quarter,year + * @param int $offset 时间偏移量 默认为0,正数表示当前type之后,负数表示当前type之前 + * @param string $position 时间的开始或结束,默认为begin,可选前(begin,start,first,front),end + * @param int $year 基准年,默认为null,即以当前年为基准 + * @param int $month 基准月,默认为null,即以当前月为基准 + * @param int $day 基准天,默认为null,即以当前天为基准 + * @param int $hour 基准小时,默认为null,即以当前年小时基准 + * @param int $minute 基准分钟,默认为null,即以当前分钟为基准 + * @return int 处理后的Unix时间戳 + */ + public static function unixtime($type = 'day', $offset = 0, $position = 'begin', $year = null, $month = null, $day = null, $hour = null, $minute = null) + { + $year = is_null($year) ? date('Y') : $year; + $month = is_null($month) ? date('m') : $month; + $day = is_null($day) ? date('d') : $day; + $hour = is_null($hour) ? date('H') : $hour; + $minute = is_null($minute) ? date('i') : $minute; + $position = in_array($position, array('begin', 'start', 'first', 'front')); + + $baseTime = mktime(0, 0, 0, $month, $day, $year); + + switch ($type) { + case 'minute': + $time = $position ? mktime($hour, $minute + $offset, 0, $month, $day, $year) : mktime($hour, $minute + $offset, 59, $month, $day, $year); + break; + case 'hour': + $time = $position ? mktime($hour + $offset, 0, 0, $month, $day, $year) : mktime($hour + $offset, 59, 59, $month, $day, $year); + break; + case 'day': + $time = $position ? mktime(0, 0, 0, $month, $day + $offset, $year) : mktime(23, 59, 59, $month, $day + $offset, $year); + break; + case 'week': + $weekIndex = date("w", $baseTime); + $time = $position ? + strtotime($offset . " weeks", strtotime(date('Y-m-d', strtotime("-" . ($weekIndex ? $weekIndex - 1 : 6) . " days", $baseTime)))) : + strtotime($offset . " weeks", strtotime(date('Y-m-d 23:59:59', strtotime("+" . (6 - ($weekIndex ? $weekIndex - 1 : 6)) . " days", $baseTime)))); + break; + case 'month': + $_timestamp = mktime(0, 0, 0, $month + $offset, 1, $year); + $time = $position ? $_timestamp : mktime(23, 59, 59, $month + $offset, self::days_in_month(date("m", $_timestamp), date("Y", $_timestamp)), $year); + break; + case 'quarter': + $_month = date("m", mktime(0, 0, 0, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, $day, $year)); + $time = $position ? + mktime(0, 0, 0, 1 + ((ceil(date('n', $baseTime) / 3) + $offset) - 1) * 3, 1, $year) : + mktime(23, 59, 59, (ceil(date('n', $baseTime) / 3) + $offset) * 3, self::days_in_month((ceil(date('n', $baseTime) / 3) + $offset) * 3, $year), $year); + break; + case 'year': + $time = $position ? mktime(0, 0, 0, 1, 1, $year + $offset) : mktime(23, 59, 59, 12, 31, $year + $offset); + break; + default: + $time = mktime($hour, $minute, 0, $month, $day, $year); + break; + } + return $time; + } + + /** + * 获取指定年月拥有的天数 + * @param int $month + * @param int $year + * @return false|int|string + */ + public static function days_in_month($month, $year) + { + if (function_exists("cal_days_in_month")) { + return cal_days_in_month(CAL_GREGORIAN, $month, $year); + } else { + return date('t', mktime(0, 0, 0, $month, 1, $year)); + } + } +} diff --git a/extend/fast/Form.php b/extend/fast/Form.php new file mode 100644 index 0000000..92f343d --- /dev/null +++ b/extend/fast/Form.php @@ -0,0 +1,1289 @@ + '__token__'); + + /** + * 已创建的标签名称 + * + * @var array + */ + protected $labels = []; + + /** + * 跳过的填充value值的类型 + * + * @var array + */ + protected $skipValueTypes = array('file', 'password', 'checkbox', 'radio'); + + /** + * 转义HTML + * @var boolean + */ + protected $escapeHtml = true; + protected static $instance; + + /** + * 获取单例 + * @param array $options + * @return static + */ + public static function instance($options = []) + { + if (is_null(self::$instance)) { + self::$instance = new static($options); + } + + return self::$instance; + } + + /** + * 设置是否转义 + * @param boolean $escape + */ + public function setEscapeHtml($escape) + { + $this->escapeHtml = $escape; + } + + /** + * 获取转义编码后的值 + * @param string $value + * @return string + */ + public function escape($value) + { + if (!$this->escapeHtml) { + return $value; + } + if (is_array($value)) { + $value = json_encode($value, JSON_UNESCAPED_UNICODE); + } + return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false); + } + + /** + * 生成Token + * + * @param string $name + * @param string $type + * @return string + */ + public function token($name = '__token__', $type = 'md5') + { + if (function_exists('token')) { + return token($name, $type); + } + + return ''; + } + + /** + * 生成Label标签 + * + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function label($name, $value = null, $options = []) + { + $this->labels[] = $name; + + $options = $this->attributes($options); + $value = $this->escape($this->formatLabel($name, $value)); + + return ''; + } + + /** + * Format the label value. + * + * @param string $name + * @param string|null $value + * @return string + */ + protected function formatLabel($name, $value) + { + return $value ?: ucwords(str_replace('_', ' ', $name)); + } + + /** + * 生成文本框(按类型) + * + * @param string $type + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function input($type, $name, $value = null, $options = []) + { + if (!isset($options['name'])) { + $options['name'] = $name; + } + + $id = $this->getIdAttribute($name, $options); + + if (!in_array($type, $this->skipValueTypes)) { + $value = $this->getValueAttribute($name, $value); + $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' form-control') : 'form-control'; + } + + $merge = compact('type', 'value', 'id'); + $options = array_merge($options, $merge); + + return 'attributes($options) . '>'; + } + + /** + * 生成普通文本框 + * + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function text($name, $value = null, $options = []) + { + return $this->input('text', $name, $value, $options); + } + + /** + * 生成密码文本框 + * + * @param string $name + * @param array $options + * @return string + */ + public function password($name, $options = []) + { + return $this->input('password', $name, '', $options); + } + + /** + * 生成隐藏文本框 + * + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function hidden($name, $value = null, $options = []) + { + return $this->input('hidden', $name, $value, $options); + } + + /** + * 生成Email文本框 + * + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function email($name, $value = null, $options = []) + { + return $this->input('email', $name, $value, $options); + } + + /** + * 生成URL文本框 + * + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function url($name, $value = null, $options = []) + { + return $this->input('url', $name, $value, $options); + } + + /** + * 生成上传文件组件 + * + * @param string $name + * @param array $options + * @return string + */ + public function file($name, $options = []) + { + return $this->input('file', $name, null, $options); + } + + /** + * 生成多行文本框 + * + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function textarea($name, $value = null, $options = []) + { + if (!isset($options['name'])) { + $options['name'] = $name; + } + + $options = $this->setTextAreaSize($options); + $options['id'] = $this->getIdAttribute($name, $options); + $value = (string)$this->getValueAttribute($name, $value); + + unset($options['size']); + + $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' form-control') : 'form-control'; + $options = $this->attributes($options); + + return '' . $this->escape($value) . ''; + } + + /** + * 生成富文本编辑器 + * + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function editor($name, $value = null, $options = []) + { + $options['class'] = isset($options['class']) ? $options['class'] . ' editor' : 'editor'; + return $this->textarea($name, $value, $options); + } + + /** + * 设置默认的文本框行列数 + * + * @param array $options + * @return array + */ + protected function setTextAreaSize($options) + { + if (isset($options['size'])) { + return $this->setQuickTextAreaSize($options); + } + + $cols = array_get($options, 'cols', 50); + $rows = array_get($options, 'rows', 5); + + return array_merge($options, compact('cols', 'rows')); + } + + /** + * 根据size设置行数和列数 + * + * @param array $options + * @return array + */ + protected function setQuickTextAreaSize($options) + { + $segments = explode('x', $options['size']); + return array_merge($options, array('cols' => $segments[0], 'rows' => $segments[1])); + } + + /** + * 生成滑块 + * + * @param string $name + * @param string $min + * @param string $max + * @param string $step + * @param string $value + * @param array $options + * @return string + */ + public function slider($name, $min, $max, $step, $value = null, $options = []) + { + $options = array_merge($options, ['data-slider-min' => $min, 'data-slider-max' => $max, 'data-slider-step' => $step, 'data-slider-value' => $value ? $value : '']); + $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' slider form-control') : 'slider form-control'; + return $this->input('text', $name, $value, $options); + } + + /** + * 生成下拉列表框 + * + * @param string $name + * @param array $list + * @param mixed $selected + * @param array $options + * @return string + */ + public function select($name, $list = [], $selected = null, $options = []) + { + $selected = $this->getValueAttribute($name, $selected); + + $options['id'] = $this->getIdAttribute($name, $options); + + if (!isset($options['name'])) { + $options['name'] = $name; + } + + $html = []; + foreach ($list as $value => $display) { + $html[] = $this->getSelectOption($display, $value, $selected); + } + $options['class'] = isset($options['class']) ? $options['class'] . (stripos($options['class'], 'form-control') !== false ? '' : ' form-control') : 'form-control'; + + $options = $this->attributes($options); + $list = implode('', $html); + + return "{$list}"; + } + + /** + * 下拉列表(多选) + * + * @param string $name + * @param array $list + * @param mixed $selected + * @param array $options + * @return string + */ + public function selects($name, $list = [], $selected = null, $options = []) + { + $options[] = 'multiple'; + return $this->select($name, $list, $selected, $options); + } + + /** + * 下拉列表(友好) + * + * @param string $name + * @param array $list + * @param mixed $selected + * @param array $options + * @return string + */ + public function selectpicker($name, $list = [], $selected = null, $options = []) + { + $options['class'] = isset($options['class']) ? $options['class'] . ' selectpicker' : 'selectpicker'; + return $this->select($name, $list, $selected, $options); + } + + /** + * 下拉列表(友好)(多选) + * + * @param string $name + * @param array $list + * @param mixed $selected + * @param array $options + * @return string + */ + public function selectpickers($name, $list = [], $selected = null, $options = []) + { + $options[] = 'multiple'; + return $this->selectpicker($name, $list, $selected, $options); + } + + /** + * 生成动态下拉列表 + * + * @param string $name 名称 + * @param mixed $value + * @param string $url 数据源地址 + * @param string $field 显示的字段名称,默认为name + * @param string $primaryKey 主键,数据库中保存的值,默认为id + * @param array $options + * @return string + */ + public function selectpage($name, $value, $url, $field = null, $primaryKey = null, $options = []) + { + $options = array_merge($options, ['data-source' => $url, 'data-field' => $field ? $field : 'name', 'data-primary-key' => $primaryKey ? $primaryKey : 'id']); + $options['class'] = isset($options['class']) ? $options['class'] . ' selectpage' : 'selectpage'; + return $this->text($name, $value, $options); + } + + + /** + * 生成动态下拉列表(复选) + * + * @param string $name 名称 + * @param mixed $value + * @param string $url 数据源地址 + * @param string $field 显示的字段名称,默认为name + * @param string $primaryKey 主键,数据库中保存的值,默认为id + * @param array $options + * @return string + */ + public function selectpages($name, $value, $url, $field = null, $primaryKey = null, $options = []) + { + $options['data-multiple'] = "true"; + return $this->selectpage($name, $value, $url, $field, $primaryKey, $options); + } + + /** + * 生成城市选择框 + * + * @param string $name + * @param mixed $value + * @param array $options + * @return string + */ + public function citypicker($name, $value, $options = []) + { + $options['data-toggle'] = 'city-picker'; + return "
                                                                                                                              " . $this->text($name, $value, $options) . "
                                                                                                                              "; + } + + /** + * 生成switch组件 + * + * @param string $name + * @param mixed $value + * @param array $options + * @return string + */ + public function switcher($name, $value, $options = []) + { + $domname = str_replace(['[', ']', '.'], '', $name); + $btn = $this->hidden($name, $value, ['id' => "c-{$domname}"]); + $yes = 1; + $no = 0; + if (isset($options['yes']) && isset($options['no'])) { + $yes = $options['yes']; + $no = $options['no']; + } + $selected = $no == $value ? "fa-flip-horizontal text-gray" : ""; + $disabled = (isset($options['disabled']) && $options['disabled']) || in_array('disabled', $options) ? "disabled" : ''; + $color = isset($options['color']) ? $options['color'] : 'success'; + unset($options['yes'], $options['no'], $options['color'], $options['disabled']); + $attr = $this->attributes($options); + $html = << +EOD; + return $html; + } + + /** + * 日期选择器 + * @param string $name + * @param mixed $value + * @param array $options + * @return string + */ + public function datepicker($name, $value, $options = []) + { + $defaults = [ + 'data-date-format' => "YYYY-MM-DD", + ]; + $options = array_merge($defaults, $options); + $value = is_numeric($value) ? date("Y-m-d", $value) : $value; + return $this->datetimepicker($name, $value, $options); + } + + /** + * 时间选择器 + * + * @param string $name + * @param mixed $value + * @param array $options + * @return string + */ + public function timepicker($name, $value, $options = []) + { + $defaults = [ + 'data-date-format' => "HH:mm:ss", + ]; + $options = array_merge($defaults, $options); + $value = is_numeric($value) ? date("H:i:s", $value) : $value; + return $this->datetimepicker($name, $value, $options); + } + + /** + * 日期时间选择器 + * + * @param string $name + * @param mixed $value + * @param array $options + * @return string + */ + public function datetimepicker($name, $value, $options = []) + { + $defaults = [ + 'data-date-format' => "YYYY-MM-DD HH:mm:ss", + 'data-use-current' => "true", + ]; + $value = is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + $options = array_merge($defaults, $options); + $options['class'] = isset($options['class']) ? $options['class'] . ' datetimepicker' : 'datetimepicker'; + return $this->text($name, $value, $options); + } + + /** + * 日期区间 + * + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function daterange($name, $value, $options = []) + { + $defaults = [ + 'data-locale' => [ + 'format' => 'YYYY-MM-DD' + ] + ]; + $options = array_merge($defaults, $options); + return $this->datetimerange($name, $value, $options); + } + + /** + * 时间区间 + * + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function timerange($name, $value, $options = []) + { + $defaults = [ + 'data-locale' => [ + 'format' => 'HH:mm:ss' + ], + 'data-ranges' => [], + 'data-show-custom-range-label' => "false", + 'data-time-picker' => "true", + ]; + $options = array_merge($defaults, $options); + return $this->datetimerange($name, $value, $options); + } + + /** + * 日期时间区间 + * + * @param string $name + * @param string $value + * @param array $options + * @return string + */ + public function datetimerange($name, $value, $options = []) + { + $defaults = [ + 'data-locale' => [ + 'format' => 'YYYY-MM-DD HH:mm:ss' + ] + ]; + $options = array_merge($defaults, $options); + $options['class'] = isset($options['class']) ? $options['class'] . ' datetimerange' : 'datetimerange'; + return $this->text($name, $value, $options); + } + + /** + * 生成字段列表组件 + * + * @param string $name + * @param mixed $value + * @param array $title + * @param string $template + * @param array $options + * @return string + */ + public function fieldlist($name, $value, $title = null, $template = null, $options = []) + { + $append = __('Append'); + $template = $template ? 'data-template="' . $template . '"' : ''; + $attributes = $this->attributes($options); + if (is_null($title)) { + $title = [__('Key'), __('Value')]; + } + $ins = implode("\n", array_map(function ($value) { + return "{$value}"; + }, $title)); + $value = is_array($value) ? json_encode($value) : $value; + $html = << +
                                                                                                                              + {$ins} +
                                                                                                                              +
                                                                                                                              {$append}
                                                                                                                              + + +EOD; + return $html; + } + + /** + * 生成联动下拉列表 + * + * @param string $url 联动获取数据源的URL地址 + * @param array $names 联动字段名称 + * @param array $values 联动字段默认选中的值 + * @param array $options 扩展属性 + * @return string + */ + public function cxselect($url, $names = [], $values = [], $options = []) + { + $classes = []; + $cxselect = []; + $attributes = $this->attributes($options); + foreach ($names as $index => $value) { + $level = $index + 1; + $class = "cxselect-{$level}"; + $classes[] = $class; + $selectValue = isset($values[$value]) ? $values[$value] : (isset($values[$index]) ? $values[$index] : ''); + + $cxselect[] = << +EOD; + } + $cxselect = implode("\n", $cxselect); + $selects = implode(',', $classes); + $html = << +{$cxselect} + +EOD; + return $html; + } + + /** + * 创建一个下拉列表选择区间组件 + * + * @param string $name + * @param string $begin + * @param string $end + * @param string $selected + * @param array $options + * @return string + */ + public function selectRange($name, $begin, $end, $selected = null, $options = []) + { + $range = array_combine($range = range($begin, $end), $range); + return $this->select($name, $range, $selected, $options); + } + + /** + * 生成选择年组件 + * + * @param string $name + * @param string $begin + * @param string $end + * @param string $selected + * @param array $options + * @return string + */ + public function selectYear($name, $begin, $end, $selected, $options) + { + return call_user_func_array(array($this, 'selectRange'), func_get_args()); + } + + /** + * 生成选择月组件 + * + * @param string $name + * @param string $selected + * @param array $options + * @param string $format + * @return string + */ + public function selectMonth($name, $selected = null, $options = [], $format = '%m') + { + $months = []; + + foreach (range(1, 12) as $month) { + $months[$month] = strftime($format, mktime(0, 0, 0, $month, 1)); + } + + return $this->select($name, $months, $selected, $options); + } + + /** + * 根据传递的值生成option + * + * @param string $display + * @param string $value + * @param string $selected + * @return string + */ + public function getSelectOption($display, $value, $selected) + { + if (is_array($display)) { + return $this->optionGroup($display, $value, $selected); + } + + return $this->option($display, $value, $selected); + } + + /** + * 生成optionGroup + * + * @param array $list + * @param string $label + * @param string $selected + * @return string + */ + protected function optionGroup($list, $label, $selected) + { + $html = []; + + foreach ($list as $value => $display) { + $html[] = $this->option($display, $value, $selected); + } + + return '' . implode('', $html) . ''; + } + + /** + * 生成option选项 + * + * @param string $display + * @param string $value + * @param string $selected + * @return string + */ + protected function option($display, $value, $selected) + { + $selected = $this->getSelectedValue($value, $selected); + + $options = array('value' => $this->escape($value), 'selected' => $selected); + + return 'attributes($options) . '>' . $this->escape($display) . ''; + } + + /** + * 检测value是否选中 + * + * @param string $value + * @param string $selected + * @return string + */ + protected function getSelectedValue($value, $selected) + { + if (is_array($selected)) { + return in_array($value, $selected) ? 'selected' : null; + } + + return ((string)$value == (string)$selected) ? 'selected' : null; + } + + /** + * 生成复选按钮 + * + * @param string $name + * @param mixed $value + * @param bool $checked + * @param array $options + * @return string + */ + public function checkbox($name, $value = 1, $checked = null, $options = []) + { + if ($checked) { + $options['checked'] = 'checked'; + } + + return $this->input('checkbox', $name, $value, $options); + } + + /** + * 生成一组筛选框 + * + * @param string $name + * @param array $list + * @param mixed $checked + * @param array $options + * @return string + */ + public function checkboxs($name, $list, $checked, $options = []) + { + $html = []; + $checked = is_null($checked) ? [] : $checked; + $checked = is_array($checked) ? $checked : explode(',', $checked); + foreach ($list as $k => $v) { + $options['id'] = "{$name}-{$k}"; + $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::checkbox("{$name}[{$k}]", $k, in_array($k, $checked), $options)); + } + return '
                                                                                                                              ' . implode(' ', $html) . '
                                                                                                                              '; + } + + /** + * 生成单选按钮 + * + * @param string $name + * @param mixed $value + * @param bool $checked + * @param array $options + * @return string + */ + public function radio($name, $value = null, $checked = null, $options = []) + { + if (is_null($value)) { + $value = $name; + } + + if ($checked) { + $options['checked'] = 'checked'; + } + + return $this->input('radio', $name, $value, $options); + } + + /** + * 生成一组单选框 + * + * @param string $name + * @param array $list + * @param mixed $checked + * @param array $options + * @return string + */ + public function radios($name, $list, $checked = null, $options = []) + { + $html = []; + $checked = is_null($checked) ? key($list) : $checked; + $checked = is_array($checked) ? $checked : explode(',', $checked); + foreach ($list as $k => $v) { + $options['id'] = "{$name}-{$k}"; + $html[] = sprintf(Form::label("{$name}-{$k}", "%s {$v}"), Form::radio($name, $k, in_array($k, $checked), $options)); + } + return '
                                                                                                                              ' . implode(' ', $html) . '
                                                                                                                              '; + } + + /** + * 生成上传图片组件(单图) + * + * @param string $name + * @param string $value + * @param array $inputAttr + * @param array $uploadAttr + * @param array $chooseAttr + * @param array $previewAttr + * @return string + */ + public function image($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = []) + { + $default = [ + 'data-mimetype' => 'image/gif,image/jpeg,image/png,image/jpg,image/bmp' + ]; + $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr; + $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr; + return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr); + } + + /** + * 生成上传图片组件(多图) + * + * @param string $name + * @param string $value + * @param array $inputAttr + * @param array $uploadAttr + * @param array $chooseAttr + * @param array $previewAttr + * @return string + */ + public function images($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = []) + { + $default = [ + 'data-multiple' => 'true', + 'data-mimetype' => 'image/gif,image/jpeg,image/png,image/jpg,image/bmp' + ]; + $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr; + $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr; + return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr); + } + + /** + * 生成上传文件组件(单文件) + * + * @param string $name + * @param string $value + * @param array $inputAttr + * @param array $uploadAttr + * @param array $chooseAttr + * @param array $previewAttr + * @return string + */ + public function upload($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = []) + { + return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr); + } + + /** + * 生成上传文件组件(多文件) + * + * @param string $name + * @param string $value + * @param array $inputAttr + * @param array $uploadAttr + * @param array $chooseAttr + * @param array $previewAttr + * @return string + */ + public function uploads($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = []) + { + $default = [ + 'data-multiple' => 'true', + ]; + $uploadAttr = is_array($uploadAttr) ? array_merge($default, $uploadAttr) : $uploadAttr; + $chooseAttr = is_array($chooseAttr) ? array_merge($default, $chooseAttr) : $chooseAttr; + return $this->uploader($name, $value, $inputAttr, $uploadAttr, $chooseAttr, $previewAttr); + } + + protected function uploader($name = null, $value = null, $inputAttr = [], $uploadAttr = [], $chooseAttr = [], $previewAttr = []) + { + $domname = str_replace(['[', ']', '.'], '', $name); + $options = [ + 'id' => "plupload-{$domname}", + 'class' => "btn btn-danger plupload", + 'data-input-id' => "c-{$domname}", + ]; + $upload = $uploadAttr === false ? false : true; + $choose = $chooseAttr === false ? false : true; + $preview = $previewAttr === false ? false : true; + if ($preview) { + $options['data-preview-id'] = "p-{$domname}"; + } + $uploadBtn = $upload ? $this->button(' ' . __('Upload'), array_merge($options, $uploadAttr)) : ''; + $options = [ + 'id' => "fachoose-{$domname}", + 'class' => "btn btn-danger fachoose", + 'data-input-id' => "c-{$domname}", + ]; + if ($preview) { + $options['data-preview-id'] = "p-{$domname}"; + } + $chooseBtn = $choose ? $this->button(' ' . __('Choose'), array_merge($options, $chooseAttr)) : ''; + $previewAttrHtml = $this->attributes($previewAttr); + $previewArea = $preview ? '
                                                                                                                                ' : ''; + $input = $this->text($name, $value, array_merge(['size' => 50, 'id' => "c-{$domname}"], $inputAttr)); + $html = << + {$input} +
                                                                                                                                + {$uploadBtn} + {$chooseBtn} +
                                                                                                                                + + + {$previewArea} +EOD; + return $html; + } + + /** + * 生成一个按钮 + * + * @param string $value + * @param array $options + * @return string + */ + public function button($value = null, $options = []) + { + if (!array_key_exists('type', $options)) { + $options['type'] = 'button'; + } + + return 'attributes($options) . '>' . $value . ''; + } + + /** + * 获取ID属性值 + * + * @param string $name + * @param array $attributes + * @return string + */ + public function getIdAttribute($name, $attributes) + { + if (array_key_exists('id', $attributes)) { + return $attributes['id']; + } + + if (in_array($name, $this->labels)) { + return $name; + } + } + + /** + * 获取Value属性值 + * + * @param string $name + * @param string $value + * @return string + */ + public function getValueAttribute($name, $value = null) + { + if (is_null($name)) { + return $value; + } + + if (!is_null($value)) { + return $value; + } + } + + /** + * 数组转换成一个HTML属性字符串。 + * + * @param array $attributes + * @return string + */ + public function attributes($attributes) + { + $html = []; + // 假设我们的keys 和 value 是相同的, + // 拿HTML“required”属性来说,假设是['required']数组, + // 会已 required="required" 拼接起来,而不是用数字keys去拼接 + foreach ((array)$attributes as $key => $value) { + $element = $this->attributeElement($key, $value); + if (!is_null($element)) { + $html[] = $element; + } + } + return count($html) > 0 ? ' ' . implode(' ', $html) : ''; + } + + /** + * 拼接成一个属性。 + * + * @param string $key + * @param string $value + * @return string + */ + protected function attributeElement($key, $value) + { + if (is_numeric($key)) { + $key = $value; + } + if (!is_null($value)) { + if (is_array($value) || stripos($value, '"') !== false) { + $value = is_array($value) ? json_encode($value, JSON_UNESCAPED_UNICODE) : $value; + return $key . "='" . $value . "'"; + } else { + return $key . '="' . $value . '"'; + } + } + } +} + +class Arr +{ + + /** + * Determine whether the given value is array accessible. + * + * @param mixed $value + * @return bool + */ + public static function accessible($value) + { + return is_array($value) || $value instanceof ArrayAccess; + } + + /** + * Determine if the given key exists in the provided array. + * + * @param \ArrayAccess|array $array + * @param string|int $key + * @return bool + */ + public static function exists($array, $key) + { + if ($array instanceof ArrayAccess) { + return $array->offsetExists($key); + } + return array_key_exists($key, $array); + } + + /** + * Get an item from an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string $key + * @param mixed $default + * @return mixed + */ + public static function get($array, $key, $default = null) + { + if (!static::accessible($array)) { + return $default; + } + if (is_null($key)) { + return $array; + } + if (static::exists($array, $key)) { + return $array[$key]; + } + foreach (explode('.', $key) as $segment) { + if (static::accessible($array) && static::exists($array, $segment)) { + $array = $array[$segment]; + } else { + return $default; + } + } + return $array; + } + + /** + * Get all of the given array except for a specified array of items. + * + * @param array $array + * @param array|string $keys + * @return array + */ + public static function except($array, $keys) + { + static::forget($array, $keys); + return $array; + } + + /** + * Remove one or many array items from a given array using "dot" notation. + * + * @param array $array + * @param array|string $keys + * @return void + */ + public static function forget(&$array, $keys) + { + $original = &$array; + $keys = (array)$keys; + if (count($keys) === 0) { + return; + } + foreach ($keys as $key) { + // if the exact key exists in the top-level, remove it + if (static::exists($array, $key)) { + unset($array[$key]); + continue; + } + $parts = explode('.', $key); + // clean up before each pass + $array = &$original; + while (count($parts) > 1) { + $part = array_shift($parts); + if (isset($array[$part]) && is_array($array[$part])) { + $array = &$array[$part]; + } else { + continue 2; + } + } + unset($array[array_shift($parts)]); + } + } +} + +if (!function_exists('array_get')) { + + /** + * Get an item from an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string $key + * @param mixed $default + * @return mixed + */ + function array_get($array, $key, $default = null) + { + return Arr::get($array, $key, $default); + } +} +if (!function_exists('e')) { + + /** + * Escape HTML special characters in a string. + * + * + * @return string + */ + function e($value) + { + if (is_array($value)) { + $value = json_encode($value, JSON_UNESCAPED_UNICODE); + } + return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false); + } +} +if (!function_exists('array_except')) { + + /** + * Get all of the given array except for a specified array of items. + * + * @param array $array + * @param array|string $keys + * @return array + */ + function array_except($array, $keys) + { + return Arr::except($array, $keys); + } +} diff --git a/extend/fast/Http.php b/extend/fast/Http.php new file mode 100644 index 0000000..81f2b17 --- /dev/null +++ b/extend/fast/Http.php @@ -0,0 +1,185 @@ + false, + 'errno' => $errno, + 'msg' => $err, + 'info' => $info, + ]; + } + curl_close($ch); + return [ + 'ret' => true, + 'msg' => $ret, + ]; + } + + /** + * 异步发送一个请求 + * @param string $url 请求的链接 + * @param mixed $params 请求的参数 + * @param string $method 请求的方法 + * @return boolean TRUE + */ + public static function sendAsyncRequest($url, $params = [], $method = 'POST') + { + $method = strtoupper($method); + $method = $method == 'POST' ? 'POST' : 'GET'; + //构造传递的参数 + if (is_array($params)) { + $post_params = []; + foreach ($params as $k => &$v) { + if (is_array($v)) { + $v = implode(',', $v); + } + $post_params[] = $k . '=' . urlencode($v); + } + $post_string = implode('&', $post_params); + } else { + $post_string = $params; + } + $parts = parse_url($url); + //构造查询的参数 + if ($method == 'GET' && $post_string) { + $parts['query'] = isset($parts['query']) ? $parts['query'] . '&' . $post_string : $post_string; + $post_string = ''; + } + $parts['query'] = isset($parts['query']) && $parts['query'] ? '?' . $parts['query'] : ''; + //发送socket请求,获得连接句柄 + $fp = fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80, $errno, $errstr, 3); + if (!$fp) { + return false; + } + //设置超时时间 + stream_set_timeout($fp, 3); + $out = "{$method} {$parts['path']}{$parts['query']} HTTP/1.1\r\n"; + $out .= "Host: {$parts['host']}\r\n"; + $out .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $out .= "Content-Length: " . strlen($post_string) . "\r\n"; + $out .= "Connection: Close\r\n\r\n"; + if ($post_string !== '') { + $out .= $post_string; + } + fwrite($fp, $out); + //不用关心服务器返回结果 + //echo fread($fp, 1024); + fclose($fp); + return true; + } + + /** + * 发送文件到客户端 + * @param string $file + * @param bool $delaftersend + * @param bool $exitaftersend + */ + public static function sendToBrowser($file, $delaftersend = true, $exitaftersend = true) + { + if (file_exists($file) && is_readable($file)) { + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment;filename = ' . basename($file)); + header('Content-Transfer-Encoding: binary'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check = 0, pre-check = 0'); + header('Pragma: public'); + header('Content-Length: ' . filesize($file)); + ob_clean(); + flush(); + readfile($file); + if ($delaftersend) { + unlink($file); + } + if ($exitaftersend) { + exit; + } + } + } +} diff --git a/extend/fast/Pinyin.php b/extend/fast/Pinyin.php new file mode 100644 index 0000000..7397c4f --- /dev/null +++ b/extend/fast/Pinyin.php @@ -0,0 +1,36 @@ +abbr($chinese, $delimiter); + } else { + $result = $pinyin->permalink($chinese, $delimiter); + } + if ($ucfirst) { + $pinyinArr = explode($delimiter, $result); + $result = implode($delimiter, array_map('ucfirst', $pinyinArr)); + } + + return $result; + } + +} diff --git a/extend/fast/Random.php b/extend/fast/Random.php new file mode 100644 index 0000000..1e25a1d --- /dev/null +++ b/extend/fast/Random.php @@ -0,0 +1,171 @@ +20, 'p2'=>30, 'p3'=>50); + * @param int $num 默认为1,即随机出来的数量 + * @param bool $unique 默认为true,即当num>1时,随机出的数量是否唯一 + * @return mixed 当num为1时返回键名,反之返回一维数组 + */ + public static function lottery($ps, $num = 1, $unique = true) + { + if (!$ps) { + return $num == 1 ? '' : []; + } + if ($num >= count($ps) && $unique) { + $res = array_keys($ps); + return $num == 1 ? $res[0] : $res; + } + $max_exp = 0; + $res = []; + foreach ($ps as $key => $value) { + $value = substr($value, 0, stripos($value, ".") + 6); + $exp = strlen(strchr($value, '.')) - 1; + if ($exp > $max_exp) { + $max_exp = $exp; + } + } + $pow_exp = pow(10, $max_exp); + if ($pow_exp > 1) { + reset($ps); + foreach ($ps as $key => $value) { + $ps[$key] = $value * $pow_exp; + } + } + $pro_sum = array_sum($ps); + if ($pro_sum < 1) { + return $num == 1 ? '' : []; + } + for ($i = 0; $i < $num; $i++) { + $rand_num = mt_rand(1, $pro_sum); + reset($ps); + foreach ($ps as $key => $value) { + if ($rand_num <= $value) { + break; + } else { + $rand_num -= $value; + } + } + if ($num == 1) { + $res = $key; + break; + } else { + $res[$i] = $key; + } + if ($unique) { + $pro_sum -= $value; + unset($ps[$key]); + } + } + return $res; + } + + /** + * 获取全球唯一标识 + * @return string + */ + public static function uuid() + { + return sprintf( + '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0x0fff) | 0x4000, + mt_rand(0, 0x3fff) | 0x8000, + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0xffff) + ); + } +} diff --git a/extend/fast/Rsa.php b/extend/fast/Rsa.php new file mode 100644 index 0000000..b64fe69 --- /dev/null +++ b/extend/fast/Rsa.php @@ -0,0 +1,179 @@ +setKey($publicKey, $privateKey); + } + + /** + * 设置公钥和私钥 + * @param string $publicKey 公钥 + * @param string $privateKey 私钥 + */ + public function setKey($publicKey = null, $privateKey = null) + { + if (!is_null($publicKey)) { + $this->publicKey = $publicKey; + } + if (!is_null($privateKey)) { + $this->privateKey = $privateKey; + } + } + + /** + * * setup the private key + */ + private function setupPrivKey() + { + if (is_resource($this->_privKey)) { + return true; + } + $pem = chunk_split($this->privateKey, 64, "\n"); + $pem = "-----BEGIN PRIVATE KEY-----\n" . $pem . "-----END PRIVATE KEY-----\n"; + $this->_privKey = openssl_pkey_get_private($pem); + return true; + } + + /** + * * setup the public key + */ + private function setupPubKey() + { + if (is_resource($this->_pubKey)) { + return true; + } + $pem = chunk_split($this->publicKey, 64, "\n"); + $pem = "-----BEGIN PUBLIC KEY-----\n" . $pem . "-----END PUBLIC KEY-----\n"; + $this->_pubKey = openssl_pkey_get_public($pem); + return true; + } + + /** + * * encrypt with the private key + */ + public function privEncrypt($data) + { + if (!is_string($data)) { + return null; + } + $this->setupPrivKey(); + $r = openssl_private_encrypt($data, $encrypted, $this->_privKey); + if ($r) { + return base64_encode($encrypted); + } + return null; + } + + /** + * * decrypt with the private key + */ + public function privDecrypt($encrypted) + { + if (!is_string($encrypted)) { + return null; + } + $this->setupPrivKey(); + $encrypted = base64_decode($encrypted); + $r = openssl_private_decrypt($encrypted, $decrypted, $this->_privKey); + if ($r) { + return $decrypted; + } + return null; + } + + /** + * * encrypt with public key + */ + public function pubEncrypt($data) + { + if (!is_string($data)) { + return null; + } + $this->setupPubKey(); + $r = openssl_public_encrypt($data, $encrypted, $this->_pubKey); + if ($r) { + return base64_encode($encrypted); + } + return null; + } + + /** + * * decrypt with the public key + */ + public function pubDecrypt($crypted) + { + if (!is_string($crypted)) { + return null; + } + $this->setupPubKey(); + $crypted = base64_decode($crypted); + $r = openssl_public_decrypt($crypted, $decrypted, $this->_pubKey); + if ($r) { + return $decrypted; + } + return null; + } + + /** + * 构造签名 + * @param string $dataString 被签名数据 + * @return string + */ + public function sign($dataString) + { + $this->setupPrivKey(); + $signature = false; + openssl_sign($dataString, $signature, $this->_privKey); + return base64_encode($signature); + } + + /** + * 验证签名 + * @param string $dataString 被签名数据 + * @param string $signString 已经签名的字符串 + * @return number 1签名正确 0签名错误 + */ + public function verify($dataString, $signString) + { + $this->setupPubKey(); + $signature = base64_decode($signString); + $flg = openssl_verify($dataString, $signature, $this->_pubKey); + return $flg; + } + + public function __destruct() + { + is_resource($this->_privKey) && @openssl_free_key($this->_privKey); + is_resource($this->_pubKey) && @openssl_free_key($this->_pubKey); + } +} diff --git a/extend/fast/Tree.php b/extend/fast/Tree.php new file mode 100644 index 0000000..2c1997c --- /dev/null +++ b/extend/fast/Tree.php @@ -0,0 +1,438 @@ + + */ +class Tree +{ + protected static $instance; + //默认配置 + protected $config = []; + public $options = []; + + /** + * 生成树型结构所需要的2维数组 + * @var array + */ + public $arr = []; + + /** + * 生成树型结构所需修饰符号,可以换成图片 + * @var array + */ + public $icon = array('│', '├', '└'); + public $nbsp = " "; + public $pidname = 'pid'; + + public function __construct($options = []) + { + if ($config = Config::get('tree')) { + $this->options = array_merge($this->config, $config); + } + $this->options = array_merge($this->config, $options); + } + + /** + * 初始化 + * @access public + * @param array $options 参数 + * @return Tree + */ + public static function instance($options = []) + { + if (is_null(self::$instance)) { + self::$instance = new static($options); + } + + return self::$instance; + } + + /** + * 初始化方法 + * @param array $arr 2维数组,例如: + * array( + * 1 => array('id'=>'1','pid'=>0,'name'=>'一级栏目一'), + * 2 => array('id'=>'2','pid'=>0,'name'=>'一级栏目二'), + * 3 => array('id'=>'3','pid'=>1,'name'=>'二级栏目一'), + * 4 => array('id'=>'4','pid'=>1,'name'=>'二级栏目二'), + * 5 => array('id'=>'5','pid'=>2,'name'=>'二级栏目三'), + * 6 => array('id'=>'6','pid'=>3,'name'=>'三级栏目一'), + * 7 => array('id'=>'7','pid'=>3,'name'=>'三级栏目二') + * ) + * @param string $pidname 父字段名称 + * @param string $nbsp 空格占位符 + * @return Tree + */ + public function init($arr = [], $pidname = null, $nbsp = null) + { + $this->arr = $arr; + if (!is_null($pidname)) { + $this->pidname = $pidname; + } + if (!is_null($nbsp)) { + $this->nbsp = $nbsp; + } + return $this; + } + + /** + * 得到子级数组 + * @param int + * @return array + */ + public function getChild($myid) + { + $newarr = []; + foreach ($this->arr as $value) { + if (!isset($value['id'])) { + continue; + } + if ($value[$this->pidname] == $myid) { + $newarr[$value['id']] = $value; + } + } + return $newarr; + } + + /** + * 读取指定节点的所有孩子节点 + * @param int $myid 节点ID + * @param boolean $withself 是否包含自身 + * @return array + */ + public function getChildren($myid, $withself = false) + { + $newarr = []; + foreach ($this->arr as $value) { + if (!isset($value['id'])) { + continue; + } + if ((string)$value[$this->pidname] == (string)$myid) { + $newarr[] = $value; + $newarr = array_merge($newarr, $this->getChildren($value['id'])); + } elseif ($withself && (string)$value['id'] == (string)$myid) { + $newarr[] = $value; + } + } + return $newarr; + } + + /** + * 读取指定节点的所有孩子节点ID + * @param int $myid 节点ID + * @param boolean $withself 是否包含自身 + * @return array + */ + public function getChildrenIds($myid, $withself = false) + { + $childrenlist = $this->getChildren($myid, $withself); + $childrenids = []; + foreach ($childrenlist as $k => $v) { + $childrenids[] = $v['id']; + } + return $childrenids; + } + + /** + * 得到当前位置父辈数组 + * @param int + * @return array + */ + public function getParent($myid) + { + $pid = 0; + $newarr = []; + foreach ($this->arr as $value) { + if (!isset($value['id'])) { + continue; + } + if ($value['id'] == $myid) { + $pid = $value[$this->pidname]; + break; + } + } + if ($pid) { + foreach ($this->arr as $value) { + if ($value['id'] == $pid) { + $newarr[] = $value; + break; + } + } + } + return $newarr; + } + + /** + * 得到当前位置所有父辈数组 + * @param int + * @param bool $withself 是否包含自己 + * @return array + */ + public function getParents($myid, $withself = false) + { + $pid = 0; + $newarr = []; + foreach ($this->arr as $value) { + if (!isset($value['id'])) { + continue; + } + if ($value['id'] == $myid) { + if ($withself) { + $newarr[] = $value; + } + $pid = $value[$this->pidname]; + break; + } + } + if ($pid) { + $arr = $this->getParents($pid, true); + $newarr = array_merge($arr, $newarr); + } + return $newarr; + } + + /** + * 读取指定节点所有父类节点ID + * @param int $myid + * @param boolean $withself + * @return array + */ + public function getParentsIds($myid, $withself = false) + { + $parentlist = $this->getParents($myid, $withself); + $parentsids = []; + foreach ($parentlist as $k => $v) { + $parentsids[] = $v['id']; + } + return $parentsids; + } + + /** + * 树型结构Option + * @param int $myid 表示获得这个ID下的所有子级 + * @param string $itemtpl 条目模板 如:"" + * @param mixed $selectedids 被选中的ID,比如在做树型下拉框的时候需要用到 + * @param mixed $disabledids 被禁用的ID,比如在做树型下拉框的时候需要用到 + * @param string $itemprefix 每一项前缀 + * @param string $toptpl 顶级栏目的模板 + * @return string + */ + public function getTree($myid, $itemtpl = "", $selectedids = '', $disabledids = '', $itemprefix = '', $toptpl = '') + { + $ret = ''; + $number = 1; + $childs = $this->getChild($myid); + if ($childs) { + $total = count($childs); + foreach ($childs as $value) { + $id = $value['id']; + $j = $k = ''; + if ($number == $total) { + $j .= $this->icon[2]; + $k = $itemprefix ? $this->nbsp : ''; + } else { + $j .= $this->icon[1]; + $k = $itemprefix ? $this->icon[0] : ''; + } + $spacer = $itemprefix ? $itemprefix . $j : ''; + $selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : ''; + $disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : ''; + $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled, 'spacer' => $spacer)); + $value = array_combine(array_map(function ($k) { + return '@' . $k; + }, array_keys($value)), $value); + $nstr = strtr((($value["@{$this->pidname}"] == 0 || $this->getChild($id)) && $toptpl ? $toptpl : $itemtpl), $value); + $ret .= $nstr; + $ret .= $this->getTree($id, $itemtpl, $selectedids, $disabledids, $itemprefix . $k . $this->nbsp, $toptpl); + $number++; + } + } + return $ret; + } + + /** + * 树型结构UL + * @param int $myid 表示获得这个ID下的所有子级 + * @param string $itemtpl 条目模板 如:"
                                                                                                                              • @name @childlist
                                                                                                                              • " + * @param string $selectedids 选中的ID + * @param string $disabledids 禁用的ID + * @param string $wraptag 子列表包裹标签 + * @param string $wrapattr 子列表包裹属性 + * @return string + */ + public function getTreeUl($myid, $itemtpl, $selectedids = '', $disabledids = '', $wraptag = 'ul', $wrapattr = '') + { + $str = ''; + $childs = $this->getChild($myid); + if ($childs) { + foreach ($childs as $value) { + $id = $value['id']; + unset($value['child']); + $selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : ''; + $disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : ''; + $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled)); + $value = array_combine(array_map(function ($k) { + return '@' . $k; + }, array_keys($value)), $value); + $nstr = strtr($itemtpl, $value); + $childdata = $this->getTreeUl($id, $itemtpl, $selectedids, $disabledids, $wraptag, $wrapattr); + $childlist = $childdata ? "<{$wraptag} {$wrapattr}>" . $childdata . "" : ""; + $str .= strtr($nstr, array('@childlist' => $childlist)); + } + } + return $str; + } + + /** + * 菜单数据 + * @param int $myid + * @param string $itemtpl + * @param mixed $selectedids + * @param mixed $disabledids + * @param string $wraptag + * @param string $wrapattr + * @param int $deeplevel + * @return string + */ + public function getTreeMenu($myid, $itemtpl, $selectedids = '', $disabledids = '', $wraptag = 'ul', $wrapattr = '', $deeplevel = 0) + { + $str = ''; + $childs = $this->getChild($myid); + if ($childs) { + foreach ($childs as $value) { + $id = $value['id']; + unset($value['child']); + $selected = in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : ''; + $disabled = in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : ''; + $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled)); + $value = array_combine(array_map(function ($k) { + return '@' . $k; + }, array_keys($value)), $value); + $bakvalue = array_intersect_key($value, array_flip(['@url', '@caret', '@class'])); + $value = array_diff_key($value, $bakvalue); + $nstr = strtr($itemtpl, $value); + $value = array_merge($value, $bakvalue); + $childdata = $this->getTreeMenu($id, $itemtpl, $selectedids, $disabledids, $wraptag, $wrapattr, $deeplevel + 1); + $childlist = $childdata ? "<{$wraptag} {$wrapattr}>" . $childdata . "" : ""; + $childlist = strtr($childlist, array('@class' => $childdata ? 'last' : '')); + $value = array( + '@childlist' => $childlist, + '@url' => $childdata || !isset($value['@url']) ? "javascript:;" : $value['@url'], + '@addtabs' => $childdata || !isset($value['@url']) ? "" : (stripos($value['@url'], "?") !== false ? "&" : "?") . "ref=addtabs", + '@caret' => ($childdata && (!isset($value['@badge']) || !$value['@badge']) ? '' : ''), + '@badge' => isset($value['@badge']) ? $value['@badge'] : '', + '@class' => ($selected ? ' active' : '') . ($disabled ? ' disabled' : '') . ($childdata ? ' treeview' . (config('fastadmin.show_submenu') ? ' treeview-open' : '') : ''), + ); + $str .= strtr($nstr, $value); + } + } + return $str; + } + + /** + * 特殊 + * @param integer $myid 要查询的ID + * @param string $itemtpl1 第一种HTML代码方式 + * @param string $itemtpl2 第二种HTML代码方式 + * @param mixed $selectedids 默认选中 + * @param mixed $disabledids 禁用 + * @param string $itemprefix 前缀 + * @return string + */ + public function getTreeSpecial($myid, $itemtpl1, $itemtpl2, $selectedids = 0, $disabledids = 0, $itemprefix = '') + { + $ret = ''; + $number = 1; + $childs = $this->getChild($myid); + if ($childs) { + $total = count($childs); + foreach ($childs as $id => $value) { + $j = $k = ''; + if ($number == $total) { + $j .= $this->icon[2]; + $k = $itemprefix ? $this->nbsp : ''; + } else { + $j .= $this->icon[1]; + $k = $itemprefix ? $this->icon[0] : ''; + } + $spacer = $itemprefix ? $itemprefix . $j : ''; + $selected = $selectedids && in_array($id, (is_array($selectedids) ? $selectedids : explode(',', $selectedids))) ? 'selected' : ''; + $disabled = $disabledids && in_array($id, (is_array($disabledids) ? $disabledids : explode(',', $disabledids))) ? 'disabled' : ''; + $value = array_merge($value, array('selected' => $selected, 'disabled' => $disabled, 'spacer' => $spacer)); + $value = array_combine(array_map(function ($k) { + return '@' . $k; + }, array_keys($value)), $value); + $nstr = strtr(!isset($value['@disabled']) || !$value['@disabled'] ? $itemtpl1 : $itemtpl2, $value); + + $ret .= $nstr; + $ret .= $this->getTreeSpecial($id, $itemtpl1, $itemtpl2, $selectedids, $disabledids, $itemprefix . $k . $this->nbsp); + $number++; + } + } + return $ret; + } + + /** + * + * 获取树状数组 + * @param string $myid 要查询的ID + * @param string $itemprefix 前缀 + * @return array + */ + public function getTreeArray($myid, $itemprefix = '') + { + $childs = $this->getChild($myid); + $n = 0; + $data = []; + $number = 1; + if ($childs) { + $total = count($childs); + foreach ($childs as $id => $value) { + $j = $k = ''; + if ($number == $total) { + $j .= $this->icon[2]; + $k = $itemprefix ? $this->nbsp : ''; + } else { + $j .= $this->icon[1]; + $k = $itemprefix ? $this->icon[0] : ''; + } + $spacer = $itemprefix ? $itemprefix . $j : ''; + $value['spacer'] = $spacer; + $data[$n] = $value; + $data[$n]['childlist'] = $this->getTreeArray($id, $itemprefix . $k . $this->nbsp); + $n++; + $number++; + } + } + return $data; + } + + /** + * 将getTreeArray的结果返回为二维数组 + * @param array $data + * @param string $field + * @return array + */ + public function getTreeList($data = [], $field = 'name') + { + $arr = []; + foreach ($data as $k => $v) { + $childlist = isset($v['childlist']) ? $v['childlist'] : []; + unset($v['childlist']); + $v[$field] = $v['spacer'] . ' ' . $v[$field]; + $v['haschild'] = $childlist ? 1 : 0; + if ($v['id']) { + $arr[] = $v; + } + if ($childlist) { + $arr = array_merge($arr, $this->getTreeList($childlist, $field)); + } + } + return $arr; + } +} diff --git a/extend/fast/Version.php b/extend/fast/Version.php new file mode 100644 index 0000000..af180a6 --- /dev/null +++ b/extend/fast/Version.php @@ -0,0 +1,79 @@ + $k) { + if (!self::compare($ver[$a], $k)) { + continue 2; + } else { + $i++; + } + } + if ($i == $versize) { + return true; + } + } + } + } + return false; + } + + /** + * 比较两个版本号 + * + * @param string $v1 + * @param string $v2 + * @return boolean + */ + public static function compare($v1, $v2) + { + if ($v2 == "*" || $v1 == $v2) { + return true; + } else { + $values = []; + $k = explode(',', $v2); + foreach ($k as $v) { + if (strpos($v, '-') !== false) { + list($start, $stop) = explode('-', $v); + for ($i = $start; $i <= $stop; $i++) { + $values[] = $i; + } + } else { + $values[] = $v; + } + } + return in_array($v1, $values) ? true : false; + } + } +} diff --git a/sites.txt b/sites.txt new file mode 100644 index 0000000..a9c9672 --- /dev/null +++ b/sites.txt @@ -0,0 +1 @@ +Runoob \ No newline at end of file diff --git a/think b/think new file mode 100644 index 0000000..8eeb81b --- /dev/null +++ b/think @@ -0,0 +1,17 @@ +#!/usr/bin/env php + +// +---------------------------------------------------------------------- + +// 定义项目路径 +define('APP_PATH', __DIR__ . '/application/'); + +// 加载框架引导文件 +require __DIR__ . '/thinkphp/console.php'; \ No newline at end of file