Compare commits

...

256 Commits

Author SHA1 Message Date
isummer e12779d654 修复容器部署失败问题,重新编写本地部署文档 2 years ago
isummer ac590563a7 修复超级签共享用户下载文件失败问题 2 years ago
isummer 8fc1d353c9 管理后台增加重置密码功能 3 years ago
isummer 35b64d74de 修复flyapps容器构建失败问题,优化部署文档 3 years ago
isummer a064988f56 修复flyapps容器构建失败问题,优化部署文档 3 years ago
isummer adff361c11 修复非苹果设备导致余额不足问题 3 years ago
isummer 419e39eaaa 修复非苹果设备导致余额不足问题 3 years ago
isummer 9d9cb3f2cb Merge remote-tracking branch 'origin/master' 3 years ago
isummer dd802dccbb 优化代码 3 years ago
isummer 1262bff671 优化代码 3 years ago
isummer 289737ed00 修复异常,优化代码 3 years ago
isummer dc7b491534 修复新苹果,新应用无法设置专属签名问题 3 years ago
isummer eed0f5194c 增加苹果设备类型区分,优化苹果设备注册量及展示 3 years ago
isummer e0599585be 修复前端应用超级签名展示异常问题 3 years ago
isummer 42451f2cb0 修复超级签权限问题 3 years ago
isummer 646162593e 增加打包时间 3 years ago
isummer 200494fcf3 增加打包时间 3 years ago
isummer fe5a7605f0 增加打包时间 3 years ago
isummer f70351d8e5 修复容器启动异常问题 3 years ago
isummer 9e2f40383a 优化代码,增加邀请码配置,修改极验弹窗模式 3 years ago
isummer debe95791e 优化容器部署 3 years ago
isummer a3887825c9 更新依赖包,支持python39环境 3 years ago
isummer 8a8f9ef7fe 升级python环境为python39 3 years ago
isummer 8e325e6cde 优化容器启停异常 3 years ago
isummer 92961cfa61 flyapps-cli增加容器访问 3 years ago
isummer 8c25aab63c flyapps-cli增加容器访问 3 years ago
isummer 0cc5084390 修复授权绑定设备添加重复问题 3 years ago
isummer 4d98981485 容器部署支持管理后台访问 3 years ago
isummer a7a1472533 苹果应用授权码支持添加绑定设备 3 years ago
isummer 332d12bddf 容器部署优化 3 years ago
isummer 6439824468 优化docker部署方式 3 years ago
isummer 42478f21d9 1.修复授权码为一次,超级签苹果包下载失败问题 3 years ago
isummer 2c67305f8a 增加通过命令给用户充值次数 3 years ago
isummer 79037d5248 修复应用上传超级签检测问题 3 years ago
isummer 525ec097d7 更新readme 3 years ago
isummer a35ca0b087 更新配置文件信息 3 years ago
isummer d9ac93ccc1 no message 3 years ago
isummer 1ff3da6896 修复管理页面预览展示 3 years ago
isummer cbad06e95d 应用时间更新修复 3 years ago
isummer b093d7133b 页面底部信息配置迁移 3 years ago
isummer 7aa0471ba9 增加应用当天下载次数展示 3 years ago
isummer 2224ce27c7 优化代码 3 years ago
isummer d8b124c1a7 用户下载页私有配置功能 3 years ago
isummer d1a5d69ac8 增加客户端独立部署功能 3 years ago
isummer 8a8d2f2400 增加预览下载页路由模式 3 years ago
isummer 49832ca702 修复新注册用户 默认域名绑定问题 3 years ago
isummer 7075bc7ed6 修改存储添加失败问题 3 years ago
MMXX 7d70f381a1 geetest 增加代理访问 3 years ago
MMXX 15a506450c 增加管理后台应用当天下载次数展示 3 years ago
isummer 93c13fa776 日志增加function名称 3 years ago
MMXX c8f02465d0 修复异常注册问题 3 years ago
isummer 2e240735dc 优化下载次数展示 3 years ago
I夏 ec908f0dea 代码优化,增加缓存函数提前失效参数 3 years ago
I夏 47abaf61f5 订单展示格式化 3 years ago
I夏 31fe8664a9 修复注册赠送下载数异常 3 years ago
nineven f4ca8d48f9 修復转换异常问题 3 years ago
nineven e2b9b12613 修復转换异常问题 3 years ago
nineven f772d65765 存储扩容功能 3 years ago
nineven 3ad5e03504 存储扩容功能 3 years ago
nineven 5f967a9b52 修复存储切换下载token过期问题 3 years ago
nineven e7858a840e 服务器需要部署阿里云,服务器和oss存储内网互通 3 years ago
nineven b191cbae6b 优化代码,增加存储迁移锁解除功能 3 years ago
nineven c36ab08f35 优化存储逻辑和展示页面 3 years ago
nineven a5b73c3695 增加存储共享功能,增加应用历史版本设置,修复设备共享查询问题,优化存储切换逻辑 3 years ago
nineven 0c6b61327a 修复域名绑定获取最小错误返回 3 years ago
nineven 92c0345341 优化代码展示 3 years ago
nineven 25c2ea4c7a 优化代码展示 3 years ago
nineven dfcf213bed 优化代码 3 years ago
nineven 2b29e0e3bd 优化代码 3 years ago
nineven a9bbdd40bd 修复读模式获取开发者发送失败通知 3 years ago
nineven 97349e758f 代码更新 3 years ago
nineven 156bf67c1c 优化代码 3 years ago
nineven 1d75c8ed23 优化异常注册逻辑,修复设置设备状态返回异常问题 3 years ago
nineven ef243feb9f 优化查询状态 3 years ago
nineven 58472fb9e4 修改文件下载失败问题 3 years ago
nineven 6c25f6a932 优化签名开发者查询逻辑 3 years ago
nineven 2d21ee9150 优化签名开发者查询逻辑 3 years ago
nineven 04ba947219 修复链接存在密码情况下,跳转密码丢失问题 3 years ago
nineven 884d6a47e8 大意失荆州啊 3 years ago
nineven 0a64689902 修复 bytesio 导致uwsgi 不识别问题 3 years ago
nineven 5ca5b0eb34 优化错误信息展示 3 years ago
nineven 3bbabdafdb 1.增加异常设备和设备黑名单功能 3 years ago
nineven 4cd0dc7586 设备异常 3 years ago
nineven 5eb947843e 优化啦 3 years ago
nineven 17ab6e6efd 修复前端页面时间显示问题 3 years ago
nineven 94bc5cc1db 优化代码 3 years ago
nineven 584f50ba20 优化页面 3 years ago
nineven df38944eab 优化页面 3 years ago
nineven fe880b27b7 优化下载方式 3 years ago
nineven 8316a52595 下载授权码和设备绑定功能,下载页面优化 3 years ago
nineven 747d2b3dfa 优化通知提醒,增加批量设置开发者账户自动检测,定时任务修改为 【0,1,3,7】自动提醒,同步设备支持条件 3 years ago
nineven 04ae8cccc4 增加应用下载授权token, 增加超级签名多重安全验证,优化本地下载逻辑 3 years ago
nineven 3b8134aafd 优化代码 3 years ago
nineven bb9bdf8891 邮件通知模板优化 3 years ago
nineven bc0cee937d 修复定时任务多线程数据共享异常 3 years ago
nineven d1a41a179b 优化代码,增加苹果签名错误操作记录 3 years ago
nineven 3ff322849a 优化设备错误展示 3 years ago
nineven 4184f2ad1c 问题修复 3 years ago
nineven 22a9b8d816 优化代码 3 years ago
nineven 2390397f1b 微信web登录跳转优化 3 years ago
nineven ae9dfae6a3 微信web登录跳转修改,定时通知逻辑优化 3 years ago
nineven d7e3186f9d 修复配置名重复添加失败问题 3 years ago
nineven 2bfd694f23 重要,微信登录 大版本更新,上一个版本更新 3 years ago
nineven 63e09c1925 增加微信登录多账户功能 3 years ago
nineven 391e4987de 优化消息提醒 3 years ago
nineven 88745550d4 增加消息提醒功能,下载余额,签名余额不足自动检测,发送邮件或微信通知功能 3 years ago
nineven 59e73fa83e 增加消息提醒功能,下载余额,签名余额不足自动检测,发送邮件或微信通知功能 3 years ago
nineven 5eca01278b 优化代码,增加余额不足微信模板消息 3 years ago
nineven 5edf7cd65a 优化代码,增加证书信息展示功能,增加包重签功能 3 years ago
nineven cf9d9eeaf8 优化代码 3 years ago
nineven c4a4db2c7e 优化定时任务设备消耗展示 3 years ago
nineven 24a8307164 优化设备消耗展示 3 years ago
nineven 05669b8d3e 苹果设备状态异常,导致注册失败【苹果设备状态还可能为 INELIGIBLE 和 PROCESSING】,当设备存在该状态,则该开发者将无法注册新设备号,需要禁用该开发者 3 years ago
nineven 32c3058d57 优化账户激活检测代码 3 years ago
nineven 4bbae93933 优化代码 3 years ago
nineven f94184551a 优化代码 3 years ago
nineven dd73441bdd 优化代码 3 years ago
nineven 4e7927a332 优化代码,修复定时任务检测证书获取异常并清理问题 3 years ago
MMXX fc1963c8a9 优化代码 3 years ago
MMXX d15b583804 优化代码 3 years ago
MMXX d930603919 优化代码 3 years ago
MMXX 9b9be3fcf9 版本更新 3 years ago
MMXX 378912944f 优化代码 3 years ago
MMXX 75c7d365a3 优化代码 3 years ago
MMXX 1dec3b7de2 Merge branch 'xsign' of https://github.com/nineaiyu/FlyApps into xsign 3 years ago
MMXX eeba5b409a 优化代码展示 3 years ago
nineven 684a407f97 优化代码,增加设备同步详细日志【有时候会删除该开发者下所有签名数据,很诡异】 3 years ago
nineven 5901d63f3e 优化代码,增加设备同步详细日志【有时候会删除该开发者下所有签名数据,很诡异】 3 years ago
MMXX cf14dcb0a5 代码优化,苹果开发者账户展示优化 3 years ago
MMXX e780a2597e 增加共享设备一键清理功能 3 years ago
MMXX 02f908e3de 增加共享设备一键清理功能 3 years ago
MMXX 7ceb9c83c1 优化代码,修改共享撤回问题 3 years ago
MMXX 77d97fd1d2 优化签名包下载逻辑 3 years ago
nineven 98fe1d5f61 优化代码 3 years ago
nineven fb77c251c3 优化代码 3 years ago
nineven c765658fb5 微信支付,微信web登录功能优化 3 years ago
nineven dd1fb29ba0 优化微信登录和获取任务长轮询请求方式 3 years ago
nineven d571b399d7 代码优化 3 years ago
nineven 115d1c95df 信号优化 3 years ago
nineven e54f6c1286 验证码优化 3 years ago
nineven 75fecbca10 超级签名重签功能 3 years ago
nineven 96295132d9 应用上传黑白名单 3 years ago
nineven ec327598a2 优化代码结构 3 years ago
nineven 81db86b9ef 优化代码结构 3 years ago
nineven 6076659bf1 Merge branch 'master' into xsign 3 years ago
nineven 4b90ce682b 问题修复 3 years ago
nineven f00135a6a4 xsign 模块 3 years ago
nineven 8ce3b14475 Merge branch 'master' into xsign 3 years ago
nineven 25d09e8cf1 修复小问题 3 years ago
nineven c6e78a6eab 优化代码 3 years ago
nineven e9c6cadeb6 重新迁移数据文件 3 years ago
nineven 153f800b91 代码结构优化 3 years ago
nineven 5e380fa09e 优化代码 3 years ago
nineven 42bab0739b 优化代码目录结构 3 years ago
nineven 518fc3101e 优化配置文件读取,支持文件数据库配置模板变量使用 3 years ago
nineven 875a3a4305 设备转移之后,匹配异常问题 3 years ago
nineven 6051f443c1 优化代码,配置文件内部支持template语法,修复新设备注册异常的问题 3 years ago
nineven c4fd9aa793 修复json配置读取失败的问题 3 years ago
nineven 9ba02e449d 大版本更新,签名逻辑更改 3 years ago
nineven cb841b5cf7 大版本更新,签名逻辑更改 3 years ago
nineven 9353abc8a9 配置文件迁移更新 3 years ago
nineven 06bace9f66 配置文件迁移更新 3 years ago
nineven ce31dd5621 优化域名绑定功能 3 years ago
nineven 91e042ea1d 優化代碼 3 years ago
nineven f02eb026de 开发者账号支持自定义自动检测 3 years ago
nineven 44cfd03cee 设置配置缓存过期时间30天 3 years ago
nineven 14f60d4f8d 部分配置信息迁移到数据库,方便直接修改数据库生效 3 years ago
nineven 614db12422 设备共享逻辑及前端页面书写 3 years ago
nineven 10ab90a104 修复清理开发者脏数据失败问题 3 years ago
nineven f5ffc3632f 签名账户设备转移功能 3 years ago
nineven adc71de160 优化上传p8key文件 3 years ago
nineven 6f959fa4a9 优化上传p8key文件 3 years ago
nineven 3d2ce3b73d 优化添加苹果开发者账号逻辑 3 years ago
nineven 192d4cadc0 优化代码 3 years ago
nineven 9f2fea094d 优化代码 3 years ago
nineven 32bdc4ba9e 优化代码 3 years ago
nineven fdd0abb07e 增加添加开发者账号检测功能 3 years ago
nineven a580dddd83 优化下载页 3 years ago
nineven 2f32b71b2b 优化下载页 3 years ago
MMXX 03a6202ee2 优化后台管理api 3 years ago
MMXX 7e15211fd2 清理签名临时数据 3 years ago
MMXX ae58a71ee4 修复签名异常导致死锁问题 3 years ago
MMXX b270d8e5c6 修复域名配置异常问题 3 years ago
MMXX 32ca967d07 优化代码,修改cors逻辑 4 years ago
MMXX 878183dbce 修改日志存储时间 4 years ago
MMXX bebc8417eb 优化应用域名展示 4 years ago
MMXX de511bf90c 状态展示优化 4 years ago
MMXX a8c1df58ee 优化代码,增加设备删除控制,增加设备被封状态 4 years ago
MMXX ef335e118a 优化代码 4 years ago
MMXX 03f66df7b9 优化代码,修复多个开发者,下载不是最新签名包问题,增加批量修改开发者状态 4 years ago
MMXX 4d4ee64a65 优化显示异常提示 4 years ago
MMXX 04ccfe77a3 优化代码,增加苹果账户备注查询功能 4 years ago
MMXX d085d86a26 苹果超级签名优化 4 years ago
MMXX 8a60be6f4f 优化代码 4 years ago
MMXX 869d5ee7bb 优化代码 4 years ago
MMXX 9572382a9e 优化代码 4 years ago
MMXX 7ad5bc8161 优化签名代码逻辑 4 years ago
MMXX 59d7d905d3 优化代码 4 years ago
MMXX c361d740bf 优化代码 4 years ago
MMXX 17c1f0612a 优化缓存代码 4 years ago
MMXX 2b5325ff32 优化缓存代码 4 years ago
MMXX 09257733f8 更新苹果api接口 4 years ago
MMXX 5dd1dd0681 后端更新下载码逻辑 4 years ago
MMXX 979b6f0386 更新前端页面 4 years ago
MMXX e8e6ea962a 修复已知问题 4 years ago
MMXX c50005e7c7 修复激活问题 4 years ago
MMXX 465aea9744 修复页面udid展示不全问题 4 years ago
MMXX 7c2a29b86d 优化代码 4 years ago
MMXX f8418f6414 增加签名应用限额 4 years ago
youngS def6c9dfc8 优化代码 4 years ago
youngS 205505cb0e 优化超级签 获取开发者账户逻辑,增加微信删除必须输入密码限制 4 years ago
youngS 106ae636a2 优化代码 4 years ago
youngS 2e96183c06 修复展示问题 4 years ago
youngS 216bc367c4 优化代码 4 years ago
youngS e559267de3 增加苹果开发者备注显示信息 4 years ago
youngS f04c4411c7 修复已知问题,修正 专属账户,其他应用禁止使用 4 years ago
youngS a60a7d6011 优化代码 4 years ago
youngS d3fc7327a6 增加应用专属开发者功能 4 years ago
youngS 4decc14bf8 优化代码 4 years ago
nineven d657c709f6 优化代码 4 years ago
nineven 73d783b97d 优化代码 4 years ago
nineven b18dfa7a14 优化代码 4 years ago
youngS 0d26e5ac7b 优化代码 4 years ago
youngS bda9cf994d 优化代码 4 years ago
youngS 9d0d36ca63 优化api请求时间 4 years ago
youngS 26e6efeaf4 优化代码,增加苹果api访问代理 4 years ago
youngS 49c35275b6 修复设备消耗查询问题 4 years ago
youngS 92ba4fc258 增加下载码展示 4 years ago
youngS c8a69acb84 增加下载码展示 4 years ago
youngS 7932dde3b4 修复bug 4 years ago
youngS 55f5f65e15 修复已知问题,增加设备消化统计 4 years ago
youngS fd43dcfea4 优化代码 4 years ago
youngS 4c41efa46f 修改 查询开发者策略 按照最小注册设备数进行查找 4 years ago
youngS d3e0c53b8f 修复数据存储异常问题 4 years ago
youngS bd77da66b7 修复自定义id名 4 years ago
youngS ebed9b1aa4 修复只有一个证书激活失败的问题 4 years ago
youngS 0c5c4a6ce7 修复异常问题 4 years ago
youngS b3899d4a97 修复异常问题 4 years ago
youngS 27b8a21556 数据库优化 4 years ago
youngS bbf9664fc7 修复异常问题 4 years ago
youngS 86467ef2fd 优化展示 4 years ago
youngS 2aedb458ee 优化前端展示 4 years ago
youngS 2e491db16c 代码bug修复 4 years ago
youngS 8a59b8d329 代码bug修复 4 years ago
youngS fce8a09d5e 优化代码 4 years ago
fxC 29328d293b 修复mac环境下启动服务失败 4 years ago
fxC 2ec7fb05eb 修复已知问题 4 years ago
youngS 341cf21367 签名优化 4 years ago
youngS 6412e36d0b 优化代码 4 years ago
youngS 8bf925e9df 优化代码 4 years ago
youngS d550d20998 管理后台增加超级签充值功能,前端页面增加udid展示 4 years ago
youngS c170086609 签名余额限制,开发者账户加锁,修复账单数量统计错误问题 4 years ago
youngS bd8c8d00cc 超级签名并发优化,签名余额还未限制,下个版本限制 4 years ago
youngS a2c5d1bd3f 超级签名并发优化,签名余额还未限制,下个版本限制 4 years ago
youngS 169347ca16 下载页举报按钮优化 4 years ago
youngS 372455bcb3 下载页举报按钮优化 4 years ago
  1. 5
      .gitignore
  2. 207
      README.md
  3. 12
      build.sh
  4. 7
      build_client.sh
  5. 102
      doc/docker.md
  6. BIN
      doc/images/img_1.png
  7. BIN
      doc/images/img_10.png
  8. BIN
      doc/images/img_11.png
  9. BIN
      doc/images/img_2.png
  10. BIN
      doc/images/img_3.png
  11. BIN
      doc/images/img_4.png
  12. BIN
      doc/images/img_5.png
  13. BIN
      doc/images/img_6.png
  14. BIN
      doc/images/img_7.png
  15. BIN
      doc/images/img_8.png
  16. BIN
      doc/images/img_9.png
  17. 166
      doc/local.md
  18. 51
      docker.md
  19. 67
      docker/build/docker-compose.yml
  20. 31
      docker/flyapps/docker-compose.only.yml
  21. 48
      docker/flyapps/docker-compose.yml
  22. 7
      docker/init/clean.sh
  23. 10
      docker/init/download.images
  24. 24
      docker/init/init.sh
  25. 1
      docker/init/timezone
  26. 43
      docker/mariadb/docker-compose.yml
  27. 108
      docker/mariadb/server.cnf
  28. 25
      docker/redis/docker-compose.yml
  29. 6
      docker/scripts/build.sh
  30. 9
      docker/scripts/start_all.sh
  31. 10
      docker/scripts/stop_all.sh
  32. 1
      fir_admin/.env.development
  33. 2
      fir_admin/.env.production
  34. 33
      fir_admin/src/api/app.js
  35. 43
      fir_admin/src/api/developer.js
  36. 9
      fir_admin/src/api/devices.js
  37. 20
      fir_admin/src/api/domain.js
  38. 20
      fir_admin/src/api/order.js
  39. 20
      fir_admin/src/api/report.js
  40. 13
      fir_admin/src/api/storage.js
  41. 8
      fir_admin/src/api/user.js
  42. 20
      fir_admin/src/api/wxbind.js
  43. 21
      fir_admin/src/router/index.js
  44. 15
      fir_admin/src/views/appinfos/AppDetail.vue
  45. 18
      fir_admin/src/views/appinfos/AppReleaseDetail.vue
  46. 20
      fir_admin/src/views/appinfos/AppReleaseList.vue
  47. 36
      fir_admin/src/views/appinfos/list.vue
  48. 15
      fir_admin/src/views/domain/Detail.vue
  49. 21
      fir_admin/src/views/domain/list.vue
  50. 2
      fir_admin/src/views/login/index.vue
  51. 15
      fir_admin/src/views/order/Detail.vue
  52. 18
      fir_admin/src/views/order/list.vue
  53. 15
      fir_admin/src/views/report/Detail.vue
  54. 14
      fir_admin/src/views/report/list.vue
  55. 15
      fir_admin/src/views/storage/Detail.vue
  56. 15
      fir_admin/src/views/storage/list.vue
  57. 117
      fir_admin/src/views/supersign/bill/add.vue
  58. 202
      fir_admin/src/views/supersign/bill/list.vue
  59. 15
      fir_admin/src/views/supersign/developer/Detail.vue
  60. 26
      fir_admin/src/views/supersign/developer/list.vue
  61. 13
      fir_admin/src/views/supersign/devices/list.vue
  62. 92
      fir_admin/src/views/userinfos/UserDetail.vue
  63. 5
      fir_admin/src/views/userinfos/list.vue
  64. 67
      fir_admin/src/views/wxbind/Detail.vue
  65. 31
      fir_admin/src/views/wxbind/list.vue
  66. 47
      fir_client/README.md
  67. 3
      fir_client/package.json
  68. 6
      fir_client/public/index.html
  69. 6
      fir_client/public/short.html
  70. BIN
      fir_client/src/assets/github.png
  71. BIN
      fir_client/src/assets/wxchat.jpg
  72. 11
      fir_client/src/components/FirBase.vue
  73. 320
      fir_client/src/components/FirDownload.vue
  74. 16
      fir_client/src/components/FirFooter.vue
  75. 9
      fir_client/src/components/FirHeader.vue
  76. 138
      fir_client/src/components/FirLogin.vue
  77. 27
      fir_client/src/components/FirRegist.vue
  78. 23
      fir_client/src/components/FirResetPwd.vue
  79. 307
      fir_client/src/components/ShortDownload.vue
  80. 73
      fir_client/src/components/apps/FirAppInfosBase.vue
  81. 5
      fir_client/src/components/apps/FirAppInfosbaseinfo.vue
  82. 493
      fir_client/src/components/apps/FirAppInfossecurity.vue
  83. 103
      fir_client/src/components/apps/FirAppInfossupersign.vue
  84. 65
      fir_client/src/components/apps/FirAppInfostimeline.vue
  85. 138
      fir_client/src/components/apps/FirApps.vue
  86. 286
      fir_client/src/components/base/AppleDeveloperBindApp.vue
  87. 61
      fir_client/src/components/base/BindDomain.vue
  88. 15
      fir_client/src/components/index/FirContact.vue
  89. 4
      fir_client/src/components/index/FirIndexBody.vue
  90. 9
      fir_client/src/components/index/FirIndexTem.vue
  91. 16
      fir_client/src/components/index/FirNews.vue
  92. 2180
      fir_client/src/components/user/FirSuperSignBase.vue
  93. 16
      fir_client/src/components/user/FirUserAdvert.vue
  94. 174
      fir_client/src/components/user/FirUserDomain.vue
  95. 470
      fir_client/src/components/user/FirUserDownload.vue
  96. 733
      fir_client/src/components/user/FirUserNotify.vue
  97. 67
      fir_client/src/components/user/FirUserOrders.vue
  98. 32
      fir_client/src/components/user/FirUserProfileCertification.vue
  99. 211
      fir_client/src/components/user/FirUserProfileInfo.vue
  100. 317
      fir_client/src/components/user/FirUserQrcode.vue
  101. Some files were not shown because too many files have changed in this diff Show More

5
.gitignore vendored

@ -9,3 +9,8 @@ fir_client/yarn.lock
fir_download/package-lock.json fir_download/package-lock.json
fir_client/src/assets/down_left.png fir_client/src/assets/down_left.png
fir_client/src/assets/down_right.png fir_client/src/assets/down_right.png
mailhtml/build/
mailhtml/node_modules/
mailhtml/.idea/
fir_client/dist_index/
fir_client/dist_short/

@ -1,144 +1,63 @@
##### 从git上面下载源码 ### 用与应用分发,苹果超级签名
``` #### 部署前准备
cd /data/ - 备案域名【至少需要一个域名,以下可通过子域名部署】
git clone https://github.com/nineaiyu/FlyApps - API域名
``` - 前端web域名
- 下载页域名
#### 超级签名依赖部署 - 下载页域名可配置多个
##### fastlane [相关文档](https://github.com/fastlane/fastlane/blob/master/spaceship/docs/DeveloperPortal.md) - 存储域名(使用阿里云oss存储)
``` - ssl证书
# fastlane - API域名证书
#1 gem ruby安装 - 存储域名证书(使用阿里云oss存储)
yum install gem -y - 前端web域名证书(可选)
yum install ruby-devel -y - Centos8Stream 服务器
yum install gcc gcc-c++ -y - 如果使用oss存储,则带宽为1M,若使用本地存储,则带宽越大越好
- 如果使用超级签,最低配置为2cpu 4G内存,若干不使用签名,则1cpu2G就行
#gem修改为国内镜像 - 阿里云短信或极光短信服务【可选一个,主要用与注册,重置密码】
gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/ - 阿里云短信
- 极光短信
gem sources -l - 邮箱服务【可选,用与注册,重置密码,通知信息】
#https://gems.ruby-china.com - 阿里云OSS存储【可选】
#确保只有 gems.ruby-china.com - [sts授权配置](https://help.aliyun.com/document_detail/100624.html)
- 阿里云CDN【可选,用与加速访问】
#2 更新rubby - 极验验证【可选,滑动验证服务】
gpg2 --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB - 微信公众号【可选,用与微信扫描登录】
- 阿里云支付【可选,用与购买下载次数】
#如果脚本执行失败,可以这样 - 微信支付【可选,用与购买下载次数】
# echo 199.232.68.133 raw.githubusercontent.com >> /etc/hosts
#### 自用搭建建议
curl -L get.rvm.io | bash -s stable - 阿里云服务器需要1cpu 2G内存,无需系统盘,如果使用超级签,可以适当增加配置
source /etc/profile.d/rvm.sh - 需要阿里云OSS存储和阿里云CDN,并且OSS存储和阿里云服务器部署同一个地区
rvm list known - 可以申请一个极验进行滑动验证,或者开启验证码验证
rvm install 2.7 - 阿里云备案域名:api和前端可以使用一个域名,下载页单独域名
#3 安装fastlane #### 部署必备资料
gem install fastlane - 域名证书
gem install pry - web域名和证书
- api域名和证书
# space 好像有点问题,需要把这个注释掉 - 下载页域名(可配置证书)
vim /usr/local/rvm/gems/ruby-2.7.0/gems/fastlane-2.146.1/spaceship/lib/spaceship/base.rb +298 - 存储域名和证书
#v.gsub!("\n", "\n\t") # to align nested elements #注释掉,要不然会报错 - 本地存储,则该域名和证书可以和api域名证书一致
- 阿里云oss存储
``` - 开启cdn,需要新域名和证书
##### isign - 不开启,无需域名和证书
``` - Centos8Stream 服务器
# python2.7
yum update python -y ## 部署方式
pip install -U setuptools pip
### 1.[Docker 部署](./doc/docker.md) 【推荐】
wget https://github.com/nineaiyu/isign/archive/v1.8.0.tar.gz
tar xvf v1.8.0.tar.gz ### 2.[本地部署](./doc/local.md)
cd isign-1.8.0/
### 功能预览
python setup.py build ![img_1.png](./doc/images/img_1.png)
python setup.py install ![img_2.png](./doc/images/img_2.png)
``` ![img_3.png](./doc/images/img_3.png)
![img_4.png](./doc/images/img_4.png)
### FlyApp 服务器搭建 [python3 环境] ![img_5.png](./doc/images/img_5.png)
![img_6.png](./doc/images/img_6.png)
![img_7.png](./doc/images/img_7.png)
##### 搭建python env 环境 ![img_8.png](./doc/images/img_8.png)
``` ![img_9.png](./doc/images/img_9.png)
yum install python36 python36-devel redis mariadb-server mariadb-devel -y ![img_10.png](./doc/images/img_10.png)
python3 -m venv py3 ![img_11.png](./doc/images/img_11.png)
source py3/bin/activate
```
###### 安装pip包
```
cd FlyApps/fir_ser/
pip install -r requirements.txt
```
###### 配置数据库
```
在 settings.py 配置redis 和 mysql 数据库
```
###### 迁移数据库
```
#如果是mysql,需要做一下配置,如果是sqlite,需要升级sqlite
#记得根据配置创建数据库
#create database flyapp default character set utf8 COLLATE utf8_general_ci;
#grant all on flyapp.* to flyuser@'127.0.0.1' identified by 'flypwd00oo.1';
python manage.py makemigrations
python manage.py migrate
```
### FlyApp web端搭建
###### npm编译环境
```
yum install npm
npm install -g n
n latest
npm install -g yarn
```
###### 编译web端
```
cd FlyApps/fir_client/
vim src/restful/index.js #修改api接口地址
yarn install
yarn build
```
###### 编译下载页面
```
cd FlyApps/fir_download/
vim src/restful/download.js #修改api接口地址
yarn install
yarn build
```
###### web目录操作
```
# web目录: /www/wwwroot/fly.dvcloud.xin/
cd /www/wwwroot/fly.dvcloud.xin/
cp -a /data/FlyApps/fir_client/dist/* .
cp -a /data/FlyApps/fir_download/dist/* .
```
##### 更新sqllite
```
tar xvf sqlite-autoconf-3310100.tar.gz
cd sqlite-autoconf-3310100
./configure --prefix=/usr/local/sqlite
make -j4
make install
rm -rf /usr/bin/sqlite3
ln -s /usr/local/sqlite/bin/sqlite3 /usr/bin/sqlite3
ll /usr/bin/sqlite3
echo "/usr/local/sqlite/lib" > /etc/ld.so.conf.d/sqlite3.conf
ldconfig
```

@ -1,12 +0,0 @@
#!/bin/bash
#
#
# shellcheck disable=SC2006
s_path=`pwd`
#first build fir_client
docker pull node:14.17.3
\cp -a build_client.sh fir_client/
docker run --rm -v "${s_path}"/fir_client:/fir_client -it node:14.17.3 sh /fir_client/build_client.sh
cd "${s_path}"/fir_ser/ && docker build . -t flyapps

@ -1,7 +0,0 @@
#!/bin/bash
#
#
cd /fir_client/ && yarn config set registry https://registry.npm.taobao.org && yarn install && yarn build

@ -0,0 +1,102 @@
### Docker最小化部署前准备,如果使用超级签和云存储,需要使用阿里云的服务器
系统|Centos 8 Stream 纯净系统 [2cpu 4G内存]
----|----
域名|```app.hehelucky.cn```
域名SSL证书| nginx 格式证书 [阿里云和腾讯云都可以申请免费ssl证书]
数据路径| ```/data```
### 开始部署
##### 1.从git上面下载源码
```
cd /data/
dnf install git -y
git clone https://github.com/nineaiyu/flyapps
```
## 注意,下面展示的相对路径,都是以```/data/flyapps```为相对目录
#### 2.docker环境安装
```
cd /data/flyapps/docker/init
sh init.sh
```
#### 3.配置域名和证书,如果有cdn或者oss,也要进行配置
a.将域名证书通过```sftp软件```或者```rz命令```复制到```/data/flyapps/nginx.conf.d```目录中
b.将域名证书分别重命名为 ```域名.pem``` 和 ```域名.key``` .本次使用的域名是 ```app.hehelucky.cn``` ,因此,证书名称类似如下
```shell script
nginx.conf.d/app.hehelucky.cn.key
nginx.conf.d/app.hehelucky.cn.pem
```
c.修改NGINX配置文件```nginx.conf.d/flyapps-vhost.conf```,将```server_name```字段修改为域名,将```ssl_certificate```和
```ssl_certificate_key```修改为对应证书路径,修改之后,对应字段如下
```shell
server_name app.hehelucky.cn;
ssl_certificate /etc/nginx/conf.d/app.hehelucky.cn.pem;
ssl_certificate_key /etc/nginx/conf.d/app.hehelucky.cn.key;
```
#### 4.配置api服务需要修改api和web域名,如果有需求,还可以配置 短信,邮箱,geetest,存储等信息
##### fir_ser配置文件 ```fir_ser/config.py```
```python
class DOMAINCONF(object):
API_DOMAIN = "https://app.hehelucky.cn" # 用与开启本地存储,上传应用配置
WEB_DOMAIN = "https://app.hehelucky.cn" # 用于超级签跳转配置,该域名一般为前端页面域名
```
##### fir_client配置文件 ```fir_client/vue.confjg.js```
```javascript
const pro_base_env = {
baseUrl: '/', //该选项可以填写web-api的域名,类似 https://api.xxx.com/
index_static: '/', //若配置cdn等加速,可以填写cdn加速域名
baseShortUrl: '/', //该选项可以填写short-api的域名,也可以和web-api域名一样,类似 https://api.xxx.com/
short_static: '/short/', //若配置cdn等加速,可以填写cdn加速域名
version: version,
};
```
##### 构建静态资源
```
cd /data/flyapps/docker/scripts
sh build.sh
```
##### 启动所有服务
```
cd /data/flyapps/docker/scripts
sh start_all.sh
```
##### 关闭所有服务
```
cd /data/flyapps/docker/scripts
sh stop_all.sh
```
##### 根据提示创建默认管理用户,用与访问管理后台
```shell
docker exec -it flyapps python manage.py createsuperuser
```
##### 测试访问
- 在浏览器输入自己配置的域名 https://app.hehelucky.cn/ 进行访问
- 管理后台访问 https://app.hehelucky.cn:3448/ 进行访问
##### 如果要使用本地的数据库和redis,api服务使用容器,需要修改文件```/data/flyapps/docker/flyapps/docker-compose.yml```
```shell
external_links:
- mariadb:mariadb
- redis:redis
```
修改为
```shell
extra_hosts:
- "mariadb:172.31.31.1"
- "redis:172.31.31.1"
```
并将```nginx:```下面的配置注释掉
同时还需要修改mariadb和redis授权

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

@ -0,0 +1,166 @@
### 本地部署前准备
系统 | Centos 8 Stream [2cpu 4G内存]
----|----
域名|```app.hehelucky.cn```
域名SSL证书| nginx 格式证书 [阿里云和腾讯云都可以申请免费ssl证书]
数据路径| ```/data```
##### 从git上面下载源码
```shell
cd /data/
dnf install git gcc zip unzip -y
git clone https://github.com/nineaiyu/flyapps
```
### flyapps 本地部署 [python3 环境]
##### 搭建python env 环境
```shell
dnf install python39 python39-devel redis mariadb-server mariadb-devel -y
python3.9 -m venv py39
source py39/bin/activate
```
###### 安装pip包
```shell
cd /data/flyapps/fir_ser/
pip install -U setuptools pip -i https://pypi.tuna.tsinghua.edu.cn/simple/
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
```
#### 配置数据库
##### 启动mariadb数据库
```shell
systemctl restart mariadb
systemctl enable mariadb
```
##### 创建mysql数据库
```shell
mysql
```
执行下面mysql命令
```mariadb
create database flyapps default character set utf8 COLLATE utf8_general_ci;
grant all on flyapps.* to flyuser@'127.0.0.1' identified by 'KGzKjZpWBp4R4RSa';
```
##### 启动redis数据库
```shell
echo 'requirepass nineven' >> /etc/redis.conf # 配置授权密码
systemctl restart redis
systemctl enable redis
```
### flyapps zsign ipa重签名工具安装
```shell
dnf install openssl-devel -y
dnf install gcc-c++ gcc -y
cd /data/flyapps/fir_ser/
tar xvf zsign-1.1.2.tar.gz
cd zsign-1.1.2/
g++ *.cpp common/*.cpp -lcrypto -O3 -std=c++11 -o /usr/bin/zsign
```
#### 配置api服务需要修改api和web域名,如果有需求,还可以配置 短信,邮箱,geetest,存储等信息
##### fir_ser配置文件 ```fir_ser/config.py```
```python
class DOMAINCONF(object):
API_DOMAIN = "https://app.hehelucky.cn" # 用与开启本地存储,上传应用配置
WEB_DOMAIN = "https://app.hehelucky.cn" # 用于超级签跳转配置,该域名一般为前端页面域名
```
##### fir_client配置文件 ```fir_client/vue.confjg.js```
```javascript
const pro_base_env = {
baseUrl: '/', //该选项可以填写web-api的域名,类似 https://api.xxx.com/
index_static: '/', //若配置cdn等加速,可以填写cdn加速域名
baseShortUrl: '/', //该选项可以填写short-api的域名,也可以和web-api域名一样,类似 https://api.xxx.com/
short_static: '/short/', //若配置cdn等加速,可以填写cdn加速域名
version: version,
};
```
### flyapps web端搭建
###### npm编译环境
```shell
dnf install npm -y
npm install -g n
n 12.13 # 安装12版本的node或者14版本 ,最新版本会有问题
npm install -g yarn
```
###### 编译web端和下载页
```shell
cd /data/flyapps/fir_client/
vim vue.config.js #修改api接口地址
# pro_base_env 正式环境信息
# dev_base_env 开发环境信息
yarn install
yarn build index # 前端打包,输出目录 dist_index
yarn build short # 下载页打包,输出目录 dist_short
```
###### 编译web管理后台
```shell
cd /data/flyapps/fir_admin/
yarn install
yarn build:prod # 下载页打包,输出目录 dist
```
### nginx配置
##### 安装nginx
```shell
dnf install nginx -y
```
##### 将域名ssl证书解压到```/etc/nginx/conf.d/``` 目录中,并重命名为```域名.pem```,```域名.key```格式
##### 拷贝虚拟主机配置文件
```shell
cp /data/flyapps/nginx.conf.d/flyapps-vhost.conf /etc/nginx/conf.d/
```
1.创建web目录,并将打包好的web复制该目录
```shell
mkdir -pv /data/{fir_client/short,fir_admin}
cp -a /data/flyapps/fir_client/dist_index/* /data/fir_client/
cp -a /data/flyapps/fir_client/dist_short/* /data/fir_client/short/
cp -a /data/flyapps/fir_admin/dist/* /data/fir_admin/
chown nginx.nginx -R /data/{fir_client,fir_admin}
```
2.启动nginx服务
```shell
nginx -t
systemctl restart nginx
systemctl enable nginx
```
##### 添加redis和mariadb uwsgi本地解析
```shell
echo '127.0.0.1 mariadb redis flyapps' >> /etc/hosts
```
###### 迁移数据库
```shell
cd /data/flyapps/fir_ser/
python manage.py makemigrations
python manage.py migrate
```
#### 启动api服务
```shell
echo 'net.core.somaxconn=1024' >> /etc/sysctl.conf
sysctl -p
cd /data/flyapps/fir_ser/
python manage.py start all -u nginx -usm 1 -d
```
##### 根据提示创建默认管理用户,用与访问管理后台
```shell
python manage.py createsuperuser
```
- 需要输入用户名,邮箱和密码
- 用户名和密码用与登录管理后台
- 邮箱和密码用与登录前端web

@ -1,51 +0,0 @@
##### 从git上面下载源码
```
cd /data/
git clone https://github.com/nineaiyu/FlyApps
```
#### docker环境安装 [centos7]
```
yum install epel-release -y
yum clean all && yum makecache
yum install docker -y
```
#### 配置域名和证书,如果有cdn或者oss,也要进行配置
```shell script
nginx.conf.d/app.hehelucky.cn.key
nginx.conf.d/app.hehelucky.cn.pem
nginx.conf.d/flyapps-vhost.conf
```
### api服务需要修改api和web域名,短信,邮箱,geetest,存储等信息
```shell script
vim fir_ser/config.py
API_DOMAIN = "https://app.hehelucky.cn"
WEB_DOMAIN = "https://app.hehelucky.cn"
MOBILEPROVISION = "https://ali-static.jappstore.com/embedded.mobileprovision"
```
### web页面需要修改指定api域名
```vuejs
vim fir_client/vue.config.js
const pro_base_env = {
baseUrl: 'https://flyapps.cn',
index_static: 'https://static.flyapps.cn/index/',
baseShortUrl: 'https://flyapps.top',
short_static: 'https://static.flyapps.top/short/',
};
```
##### 构建静态资源和api服务
```
sh build.sh
```
##### 启动所有服务
```
sh start.sh
```

@ -0,0 +1,67 @@
version: '3.2'
services:
buildflyapps:
image: 'flyapps'
build:
context: ../../fir_ser
dockerfile: Dockerfile
command:
- echo
- success
buildclient:
container_name: buildclient
image: 'node:14.17.3'
working_dir: /fir_client
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_client:/fir_client
- ../../data/web/:/web
command:
- bash
- -c
- yarn config set registry https://registry.npm.taobao.org && yarn install && yarn build index && yarn build short && ([ ! -e /web/fir_client ] && \cp -a dist_index /web/fir_client || \cp -a dist_index/* /web/fir_client/) && ([ ! -e /web/fir_client/short ] && \cp -a dist_short /web/fir_client/short || \cp -a dist_short/* /web/fir_client/short/) && echo build success
buildindex:
container_name: buildindex
image: 'node:14.17.3'
working_dir: /fir_client
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_client:/fir_client
- ../../data/web/:/web
command:
- bash
- -c
- yarn config set registry https://registry.npm.taobao.org && yarn install && yarn build index && ([ ! -e /web/fir_index ] && \cp -a dist_index /web/fir_index || \cp -a dist_index/* /web/fir_index/) && echo build success
buildshort:
container_name: buildshort
image: 'node:14.17.3'
working_dir: /fir_client
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_client:/fir_client
- ../../data/web/:/web
command:
- sh
- -c
- yarn config set registry https://registry.npm.taobao.org && yarn install && yarn build short && ([ ! -e /web/fir_short ] && \cp -a dist_short /web/fir_short || \cp -a dist_short/* /web/fir_short/) && echo build success
buildadmin:
container_name: buildadmin
image: 'node:14.17.3'
working_dir: /fir_admin
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_admin:/fir_admin
- ../../data/web/:/web
command:
- sh
- -c
- yarn config set registry https://registry.npm.taobao.org && yarn install && yarn build:prod && ([ ! -e /web/fir_admin ] && \cp -a dist /web/fir_admin || \cp -a dist/* /web/fir_admin/) && echo build success

@ -0,0 +1,31 @@
version: '3'
networks:
flyapps:
external: true
name: flyapps
services:
flyapps:
container_name: flyapps
restart: always
image: 'nineven/flyapps'
working_dir: /data/fir_ser
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_ser:/data/fir_ser
- ../../data/flyapps/files:/data/fir_ser/files
- ../../data/logs/flyapps/:/data/fir_ser/logs
- ../../data/flyapps/supersign/:/data/fir_ser/supersign
- ../../nginx.conf.d:/data/cert
networks:
flyapps:
ipv4_address: 172.31.31.100
external_links:
- mariadb:mariadb
- redis:redis
sysctls:
- net.core.somaxconn=4096
extra_hosts:
- "mariadb:172.31.31.1"
- "redis:172.31.31.1"

@ -0,0 +1,48 @@
version: '3'
networks:
flyapps:
external: true
name: flyapps
services:
flyapps:
container_name: flyapps
restart: always
image: 'nineven/flyapps'
working_dir: /data/fir_ser
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_ser:/data/fir_ser
- ../../data/flyapps/files:/data/fir_ser/files
- ../../data/logs/flyapps/:/data/fir_ser/logs
- ../../data/flyapps/supersign/:/data/fir_ser/supersign
- ../../nginx.conf.d:/data/cert
networks:
flyapps:
ipv4_address: 172.31.31.100
external_links:
- mariadb:mariadb
- redis:redis
sysctls:
- net.core.somaxconn=4096
nginx:
container_name: nginx
restart: always
image: 'nginx:1.21.3'
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../data/web:/data
- ../../nginx.conf.d:/etc/nginx/conf.d
- ../../data/logs/nginx:/var/log/nginx
networks:
flyapps:
ipv4_address: 172.31.31.200
ports:
- 80:80
- 443:443
- 3448:3448
external_links:
- flyapps:flyapps
depends_on:
- flyapps

@ -0,0 +1,7 @@
#!/bin/bash
#
#
for i in nginx flyapps mariadb redis buildclient buildshort buildadmin ;do echo $i;docker rm -f $i;done
docker network rm flyapps

@ -0,0 +1,10 @@
#!/bin/bash
#
#
docker pull 'bitnami/mariadb:10.7.3'
docker pull 'bitnami/redis:6.2.7'
docker pull 'nginx:1.21.3'
docker pull 'nineven/flyapps:latest'
docker pull 'node:14.17.3'

@ -0,0 +1,24 @@
#!/bin/bash
#
#
which dockerd
if [ $? -ne 0 ];then
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
dnf install docker-ce -y
fi
#which docker-compose
#if [ $? -ne 0 ];then
# curl -L https://get.daocloud.io/docker/compose/releases/download/v2.5.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
#fi
data_path="$(dirname $(dirname `pwd`))/data"
mkdir -pv ${data_path}/{flyapps/files,web,mariadb,redis,logs/{mariadb,nginx,flyapps}}
\cp $(dirname $(dirname `pwd`))/fir_ser/files/head_img.jpeg ${data_path}/flyapps/files/
chown 1001.1001 -R ${data_path}/{flyapps,web,mariadb,redis,logs/{mariadb,nginx,flyapps}}
chown 101.101 -R ${data_path}/{flyapps,logs/flyapps}
systemctl start docker && docker network create flyapps --driver bridge --subnet=172.31.31.0/24 --gateway=172.31.31.1
systemctl enable docker
systemctl status docker

@ -0,0 +1 @@
Asia/Shanghai

@ -0,0 +1,43 @@
version: '3.2'
networks:
flyapps:
external: true
name: flyapps
services:
mariadb:
image: bitnami/mariadb:10.7.3
container_name: mariadb
restart: always
environment:
- MARIADB_ROOT_PASSWORD=rootIPD.xx2.19
- MARIADB_DATABASE=flyapps
- MARIADB_USER=flyuser
- MARIADB_PASSWORD=KGzKjZpWBp4R4RSa
#- ALLOW_EMPTY_PASSWORD=yes
- MARIADB_ENABLE_SLOW_QUERY=1
- MARIADB_LONG_QUERY_TIME=3
- MARIADB_SKIP_TEST_DB=yes
- MARIADB_EXTRA_FLAGS=--max-connect-errors=3000 --max_connections=30000
ports:
- 3306:3306
networks:
flyapps:
ipv4_address: 172.31.31.90
volumes:
- ../init/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ../../data/mariadb:/bitnami/mariadb/data
- ../../data/logs/mariadb:/data/logs/mariadb
- ./server.cnf:/opt/bitnami/mariadb/conf/my_custom.cnf:ro
# adminer:
# image: adminer
# ports:
# - 8080:8080
# networks:
# - fungame

@ -0,0 +1,108 @@
#
# These groups are read by MariaDB server.
# Use it for options that only the server (but not clients) should see
#
# See the examples of server my.cnf files in /usr/share/mysql/
#
# this is only for the mysqld standalone daemon
[mysqld]
binlog_cache_size = 192K
thread_stack = 384K
join_buffer_size = 4096K
query_cache_type = 1
max_heap_table_size = 1024M
default_storage_engine = InnoDB
performance_schema_max_table_instances = 400
table_definition_cache = 400
skip-external-locking
key_buffer_size = 512M
max_allowed_packet = 1G
table_open_cache = 1024
sort_buffer_size = 2048K
net_buffer_length = 4K
read_buffer_size = 2048K
read_rnd_buffer_size = 1024K
myisam_sort_buffer_size = 16M
thread_cache_size = 192
query_cache_size = 256M
tmp_table_size = 1024M
sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
max_connections = 30000
max_connect_errors = 1000
open_files_limit = 65535
expire_logs_days = 10
#log_queries_not_using_indexes=on
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
init_connect='SET NAMES utf8mb4'
#character-set-server=utf8
skip_name_resolve
event_scheduler=1
#skip-grant-tables
#innodb_data_home_dir = /var/lib/mysql/
#innodb_data_file_path = ibdata1:10M:autoextend
#innodb_log_group_home_dir = /var/lib/mysql/
#innodb_buffer_pool_size = 1024M
#innodb_log_file_size = 128M
#innodb_log_buffer_size = 32M
#innodb_flush_log_at_trx_commit = 1
#innodb_lock_wait_timeout = 50
#innodb_max_dirty_pages_pct = 90
#innodb_read_io_threads = 4
#innodb_write_io_threads = 4
#
# Allow server to accept connections on all interfaces.
#
#bind-address=0.0.0.0
#
# this is only for embedded server
# This group is only read by MariaDB servers, not by MySQL.
# If you use the same .cnf file for MySQL and MariaDB,
# you can put MariaDB-only options here
[mariadb]
#autoset_open_files_limit
#enable_slow_query_log
#audit
plugin_load_add=server_audit
server_audit_logging=on
server_audit_events=connect,query
server_audit=force_plus_permanent
server_audit_events=QUERY_DDL,QUERY_DML,CONNECT
server_audit_output_type=file
server_audit_file_rotate_now=on
server_audit_file_rotations=9
server_audit_file_rotate_size=1G
server_audit_file_path=/data/logs/mariadb
log_error=/data/logs/mariadb/mariadb.err.log
log_output=FILE
slow_query_log
long_query_time=3
slow_query_log_file=/data/logs/mariadb/mariadb-slow.log
log_queries_not_using_indexes=ON #Logging Queries That Don't Use Indexes
#server_id=2
log-bin=/data/logs/mariadb/mysql-bin
# This group is only read by MariaDB-10.5 servers.
# If you use the same .cnf file for MariaDB of different versions,
# use this group for options that older servers don't understand

@ -0,0 +1,25 @@
version: '3.2'
networks:
flyapps:
external: true
name: flyapps
services:
redis:
image: 'bitnami/redis:6.2.7'
container_name: redis
restart: always
volumes:
- ../init/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ../../data/redis:/bitnami/redis/data
environment:
#- REDIS_REPLICATION_MODE=master
#- ALLOW_EMPTY_PASSWORD=yes
- REDIS_PASSWORD=nineven
networks:
- flyapps
#ports:
# - 6379:6379

@ -0,0 +1,6 @@
#!/bin/bash
#
#
cd ../build/
docker compose up buildclient buildadmin

@ -0,0 +1,9 @@
#!/bin/bash
#
#
cd ../mariadb/ && docker compose up -d
cd ../redis/ && docker compose up -d
cd ../flyapps/ && docker compose up -d
docker logs -f flyapps

@ -0,0 +1,10 @@
#!/bin/bash
#
#
cd ../flyapps/ && docker compose down
cd ../redis/ && docker compose down
cd ../mariadb/ && docker compose down

@ -3,3 +3,4 @@ ENV = 'development'
# base api # base api
VUE_APP_BASE_API = 'https://flyapps.cn/api/v3/fir/server' VUE_APP_BASE_API = 'https://flyapps.cn/api/v3/fir/server'
#VUE_APP_BASE_API = 'https://app.hehelucky.cn/api/v3/fir/server'

@ -2,5 +2,5 @@
ENV = 'production' ENV = 'production'
# base api # base api
VUE_APP_BASE_API = '/prod-api' VUE_APP_BASE_API = '/api/v3/fir/server'

@ -1,6 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
export function getAppInfos(query) { export function getAppList(query) {
return request({ return request({
url: '/app/info', url: '/app/info',
method: 'get', method: 'get',
@ -8,23 +8,29 @@ export function getAppInfos(query) {
}) })
} }
export function updateAppInfo(data) { export function getAppInfos(pk) {
return request({ return request({
url: '/app/info', url: '/app/info/' + pk,
method: 'get'
})
}
export function updateAppInfo(pk, data) {
return request({
url: '/app/info/' + pk,
method: 'put', method: 'put',
data data
}) })
} }
export function deleteApp(data) { export function deleteApp(pk) {
return request({ return request({
url: '/app/info', url: '/app/info/' + pk,
method: 'delete', method: 'delete'
data
}) })
} }
export function getAppReleaseInfos(query) { export function getAppReleaseList(query) {
return request({ return request({
url: '/app/release/info', url: '/app/release/info',
method: 'get', method: 'get',
@ -32,9 +38,16 @@ export function getAppReleaseInfos(query) {
}) })
} }
export function updateReleaseAppInfo(data) { export function getAppReleaseInfos(pk) {
return request({ return request({
url: '/app/release/info', url: '/app/release/info/' + pk,
method: 'get'
})
}
export function updateReleaseAppInfo(pk, data) {
return request({
url: '/app/release/info/' + pk,
method: 'put', method: 'put',
data data
}) })

@ -1,6 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
export function getDeveloperInfo(query) { export function getDeveloperList(query) {
return request({ return request({
url: '/developer/info', url: '/developer/info',
method: 'get', method: 'get',
@ -8,10 +8,47 @@ export function getDeveloperInfo(query) {
}) })
} }
export function updatedeveloperInfo(data) { export function getDeveloperInfo(pk) {
return request({ return request({
url: '/developer/info', url: '/developer/info/' + pk,
method: 'get'
})
}
export function updatedeveloperInfo(pk, data) {
return request({
url: '/developer/info/' + pk,
method: 'put', method: 'put',
data data
}) })
} }
export function getBillList(query) {
return request({
url: '/bill/info',
method: 'get',
params: query
})
}
export function addBillInfo(data) {
return request({
url: '/bill/info',
method: 'post',
data
})
}
export function delBillInfo(pk) {
return request({
url: '/bill/info/' + pk,
method: 'delete'
})
}
export function getUserBillInfo(query) {
return request({
url: '/bill/userinfo',
method: 'get',
params: query
})
}

@ -1,6 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
export function getDevicesInfo(query) { export function getDevicesList(query) {
return request({ return request({
url: '/devices/info', url: '/devices/info',
method: 'get', method: 'get',
@ -8,10 +8,3 @@ export function getDevicesInfo(query) {
}) })
} }
export function updatedevicesInfo(data) {
return request({
url: '/devices/info',
method: 'put',
data
})
}

@ -1,6 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
export function getDomainInfos(query) { export function getDomainList(query) {
return request({ return request({
url: '/domain/info', url: '/domain/info',
method: 'get', method: 'get',
@ -8,18 +8,24 @@ export function getDomainInfos(query) {
}) })
} }
export function updateDomainInfo(data) { export function getDomainInfos(pk) {
return request({ return request({
url: '/domain/info', url: '/domain/info/' + pk,
method: 'get'
})
}
export function updateDomainInfo(pk, data) {
return request({
url: '/domain/info/' + pk,
method: 'put', method: 'put',
data data
}) })
} }
export function deleteDomain(data) { export function deleteDomain(pk) {
return request({ return request({
url: '/domain/info', url: '/domain/info/' + pk,
method: 'delete', method: 'delete'
data
}) })
} }

@ -1,6 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
export function getOrderInfo(query) { export function getOrderList(query) {
return request({ return request({
url: '/order/info', url: '/order/info',
method: 'get', method: 'get',
@ -8,18 +8,24 @@ export function getOrderInfo(query) {
}) })
} }
export function updateOrderInfo(data) { export function getOrderInfo(pk) {
return request({ return request({
url: '/order/info', url: '/order/info/' + pk,
method: 'get'
})
}
export function updateOrderInfo(pk, data) {
return request({
url: '/order/info/' + pk,
method: 'put', method: 'put',
data data
}) })
} }
export function deleteOrderInfo(data) { export function deleteOrderInfo(pk) {
return request({ return request({
url: '/order/info', url: '/order/info/' + pk,
method: 'delete', method: 'delete'
data
}) })
} }
export function createOrderInfo(data) { export function createOrderInfo(data) {

@ -1,6 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
export function getAppReportIfo(query) { export function getAppReportList(query) {
return request({ return request({
url: '/report/info', url: '/report/info',
method: 'get', method: 'get',
@ -8,18 +8,24 @@ export function getAppReportIfo(query) {
}) })
} }
export function updateAppReportIfo(data) { export function getAppReportIfo(pk) {
return request({ return request({
url: '/report/info', url: '/report/info/' + pk,
method: 'get'
})
}
export function updateAppReportIfo(pk, data) {
return request({
url: '/report/info/' + pk,
method: 'put', method: 'put',
data data
}) })
} }
export function deleteAppReportIfo(data) { export function deleteAppReportIfo(pk) {
return request({ return request({
url: '/report/info', url: '/report/info/' + pk,
method: 'delete', method: 'delete'
data
}) })
} }

@ -1,6 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
export function getStorageInfo(query) { export function getStorageList(query) {
return request({ return request({
url: '/storage/info', url: '/storage/info',
method: 'get', method: 'get',
@ -8,9 +8,16 @@ export function getStorageInfo(query) {
}) })
} }
export function updateStorageInfo(data) { export function getStorageInfo(pk) {
return request({ return request({
url: '/storage/info', url: '/storage/info/' + pk,
method: 'get'
})
}
export function updateStorageInfo(pk, data) {
return request({
url: '/storage/info/' + pk,
method: 'put', method: 'put',
data data
}) })

@ -53,3 +53,11 @@ export function updateCertificationInfo(data) {
data data
}) })
} }
export function resetPassword(data) {
return request({
url: '/user/info',
method: 'post',
data
})
}

@ -1,6 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
export function getWxBindInfos(query) { export function getWxBindList(query) {
return request({ return request({
url: '/wxbind/info', url: '/wxbind/info',
method: 'get', method: 'get',
@ -8,18 +8,24 @@ export function getWxBindInfos(query) {
}) })
} }
export function updateWxBindInfo(data) { export function getWxBindInfos(pk) {
return request({ return request({
url: '/wxbind/info', url: '/wxbind/info/' + pk,
method: 'get'
})
}
export function updateWxBindInfo(pk,data) {
return request({
url: '/wxbind/info/' + pk,
method: 'put', method: 'put',
data data
}) })
} }
export function deleteWxBind(data) { export function deleteWxBind(pk) {
return request({ return request({
url: '/wxbind/info', url: '/wxbind/info/' + pk,
method: 'delete', method: 'delete'
data
}) })
} }

@ -278,6 +278,27 @@ export const constantRoutes = [
meta: { title: '设备消耗', icon: 'form' } meta: { title: '设备消耗', icon: 'form' }
} }
] ]
},
{
path: 'bill',
name: 'bill',
redirect: '/supersign/bill/list',
component: () => import('@/views/supersign/index'),
meta: { title: '设备账单', icon: 'form' },
children: [
{
path: 'list',
name: 'bill_info_list',
component: () => import('@/views/supersign/bill/list'),
meta: { title: '消耗账单', icon: 'form' }
},
{
path: 'add',
name: 'bill_info_add',
component: () => import('@/views/supersign/bill/add'),
meta: { title: '设备充值', icon: 'form' }
}
]
} }
] ]
}, },

@ -264,29 +264,30 @@ export default {
return { return {
postForm: Object.assign({}, defaultForm), postForm: Object.assign({}, defaultForm),
loading: false, loading: false,
is_edit: false is_edit: false,
id: ''
} }
}, },
computed: { computed: {
}, },
created() { created() {
if (this.isEdit) { if (this.isEdit) {
const id = this.$route.params && this.$route.params.id this.id = this.$route.params && this.$route.params.id
this.fetchData(id) this.fetchData(this.id)
} }
}, },
methods: { methods: {
fetchData(id) { fetchData(id) {
getAppInfos({ id: id }).then(response => { getAppInfos(id).then(response => {
if (response.data.length === 1) { if (response.data) {
this.postForm = response.data[0] this.postForm = response.data
} }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
}) })
}, },
updateData() { updateData() {
updateAppInfo(this.postForm).then(response => { updateAppInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功') this.$message.success('更新成功')
this.postForm = response.data this.postForm = response.data
}).catch(err => { }).catch(err => {

@ -161,28 +161,28 @@ export default {
return { return {
postForm: Object.assign({}, defaultForm), postForm: Object.assign({}, defaultForm),
loading: false, loading: false,
is_edit: false is_edit: false,
id: ''
} }
}, },
computed: { computed: {
}, },
created() { created() {
const id = this.$route.params && this.$route.params.id this.id = this.$route.params && this.$route.params.id
const app_id = this.$route.params && this.$route.params.app_id this.fetchData(this.id)
this.fetchData(id, app_id)
}, },
methods: { methods: {
fetchData(id, app_id) { fetchData(id) {
getAppReleaseInfos({ id: id, app_id: app_id }).then(response => { getAppReleaseInfos(id).then(response => {
if (response.data.length === 1) { if (response.data) {
this.postForm = response.data[0] this.postForm = response.data
} }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
}) })
}, },
updateData() { updateData() {
updateReleaseAppInfo(this.postForm).then(response => { updateReleaseAppInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功') this.$message.success('更新成功')
this.postForm = response.data this.postForm = response.data
}).catch(err => { }).catch(err => {

@ -2,7 +2,7 @@
<div class="app-container"> <div class="app-container">
<div class="filter-container"> <div class="filter-container">
<el-input v-model="listQuery.release_id" placeholder="release_id" style="width: 250px;" class="filter-item" clearable @keyup.enter.native="handleFilter" /> <el-input v-model="listQuery.release_id" placeholder="release_id" style="width: 250px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> <el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select> </el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -86,7 +86,7 @@
</template> </template>
<script> <script>
import { getAppReleaseInfos, downloadAppReleaseInfos } from '@/api/app' import { downloadAppReleaseInfos, getAppReleaseList } from '@/api/app'
import { baseFilter } from '@/utils' import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive import waves from '@/directive/waves' // waves directive
@ -135,11 +135,9 @@ export default {
app_id: undefined, app_id: undefined,
download_token: undefined, download_token: undefined,
release_id: undefined, release_id: undefined,
sort: '-created_time' ordering: '-created_time'
}, },
sortOptions, sortOptions
type_choices: [],
status_choices: []
} }
}, },
created() { created() {
@ -168,13 +166,9 @@ export default {
}, },
fetchData() { fetchData() {
this.listLoading = true this.listLoading = true
getAppReleaseInfos(this.listQuery).then(response => { getAppReleaseList(this.listQuery).then(response => {
this.list = response.data this.list = response.data.results
if (this.list && this.list.length > 0) { this.total = response.data.count
this.type_choices = this.list[0].type_choices
this.status_choices = this.list[0].status_choices
}
this.total = response.total
this.listLoading = false this.listLoading = false
}) })
} }

@ -4,7 +4,7 @@
<el-input v-model="listQuery.bundle_id" placeholder="Bundle_Id" style="width: 250px;" class="filter-item" clearable @keyup.enter.native="handleFilter" /> <el-input v-model="listQuery.bundle_id" placeholder="Bundle_Id" style="width: 250px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.name" placeholder="应用名称" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" /> <el-input v-model="listQuery.name" placeholder="应用名称" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.short" placeholder="短连接" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" /> <el-input v-model="listQuery.short" placeholder="短连接" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.domain_name" placeholder="应用专属访问域名" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" /> <!-- <el-input v-model="listQuery.domain_name" placeholder="应用专属访问域名" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />-->
<el-select v-if="type_choices" v-model="listQuery.type" placeholder="应用类型" clearable class="filter-item" style="width: 120px" @change="handleFilter"> <el-select v-if="type_choices" v-model="listQuery.type" placeholder="应用类型" clearable class="filter-item" style="width: 120px" @change="handleFilter">
<el-option v-for="item in type_choices" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
@ -12,7 +12,7 @@
<el-option v-for="item in status_choices" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in status_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
<el-input v-model="listQuery.user_id" placeholder="用户ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" /> <el-input v-model="listQuery.user_id" placeholder="用户ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> <el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select> </el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -40,7 +40,11 @@
</el-table-column> </el-table-column>
<el-table-column label="应用名称"> <el-table-column label="应用名称">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.name }} <el-tooltip content="点击访问下载页">
<el-link :underline="false" @click="go_download_url(scope.row)">
{{ scope.row.name }}
</el-link>
</el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="Bundle_Id" align="center"> <el-table-column label="Bundle_Id" align="center">
@ -71,11 +75,16 @@
{{ scope.row.short }} {{ scope.row.short }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="下载次数" width="100" align="center"> <el-table-column label="下载次数" width="80" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.count_hits }} {{ scope.row.count_hits }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="当天下载次数" width="80" align="center">
<template slot-scope="scope">
{{ scope.row.today_hits_count }}
</template>
</el-table-column>
<el-table-column label="应用大小" width="80" align="center"> <el-table-column label="应用大小" width="80" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.master_release.binary_size }} {{ scope.row.master_release.binary_size }}
@ -126,13 +135,13 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" /> <pagination v-show="count>0" :total="count" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" />
</div> </div>
</template> </template>
<script> <script>
import { getAppInfos, deleteApp } from '@/api/app' import { deleteApp, getAppList } from '@/api/app'
import { baseFilter } from '@/utils' import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive import waves from '@/directive/waves' // waves directive
@ -180,13 +189,13 @@ export default {
return { return {
list: null, list: null,
listLoading: true, listLoading: true,
total: 0, count: 0,
listQuery: { listQuery: {
page: 1, page: 1,
limit: 10, limit: 10,
name: undefined, name: undefined,
bundle_id: undefined, bundle_id: undefined,
sort: '-count_hits', ordering: '-count_hits',
type: undefined, type: undefined,
domain_name: undefined, domain_name: undefined,
user_id: undefined user_id: undefined
@ -202,6 +211,9 @@ export default {
this.fetchData() this.fetchData()
}, },
methods: { methods: {
go_download_url(app_info) {
window.open(app_info.preview_url, '_blank', '')
},
deleteApp(app_id) { deleteApp(app_id) {
this.$confirm('此操作将永久删除该应用, 是否继续?', '提示', { this.$confirm('此操作将永久删除该应用, 是否继续?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
@ -209,7 +221,7 @@ export default {
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
this.listLoading = true this.listLoading = true
deleteApp({ id: app_id }).then(response => { deleteApp(app_id).then(() => {
this.$message.success('删除成功') this.$message.success('删除成功')
this.fetchData() this.fetchData()
this.listLoading = false this.listLoading = false
@ -227,13 +239,13 @@ export default {
}, },
fetchData() { fetchData() {
this.listLoading = true this.listLoading = true
getAppInfos(this.listQuery).then(response => { getAppList(this.listQuery).then(response => {
this.list = response.data this.list = response.data.results
if (this.list && this.list.length > 0) { if (this.list && this.list.length > 0) {
this.type_choices = this.list[0].type_choices this.type_choices = this.list[0].type_choices
this.status_choices = this.list[0].status_choices this.status_choices = this.list[0].status_choices
} }
this.total = response.total this.count = response.data.count
this.listLoading = false this.listLoading = false
}) })
} }

@ -107,26 +107,27 @@ export default {
postForm: Object.assign({}, defaultForm), postForm: Object.assign({}, defaultForm),
loading: false, loading: false,
is_edit: false, is_edit: false,
domain_state_choices domain_state_choices,
id: ''
} }
}, },
computed: {}, computed: {},
created() { created() {
const id = this.$route.params && this.$route.params.id this.id = this.$route.params && this.$route.params.id
this.fetchData(id) this.fetchData(this.id)
}, },
methods: { methods: {
fetchData(id) { fetchData(id) {
getDomainInfos({ id: id }).then(response => { getDomainInfos(id).then(response => {
if (response.data.length === 1) { if (response.data) {
this.postForm = response.data[0] this.postForm = response.data
} }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
}) })
}, },
updateData() { updateData() {
updateDomainInfo(this.postForm).then(response => { updateDomainInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功') this.$message.success('更新成功')
this.postForm = response.data this.postForm = response.data
}).catch(err => { }).catch(err => {

@ -10,7 +10,7 @@
<el-select v-model="listQuery.is_enable" placeholder="域名绑定状态" clearable class="filter-item" style="width: 140px" @change="handleFilter"> <el-select v-model="listQuery.is_enable" placeholder="域名绑定状态" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in domain_state_choices" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in domain_state_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> <el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select> </el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -85,7 +85,7 @@
</template> </template>
<script> <script>
import { getDomainInfos, deleteDomain } from '@/api/domain' import { deleteDomain, getDomainList } from '@/api/domain'
import { baseFilter } from '@/utils' import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive import waves from '@/directive/waves' // waves directive
@ -143,7 +143,7 @@ export default {
page: 1, page: 1,
limit: 10, limit: 10,
user_id: undefined, user_id: undefined,
sort: '-created_time', ordering: '-created_time',
domain_name: undefined, domain_name: undefined,
app_name: undefined, app_name: undefined,
domain_type: undefined, domain_type: undefined,
@ -164,13 +164,8 @@ export default {
}, },
methods: { methods: {
remove_domain(domain_id) { remove_domain(domain_id) {
deleteDomain({ id: domain_id }).then(response => { deleteDomain(domain_id).then(response => {
this.list = response.data this.fetchData()
if (this.list && this.list.length > 0) {
this.domain_type_choices = this.list[0].domain_type_choices
}
this.total = response.total
this.listLoading = false
}) })
}, },
handleFilter() { handleFilter() {
@ -179,12 +174,12 @@ export default {
}, },
fetchData() { fetchData() {
this.listLoading = true this.listLoading = true
getDomainInfos(this.listQuery).then(response => { getDomainList(this.listQuery).then(response => {
this.list = response.data this.list = response.data.results
if (this.list && this.list.length > 0) { if (this.list && this.list.length > 0) {
this.domain_type_choices = this.list[0].domain_type_choices this.domain_type_choices = this.list[0].domain_type_choices
} }
this.total = response.total this.total = response.data.count
this.listLoading = false this.listLoading = false
}) })
} }

@ -66,7 +66,7 @@ export default {
} }
} }
const validatePassword = (rule, value, callback) => { const validatePassword = (rule, value, callback) => {
if (value.length < 6) { if (value.length < 3) {
callback(new Error('The password can not be less than 6 digits')) callback(new Error('The password can not be less than 6 digits'))
} else { } else {
callback() callback()

@ -142,26 +142,27 @@ export default {
return { return {
postForm: Object.assign({}, defaultForm), postForm: Object.assign({}, defaultForm),
loading: false, loading: false,
is_edit: false is_edit: false,
id: ''
} }
}, },
computed: {}, computed: {},
created() { created() {
const id = this.$route.params && this.$route.params.id this.id = this.$route.params && this.$route.params.id
this.fetchData(id) this.fetchData(this.id)
}, },
methods: { methods: {
fetchData(id) { fetchData(id) {
getOrderInfo({ id: id }).then(response => { getOrderInfo(id).then(response => {
if (response.data.length === 1) { if (response.data) {
this.postForm = response.data[0] this.postForm = response.data
} }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
}) })
}, },
updateData() { updateData() {
updateOrderInfo(this.postForm).then(response => { updateOrderInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功') this.$message.success('更新成功')
this.postForm = response.data this.postForm = response.data
}).catch(err => { }).catch(err => {

@ -14,7 +14,7 @@
<el-select v-model="listQuery.order_type" placeholder="订单类型" clearable class="filter-item" style="width: 140px" @change="handleFilter"> <el-select v-model="listQuery.order_type" placeholder="订单类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in order_type_choices" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in order_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> <el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select> </el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -117,7 +117,7 @@
</template> </template>
<script> <script>
import { getOrderInfo, deleteOrderInfo } from '@/api/order' import { deleteOrderInfo, getOrderList } from '@/api/order'
import { baseFilter } from '@/utils' import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive import waves from '@/directive/waves' // waves directive
@ -126,7 +126,9 @@ const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' }, { label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }, { label: '创建时间 Descending', key: '-created_time' },
{ label: '付款时间 Ascending', key: 'pay_time' }, { label: '付款时间 Ascending', key: 'pay_time' },
{ label: '付款时间 Descending', key: '-pay_time' } { label: '付款时间 Descending', key: '-pay_time' },
{ label: '付款金额 Ascending', key: 'actual_amount' },
{ label: '付款金额 Descending', key: '-actual_amount' }
] ]
export default { export default {
@ -190,7 +192,7 @@ export default {
page: 1, page: 1,
limit: 10, limit: 10,
user_id: undefined, user_id: undefined,
sort: '-created_time', ordering: '-created_time',
payment_type: undefined, payment_type: undefined,
payment_name: undefined, payment_name: undefined,
payment_number: undefined, payment_number: undefined,
@ -213,7 +215,7 @@ export default {
}, },
methods: { methods: {
remove_order_info(order_info) { remove_order_info(order_info) {
deleteOrderInfo({ id: order_info.id }).then(response => { deleteOrderInfo(order_info.id).then(response => {
this.list = response.data this.list = response.data
if (response.code === 1000) { if (response.code === 1000) {
this.fetchData() this.fetchData()
@ -228,14 +230,14 @@ export default {
}, },
fetchData() { fetchData() {
this.listLoading = true this.listLoading = true
getOrderInfo(this.listQuery).then(response => { getOrderList(this.listQuery).then(response => {
this.list = response.data this.list = response.data.results
if (this.list && this.list.length > 0) { if (this.list && this.list.length > 0) {
this.payment_type_choices = this.list[0].payment_type_choices this.payment_type_choices = this.list[0].payment_type_choices
this.status_choices = this.list[0].status_choices this.status_choices = this.list[0].status_choices
this.order_type_choices = this.list[0].order_type_choices this.order_type_choices = this.list[0].order_type_choices
} }
this.total = response.total this.total = response.data.count
this.listLoading = false this.listLoading = false
}) })
} }

@ -119,26 +119,27 @@ export default {
return { return {
postForm: Object.assign({}, defaultForm), postForm: Object.assign({}, defaultForm),
loading: false, loading: false,
is_edit: false is_edit: false,
id: ''
} }
}, },
computed: {}, computed: {},
created() { created() {
const id = this.$route.params && this.$route.params.id this.id = this.$route.params && this.$route.params.id
this.fetchData(id) this.fetchData(this.id)
}, },
methods: { methods: {
fetchData(id) { fetchData(id) {
getAppReportIfo({ id: id }).then(response => { getAppReportIfo(this.id).then(response => {
if (response.data.length === 1) { if (response.data) {
this.postForm = response.data[0] this.postForm = response.data
} }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
}) })
}, },
updateData() { updateData() {
updateAppReportIfo(this.postForm).then(response => { updateAppReportIfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功') this.$message.success('更新成功')
this.postForm = response.data this.postForm = response.data
}).catch(err => { }).catch(err => {

@ -13,7 +13,7 @@
<el-option v-for="item in status_choices" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in status_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> <el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select> </el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -103,7 +103,7 @@
</template> </template>
<script> <script>
import { getAppReportIfo, deleteAppReportIfo } from '@/api/report' import { deleteAppReportIfo, getAppReportList } from '@/api/report'
import { baseFilter } from '@/utils' import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive import waves from '@/directive/waves' // waves directive
@ -174,7 +174,7 @@ export default {
page: 1, page: 1,
limit: 10, limit: 10,
app_id: undefined, app_id: undefined,
sort: '-created_time', ordering: '-created_time',
report_type: undefined, report_type: undefined,
app_name: undefined, app_name: undefined,
bundle_id: undefined, bundle_id: undefined,
@ -197,7 +197,7 @@ export default {
}, },
methods: { methods: {
remove_order_info(order_info) { remove_order_info(order_info) {
deleteAppReportIfo({ id: order_info.id }).then(response => { deleteAppReportIfo(order_info.id).then(response => {
this.list = response.data this.list = response.data
if (response.code === 1000) { if (response.code === 1000) {
this.fetchData() this.fetchData()
@ -212,13 +212,13 @@ export default {
}, },
fetchData() { fetchData() {
this.listLoading = true this.listLoading = true
getAppReportIfo(this.listQuery).then(response => { getAppReportList(this.listQuery).then(response => {
this.list = response.data this.list = response.data.results
if (this.list && this.list.length > 0) { if (this.list && this.list.length > 0) {
this.report_type_choices = this.list[0].report_type_choices this.report_type_choices = this.list[0].report_type_choices
this.status_choices = this.list[0].status_choices this.status_choices = this.list[0].status_choices
} }
this.total = response.total this.total = response.data.count
this.listLoading = false this.listLoading = false
}) })
} }

@ -147,26 +147,27 @@ export default {
return { return {
postForm: Object.assign({}, defaultForm), postForm: Object.assign({}, defaultForm),
loading: false, loading: false,
is_edit: false is_edit: false,
id: ''
} }
}, },
computed: {}, computed: {},
created() { created() {
const id = this.$route.params && this.$route.params.id this.id = this.$route.params && this.$route.params.id
this.fetchData(id) this.fetchData(this.id)
}, },
methods: { methods: {
fetchData(id) { fetchData(id) {
getStorageInfo({ id: id }).then(response => { getStorageInfo(id).then(response => {
if (response.data.length === 1) { if (response.data) {
this.postForm = response.data[0] this.postForm = response.data
} }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
}) })
}, },
updateData() { updateData() {
updateStorageInfo(this.postForm).then(response => { updateStorageInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功') this.$message.success('更新成功')
this.postForm = response.data this.postForm = response.data
}).catch(err => { }).catch(err => {

@ -9,7 +9,7 @@
<el-select v-model="listQuery.storage_type" placeholder="存储类型" clearable class="filter-item" style="width: 140px" @change="handleFilter"> <el-select v-model="listQuery.storage_type" placeholder="存储类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in storage_choices" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in storage_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> <el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select> </el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -93,7 +93,7 @@
</template> </template>
<script> <script>
import { getStorageInfo } from '@/api/storage' import { getStorageList } from '@/api/storage'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive import waves from '@/directive/waves' // waves directive
@ -160,12 +160,12 @@ export default {
page: 1, page: 1,
limit: 10, limit: 10,
name: undefined, name: undefined,
sort: '-created_time',
bucket_name: undefined, bucket_name: undefined,
access_key: undefined, access_key: undefined,
storage_type: undefined, storage_type: undefined,
domain_name: undefined, domain_name: undefined,
used_id: undefined user_id: undefined,
ordering: '-created_time'
}, },
sortOptions, sortOptions,
storage_choices: [] storage_choices: []
@ -186,12 +186,13 @@ export default {
}, },
fetchData() { fetchData() {
this.listLoading = true this.listLoading = true
getStorageInfo(this.listQuery).then(response => { getStorageList(this.listQuery).then(response => {
this.list = response.data this.list = response.data.results
console.log(this.list)
if (this.list && this.list.length > 0) { if (this.list && this.list.length > 0) {
this.storage_choices = this.list[0].storage_choices this.storage_choices = this.list[0].storage_choices
} }
this.total = response.total this.total = response.data.count
this.listLoading = false this.listLoading = false
}) })
} }

@ -0,0 +1,117 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.user_id" placeholder="邮箱,手机号,或者uid" />
</el-col>
<el-col :span="4">
<el-button @click="check_balance('user_id')">查询校验</el-button>
</el-col>
</el-row>
<el-row v-if="user_info.private_balance_info.all_balance+user_info.public_balance_info.all_balance">
<el-col>
<el-link :underline="false"> 公有池数量 {{ user_info.public_balance_info.all_balance }} 已经使用数量 {{ user_info.public_balance_info.used_balance }}</el-link>
</el-col>
<el-col>
<el-link :underline="false"> 私有池数量 {{ user_info.private_balance_info.all_balance }} 已经使用数量 {{ user_info.private_balance_info.used_balance }}</el-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="目标用户">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.to_user_id" placeholder="邮箱,手机号,或者uid" />
</el-col>
<el-col :span="4">
<el-button @click="check_balance('to_user_id')">查询校验</el-button>
</el-col>
</el-row>
<el-row v-if="to_user_info.private_balance_info.all_balance+to_user_info.public_balance_info.all_balance">
<el-col>
<el-link :underline="false"> 公有池数量 {{ to_user_info.public_balance_info.all_balance }} 已经使用数量 {{ to_user_info.public_balance_info.used_balance }}</el-link>
</el-col>
<el-col>
<el-link :underline="false"> 私有池数量 {{ to_user_info.private_balance_info.all_balance }} 已经使用数量 {{ to_user_info.private_balance_info.used_balance }}</el-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="转移数量">
<el-row :gutter="12">
<el-col :span="16">
<el-input-number v-model="postForm.number" :min="0" :max="10000" label="可使用设备数" />
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button type="primary" @click="addbill">确定</el-button>
</el-col>
</div>
</template>
<script>
import waves from '@/directive/waves'
import { addBillInfo, getUserBillInfo } from '@/api/developer'
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
export default {
name: 'BillAddInfo',
directives: { waves },
filters: {
formatTime(time) {
return time.split('T')[0]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
postForm: {
to_user_id: undefined,
user_id: undefined,
number: undefined
},
user_info: { public_balance_info: { all_balance: undefined }, private_balance_info: { used_balance: undefined }},
to_user_info: { public_balance_info: { all_balance: undefined }, private_balance_info: { used_balance: undefined }},
action_choices: undefined,
sortOptions
}
},
created() {
},
methods: {
check_balance(act) {
let user_id = this.postForm.user_id
if (act === 'to_user_id') {
user_id = this.postForm.to_user_id
}
getUserBillInfo({ user_id: user_id }).then(response => {
if (act === 'user_id') {
this.user_info = response
} else if (act === 'to_user_id') {
this.to_user_info = response
}
console.log(this.user_info)
})
},
addbill() {
addBillInfo(this.postForm).then(response => {
this.$message.success('充值成功')
})
}
}
}
</script>

@ -0,0 +1,202 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.user_id" placeholder="用户ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.to_user_id" placeholder="目标用户" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.udid" placeholder="设备UDID" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.app_id" placeholder="应用id" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.action" placeholder="充值类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in action_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
Search
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
border
fit
highlight-current-row
stripe
>
<el-table-column align="center" label="ID" width="90">
<template slot-scope="scope">
{{ scope.row.id }}
</template>
</el-table-column>
<el-table-column label="用户ID" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'user_info_edit',params:{id:scope.row.user_id}}">
<el-link type="primary"> {{ scope.row.user_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="目标用户ID" width="100" align="center">
<template slot-scope="scope">
<router-link v-if="scope.row.to_user_id" :to="{name: 'user_info_edit',params:{id:scope.row.to_user_id}}">
<el-link type="primary"> {{ scope.row.to_user_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="应用ID" width="100" align="center">
<template slot-scope="scope">
<router-link v-if="scope.row.app_id" :to="{name: 'app_info_edit',params:{id:scope.row.app_id}}">
<el-link type="primary"> {{ scope.row.app_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="UDID" align="center">
<template slot-scope="scope">
{{ scope.row.udid }}
</template>
</el-table-column>
<el-table-column label="客户端IP" align="center" >
<template slot-scope="scope">
{{ scope.row.remote_addr }}
</template>
</el-table-column>
<el-table-column label="账单类型" align="center" width="100">
<template slot-scope="scope">
{{ scope.row.action }}
</template>
</el-table-column>
<el-table-column label="备注" align="center">
<template slot-scope="scope">
{{ scope.row.description }}
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="创建时间" width="120">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.created_time">
<span>{{ scope.row.created_time|formatTime }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="delbill(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" />
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves'
import { delBillInfo, getBillList } from '@/api/developer'
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
export default {
name: 'BillInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
return time.split('T')[0]
},
certStatusFilter(status) {
const statusMap = {
'0': 'danger',
'1': 'success',
'2': 'gray',
'3': 'info'
}
return statusMap[status]
},
certLableFilter(row) {
for (const v of row.auth_type_choices) {
if (v.id === row.auth_type) {
return v.name
}
}
},
statusFilter(status) {
const statusMap = {
true: 'success',
false: 'danger'
}
return statusMap[status]
},
appStatusNameFilter(row) {
for (const r of row.status_choices) {
if (r.id === row.status) {
return r.name
}
}
},
appStatusFilter(status) {
const statusMap = {
'0': 'danger',
'1': 'success',
'2': 'gray'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: {
page: 1,
limit: 10,
ordering: '-created_time',
action: undefined,
app_id: undefined,
to_user_id: undefined,
user_id: undefined,
udid: undefined
},
action_choices: undefined,
sortOptions
}
},
created() {
this.listQuery.issuer_id = this.$route.params && this.$route.params.issuer_id
this.listQuery.user_id = this.$route.params && this.$route.params.user_id
this.fetchData()
},
methods: {
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
delbill(id) {
delBillInfo(id).then(response => {
this.$message.success('删除成功')
this.fetchData()
this.listLoading = false
})
},
fetchData() {
this.listLoading = true
getBillList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.action_choices = this.list[0].action_choices
}
this.total = response.data.count
this.listLoading = false
})
}
}
}
</script>

@ -154,26 +154,27 @@ export default {
return { return {
postForm: Object.assign({}, defaultForm), postForm: Object.assign({}, defaultForm),
loading: false, loading: false,
is_edit: false is_edit: false,
id: ''
} }
}, },
computed: {}, computed: {},
created() { created() {
const id = this.$route.params && this.$route.params.id this.id = this.$route.params && this.$route.params.id
this.fetchData(id) this.fetchData(this.id)
}, },
methods: { methods: {
fetchData(id) { fetchData(id) {
getDeveloperInfo({ id: id }).then(response => { getDeveloperInfo(id).then(response => {
if (response.data.length === 1) { if (response.data) {
this.postForm = response.data[0] this.postForm = response.data
} }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
}) })
}, },
updateData() { updateData() {
updatedeveloperInfo(this.postForm).then(response => { updatedeveloperInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功') this.$message.success('更新成功')
this.postForm = response.data this.postForm = response.data
}).catch(err => { }).catch(err => {

@ -9,7 +9,7 @@
<el-select v-model="listQuery.auth_type" placeholder="账户类型" clearable class="filter-item" style="width: 140px" @change="handleFilter"> <el-select v-model="listQuery.auth_type" placeholder="账户类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in auth_type_choices" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in auth_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> <el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select> </el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -73,7 +73,7 @@
<el-table-column class-name="status-col" label="是否激活" width="80" align="center"> <el-table-column class-name="status-col" label="是否激活" width="80" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag :type="scope.row.is_actived | statusFilter">{{ scope.row.is_actived }}</el-tag> <el-tag :type="scope.row.is_actived | statusFilter">{{ scope.row |StatusNameFilter }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
@ -100,9 +100,9 @@
查看编辑 查看编辑
</el-button> </el-button>
</router-link> </router-link>
<el-button type="danger" size="mini" @click="deleteApp(scope.row.id)"> <!-- <el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">-->
删除 <!-- 删除-->
</el-button> <!-- </el-button>-->
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -112,7 +112,7 @@
</template> </template>
<script> <script>
import { getDeveloperInfo } from '@/api/developer' import { getDeveloperList } from '@/api/developer'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive import waves from '@/directive/waves' // waves directive
@ -156,7 +156,7 @@ export default {
} }
return statusMap[status] return statusMap[status]
}, },
appStatusNameFilter(row) { StatusNameFilter(row) {
for (const r of row.status_choices) { for (const r of row.status_choices) {
if (r.id === row.status) { if (r.id === row.status) {
return r.name return r.name
@ -180,7 +180,7 @@ export default {
listQuery: { listQuery: {
page: 1, page: 1,
limit: 10, limit: 10,
sort: '-created_time', ordering: '-created_time',
issuer_id: undefined, issuer_id: undefined,
private_key_id: undefined, private_key_id: undefined,
certid: undefined, certid: undefined,
@ -189,7 +189,8 @@ export default {
auth_type: undefined auth_type: undefined
}, },
sortOptions, sortOptions,
auth_type_choices: [] auth_type_choices: [],
status_choices: []
} }
}, },
created() { created() {
@ -202,12 +203,13 @@ export default {
}, },
fetchData() { fetchData() {
this.listLoading = true this.listLoading = true
getDeveloperInfo(this.listQuery).then(response => { getDeveloperList(this.listQuery).then(response => {
this.list = response.data this.list = response.data.results
if (this.list && this.list.length > 0) { if (this.list && this.list.length > 0) {
this.auth_type_choices = this.list[0].auth_type_choices this.auth_type_choices = this.list[0].auth_type_choices
this.status_choices = this.list[0].status_choices
} }
this.total = response.total this.total = response.data.count
this.listLoading = false this.listLoading = false
}) })
} }

@ -8,7 +8,7 @@
<el-input v-model="listQuery.bundle_id" placeholder="BundleID" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" /> <el-input v-model="listQuery.bundle_id" placeholder="BundleID" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.short" placeholder="短连接" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" /> <el-input v-model="listQuery.short" placeholder="短连接" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> <el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select> </el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -94,7 +94,7 @@
</template> </template>
<script> <script>
import { getDevicesInfo } from '@/api/devices' import { getDevicesList } from '@/api/devices'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive import waves from '@/directive/waves' // waves directive
@ -158,7 +158,7 @@ export default {
listQuery: { listQuery: {
page: 1, page: 1,
limit: 10, limit: 10,
sort: '-created_time', ordering: '-created_time',
issuer_id: undefined, issuer_id: undefined,
short: undefined, short: undefined,
bundle_id: undefined, bundle_id: undefined,
@ -180,14 +180,13 @@ export default {
this.fetchData() this.fetchData()
}, },
fetchData() { fetchData() {
console.log(this.listQuery)
this.listLoading = true this.listLoading = true
getDevicesInfo(this.listQuery).then(response => { getDevicesList(this.listQuery).then(response => {
this.list = response.data this.list = response.data.results
if (this.list && this.list.length > 0) { if (this.list && this.list.length > 0) {
this.auth_type_choices = this.list[0].auth_type_choices this.auth_type_choices = this.list[0].auth_type_choices
} }
this.total = response.total this.total = response.data.count
this.listLoading = false this.listLoading = false
}) })
} }

@ -6,21 +6,21 @@
<el-form-item label="UID"> <el-form-item label="UID">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-input :value="postForm.uid" disabled /> <el-input :value="postForm.uid" disabled/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="账号" prop="username"> <el-form-item label="账号" prop="username">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="postForm.username" /> <el-input v-model="postForm.username"/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="昵称"> <el-form-item label="昵称">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="postForm.first_name" /> <el-input v-model="postForm.first_name"/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -28,7 +28,9 @@
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-select v-model="postForm.gender" class="filter-item" placeholder="Please select"> <el-select v-model="postForm.gender" class="filter-item" placeholder="Please select">
<el-option v-for="item in postForm.gender_choices" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in postForm.gender_choices" :key="item.id" :label="item.name"
:value="item.id"
/>
</el-select> </el-select>
</el-col> </el-col>
</el-row> </el-row>
@ -37,7 +39,7 @@
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-select v-model="postForm.role" class="filter-item" placeholder="Please select"> <el-select v-model="postForm.role" class="filter-item" placeholder="Please select">
<el-option v-for="item in postForm.role_choices" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in postForm.role_choices" :key="item.id" :label="item.name" :value="item.id"/>
</el-select> </el-select>
</el-col> </el-col>
</el-row> </el-row>
@ -46,7 +48,7 @@
<el-form-item label="邮箱" prop="email"> <el-form-item label="邮箱" prop="email">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="postForm.email" /> <el-input v-model="postForm.email"/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -54,35 +56,37 @@
<el-form-item label="手机" prop="mobile"> <el-form-item label="手机" prop="mobile">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="postForm.mobile" /> <el-input v-model="postForm.mobile"/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="Q Q" prop="number"> <el-form-item label="Q Q" prop="number">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="postForm.qq" /> <el-input v-model="postForm.qq"/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="职位"> <el-form-item label="职位">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="postForm.job" /> <el-input v-model="postForm.job"/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="公司"> <el-form-item label="公司">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="postForm.company" /> <el-input v-model="postForm.company"/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="备注"> <el-form-item label="备注">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="postForm.memo" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" placeholder="Please input" /> <el-input v-model="postForm.memo" :autosize="{ minRows: 4, maxRows: 6}" type="textarea"
placeholder="Please input"
/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -91,7 +95,9 @@
<el-form-item label="用户头像"> <el-form-item label="用户头像">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-image :src="postForm.head_img" :preview-src-list="[postForm.head_img]" fit="contain" style="width: 100px; height: 100px" /> <el-image :src="postForm.head_img" :preview-src-list="[postForm.head_img]" fit="contain"
style="width: 100px; height: 100px"
/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -177,10 +183,16 @@
<el-form-item label="实名认证"> <el-form-item label="实名认证">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-select v-model="postForm.certification" class="filter-item" placeholder="Please select" :disabled="postForm.certification === -1"> <el-select v-model="postForm.certification" class="filter-item" placeholder="Please select"
<el-option v-for="item in postForm.certification_status_choices" :key="item.id" :label="item.name" :value="item.id" /> :disabled="postForm.certification === -1"
>
<el-option v-for="item in postForm.certification_status_choices" :key="item.id" :label="item.name"
:value="item.id"
/>
</el-select> </el-select>
<router-link v-if="postForm.certification_id" :to="{name: 'user_authentication_info_edit',params:{id:postForm.certification_id.id}}"> <router-link v-if="postForm.certification_id"
:to="{name: 'user_authentication_info_edit',params:{id:postForm.certification_id.id}}"
>
<el-button type="primary"> <el-button type="primary">
审核认证信息 审核认证信息
</el-button> </el-button>
@ -203,7 +215,7 @@
<el-form-item label="下载次数" prop="download_times"> <el-form-item label="下载次数" prop="download_times">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="8"> <el-col :span="8">
<el-input :value="postForm.download_times" disabled /> <el-input :value="postForm.download_times" disabled/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<router-link v-if="postForm.id" :to="{name: 'order_info_list',params:{user_id:postForm.id}}"> <router-link v-if="postForm.id" :to="{name: 'order_info_list',params:{user_id:postForm.id}}">
@ -215,14 +227,14 @@
<el-form-item label="注册时间" prop="timestamp"> <el-form-item label="注册时间" prop="timestamp">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="8"> <el-col :span="8">
<el-date-picker :value="postForm.date_joined" type="datetime" disabled /> <el-date-picker :value="postForm.date_joined" type="datetime" disabled/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="应用版本历史记录"> <el-form-item label="应用版本历史记录">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="8"> <el-col :span="8">
<el-input v-model="postForm.history_release_limit" /> <el-input v-model="postForm.history_release_limit"/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -234,6 +246,9 @@
<el-col :span="8"> <el-col :span="8">
<el-button @click="add_amount_download_times(postForm.id)">下载次数充值</el-button> <el-button @click="add_amount_download_times(postForm.id)">下载次数充值</el-button>
</el-col> </el-col>
<el-col :span="8">
<el-button @click="reset_password(postForm.id)">重置密码</el-button>
</el-col>
<el-button v-if="!is_edit" type="primary" @click="is_edit=true">修改</el-button> <el-button v-if="!is_edit" type="primary" @click="is_edit=true">修改</el-button>
<div v-else> <div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button> <el-button type="primary" @click="is_edit=false">取消</el-button>
@ -245,9 +260,9 @@
<script> <script>
import { validURL } from '@/utils/validate' import { validURL } from '@/utils/validate'
import { getUserInfos, updateUserInfo } from '@/api/user' import { getUserInfos, resetPassword, updateUserInfo } from '@/api/user'
import { createOrderInfo } from '@/api/order' import { createOrderInfo } from '@/api/order'
import { getStorageInfo, changeStorageInfo } from '@/api/storage' import { changeStorageInfo, getStorageList } from '@/api/storage'
const defaultForm = { const defaultForm = {
email: undefined, email: undefined,
@ -277,7 +292,7 @@ const defaultForm = {
export default { export default {
name: 'UserDetail', name: 'UserDetail',
components: { }, filters: { components: {}, filters: {
userStatusFilter(status) { userStatusFilter(status) {
const statusMap = { const statusMap = {
true: '激活,允许登录', true: '激活,允许登录',
@ -341,8 +356,7 @@ export default {
storage_selection: [] storage_selection: []
} }
}, },
computed: { computed: {},
},
created() { created() {
if (this.isEdit) { if (this.isEdit) {
const id = this.$route.params && this.$route.params.id const id = this.$route.params && this.$route.params.id
@ -369,18 +383,42 @@ export default {
message: '取消充值' message: '取消充值'
}) })
}) })
}, reset_password(user_id) {
this.$prompt('请输入新密码,密码至少6位', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(({ value }) => {
resetPassword({ user_id: user_id, password: value }).then(res => {
if (res.code === 1000) {
this.$message.success('密码重置成功')
this.fetchData(user_id)
} else {
this.$message.error(res.msg)
}
})
}).catch((e) => {
console.log(e)
this.$message({
type: 'info',
message: '取消充值'
})
})
}, },
changeStorageData(force) { changeStorageData(force) {
changeStorageInfo({ id: this.postForm.id, use_storage_id: this.postForm.storage, force: force }).then(response => { changeStorageInfo({
id: this.postForm.id,
use_storage_id: this.postForm.storage,
force: force
}).then(response => {
this.$message.success('存储数据迁移并设置刷新成功') this.$message.success('存储数据迁移并设置刷新成功')
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
}) })
}, },
fetchStorageData(user_id) { fetchStorageData(user_id) {
getStorageInfo({ user_id: user_id }).then(response => { getStorageList({ user_id: user_id }).then(response => {
if (response.storage_selection) { if (response.data.storage_selection) {
this.storage_selection = response.storage_selection this.storage_selection = response.data.storage_selection
} }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)

@ -60,6 +60,11 @@
{{ scope.row.download_times }} {{ scope.row.download_times }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="当天苹果|安卓下载次数" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.today_hits_count.ios_today_hits_count }} | {{ scope.row.today_hits_count.android_today_hits_count }}
</template>
</el-table-column>
<el-table-column label="应用数量" width="100" align="center"> <el-table-column label="应用数量" width="100" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<router-link :to="{name: 'app_info_list',params:{user_id:scope.row.id}}"> <router-link :to="{name: 'app_info_list',params:{user_id:scope.row.id}}">

@ -25,45 +25,54 @@
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="地址">
<el-row :gutter="12"> <el-form-item label="创建时间" prop="timestamp">
<el-col :span="16"> <el-row :gutter="20">
<el-input v-model="postForm.address" /> <el-col :span="8">
<el-date-picker :value="postForm.created_time" disabled type="datetime" />
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="创建时间" prop="timestamp"> <el-form-item label="订阅时间" prop="timestamp">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="8"> <el-col :span="8">
<el-date-picker :value="postForm.created_time" type="datetime" disabled /> <el-date-picker :value="postForm.subscribe_time*1000" disabled type="datetime" />
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="用户头像" label-width="160px"> <el-form-item label="用户头像" label-width="160px">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-image :src="postForm.head_img_url" :preview-src-list="[postForm.head_img_url]" fit="contain" style="width: 100px; height: 100px" /> <el-image
:preview-src-list="[postForm.head_img_url]"
:src="postForm.head_img_url"
fit="contain"
style="width: 100px; height: 100px"
/>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="扫码登录" label-width="160px">
<el-form-item label="订阅状态" label-width="160px">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-select v-model="postForm.subscribe" class="filter-item"> <el-input v-model="postForm.enable_login" disabled />
<el-option v-for="item in wxbind_state_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="性别" label-width="160px"> <el-form-item label="消息通知" label-width="160px">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="16"> <el-col :span="16">
<el-select v-model="postForm.sex" class="filter-item"> <el-input v-model="postForm.enable_notify" disabled />
<el-option v-for="item in wxbind_sex_choices" :key="item.id" :label="item.name" :value="item.id" /> </el-col>
</el-row>
</el-form-item>
<el-form-item label="订阅状态" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.subscribe" class="filter-item" disabled>
<el-option v-for="item in wxbind_state_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-col> </el-col>
</el-row> </el-row>
@ -84,17 +93,12 @@
<script> <script>
import { getWxBindInfos, updateWxBindInfo } from '@/api/wxbind' import { getWxBindInfos, updateWxBindInfo } from '@/api/wxbind'
const wxbind_state_choices = [ const wxbind_state_choices = [
{ id: false, name: '未订阅' }, { id: false, name: '未订阅' },
{ id: true, name: '已订阅' } { id: true, name: '已订阅' }
] ]
const wxbind_sex_choices = [
{ id: 0, name: '未知' },
{ id: 1, name: '男' },
{ id: 2, name: '女' }
]
const defaultForm = { const defaultForm = {
user_id: undefined, user_id: undefined,
address: undefined, address: undefined,
@ -103,9 +107,10 @@ const defaultForm = {
id: undefined, id: undefined,
nickname: undefined, nickname: undefined,
openid: undefined, openid: undefined,
sex: undefined,
subscribe: undefined, subscribe: undefined,
subscribe_time: undefined subscribe_time: undefined,
enable_login: undefined,
enable_notify: undefined
} }
export default { export default {
@ -117,26 +122,26 @@ export default {
loading: false, loading: false,
is_edit: false, is_edit: false,
wxbind_state_choices, wxbind_state_choices,
wxbind_sex_choices id: ''
} }
}, },
computed: {}, computed: {},
created() { created() {
const id = this.$route.params && this.$route.params.id this.id = this.$route.params && this.$route.params.id
this.fetchData(id) this.fetchData(this.id)
}, },
methods: { methods: {
fetchData(id) { fetchData(id) {
getWxBindInfos({ id: id }).then(response => { getWxBindInfos(id).then(response => {
if (response.data.length === 1) { if (response.data) {
this.postForm = response.data[0] this.postForm = response.data
} }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
}) })
}, },
updateData() { updateData() {
updateWxBindInfo(this.postForm).then(response => { updateWxBindInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功') this.$message.success('更新成功')
this.postForm = response.data this.postForm = response.data
}).catch(err => { }).catch(err => {

@ -7,7 +7,7 @@
<el-select v-model="listQuery.subscribe" placeholder="玩家是否订阅" clearable class="filter-item" style="width: 140px" @change="handleFilter"> <el-select v-model="listQuery.subscribe" placeholder="玩家是否订阅" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in wxbind_state_choices" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in wxbind_state_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter"> <el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" /> <el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select> </el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter"> <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -50,14 +50,14 @@
{{ scope.row.openid }} {{ scope.row.openid }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="地址" min-width="200px" align="center"> <el-table-column label="允许扫码登录" min-width="100px" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.address }} {{ scope.row.enable_login }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="性别" min-width="60px" align="center"> <el-table-column label="允许消息通知" min-width="100px" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row |sexLableFilter }} {{ scope.row.enable_notify }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column class-name="status-col" label="订阅状态" width="95" align="center"> <el-table-column class-name="status-col" label="订阅状态" width="95" align="center">
@ -92,7 +92,7 @@
</template> </template>
<script> <script>
import { getWxBindInfos, deleteWxBind } from '@/api/wxbind' import { deleteWxBind, getWxBindList } from '@/api/wxbind'
import { baseFilter } from '@/utils' import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive import waves from '@/directive/waves' // waves directive
@ -107,12 +107,6 @@ const wxbind_state_choices = [
{ id: true, name: '已订阅' } { id: true, name: '已订阅' }
] ]
const wxbind_sex_choices = [
{ id: 0, name: '未知' },
{ id: 1, name: '男' },
{ id: 2, name: '女' }
]
export default { export default {
name: 'WxBindInfo', name: 'WxBindInfo',
components: { Pagination }, components: { Pagination },
@ -139,9 +133,6 @@ export default {
statusLableFilter(row) { statusLableFilter(row) {
return baseFilter(row.subscribe, wxbind_state_choices) return baseFilter(row.subscribe, wxbind_state_choices)
}, },
sexLableFilter(row) {
return baseFilter(row.sex, wxbind_sex_choices)
},
statusFilter(status) { statusFilter(status) {
const statusMap = { const statusMap = {
true: 'success', true: 'success',
@ -159,7 +150,7 @@ export default {
page: 1, page: 1,
limit: 10, limit: 10,
user_id: undefined, user_id: undefined,
sort: '-created_time', ordering: '-created_time',
openid: undefined, openid: undefined,
nickname: undefined, nickname: undefined,
subscribe: undefined subscribe: undefined
@ -178,7 +169,7 @@ export default {
}, },
methods: { methods: {
remove_wxbind(wxbind_id) { remove_wxbind(wxbind_id) {
deleteWxBind({ id: wxbind_id }).then(response => { deleteWxBind(wxbind_id).then(response => {
this.list = response.data this.list = response.data
this.total = response.total this.total = response.total
this.listLoading = false this.listLoading = false
@ -190,9 +181,9 @@ export default {
}, },
fetchData() { fetchData() {
this.listLoading = true this.listLoading = true
getWxBindInfos(this.listQuery).then(response => { getWxBindList(this.listQuery).then(response => {
this.list = response.data this.list = response.data.results
this.total = response.total this.total = response.data.count
this.listLoading = false this.listLoading = false
}) })
} }

@ -39,5 +39,48 @@ yarn lint
``` ```
### Customize configuration ## deploy nginx
See [Configuration Reference](https://cli.vuejs.org/config/).
#### short deploy nginx
```shell
location / {
try_files $uri $uri/ /short.html;
}
```
#### index.html
```html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="3;URL=https://flyapps.cn">
<title>FLY分发平台</title>
<style>
.container {
width: 60%;
margin: 10% auto 0;
background-color: #f0f0f0;
padding: 2% 5%;
border-radius: 10px
}
ul {
padding-left: 20px;
}
ul li {
line-height: 2.3
}
a {
color: #20a53a
}
</style>
</head>
<body>
<div class="container">
<h3>这是分发平台下载默认页,正在跳转 <a href="https://flyapps.cn">首页</a></h3>
</div>
</body>
</html>
```

@ -11,7 +11,7 @@
"dependencies": { "dependencies": {
"ali-oss": "^6.5.1", "ali-oss": "^6.5.1",
"app-info-parser": "^0.3.9", "app-info-parser": "^0.3.9",
"axios": "^0.19.0", "axios": "^0.22.0",
"core-js": "^3.4.4", "core-js": "^3.4.4",
"element-ui": "^2.13.0", "element-ui": "^2.13.0",
"js-base64": "^2.5.2", "js-base64": "^2.5.2",
@ -36,7 +36,6 @@
"eslint": "^5.16.0", "eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0", "eslint-plugin-vue": "^5.0.0",
"html-webpack-plugin": "^5.3.1", "html-webpack-plugin": "^5.3.1",
"image-webpack-loader": "^7.0.1",
"script-ext-html-webpack-plugin": "^2.1.5", "script-ext-html-webpack-plugin": "^2.1.5",
"uglifyjs-webpack-plugin": "^2.2.0", "uglifyjs-webpack-plugin": "^2.2.0",
"vue-template-compiler": "^2.6.10", "vue-template-compiler": "^2.6.10",

@ -2,9 +2,9 @@
<html lang="zh"> <html lang="zh">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta content="width=device-width,initial-scale=1.0" name="viewport">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link href="<%= BASE_URL %>favicon.ico" rel="icon">
<script src="<%= BASE_URL %>gt.js"></script> <script src="<%= BASE_URL %>gt.js"></script>
<title>FLY分发平台</title> <title>FLY分发平台</title>
</head> </head>

@ -2,9 +2,9 @@
<html lang="zh"> <html lang="zh">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta content="width=device-width,initial-scale=1.0" name="viewport">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link href="<%= BASE_URL %>favicon.ico" rel="icon">
<script src="<%= BASE_URL %>gt.js"></script> <script src="<%= BASE_URL %>gt.js"></script>
<title>应用下载</title> <title>应用下载</title>
</head> </head>

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

@ -18,9 +18,8 @@
effect="dark" effect="dark"
type="error"> type="error">
<div slot="title"> <div slot="title">
<span :underline="false" <span class="domain-tip-bar">应用分发请绑定您自己的域名平台分发域名可能因不可违因素更换将导致您的应用无法访问</span>
class="domian-tip-bar">应用分发请绑定您自己的域名平台分发域名可能因不可违因素更换将导致您的应用无法访问</span> <el-button size="small" @click="bind_domain_type=1,bind_domain_sure=true">立即绑定</el-button>
<el-button size="medium" @click="bind_domain_type=1,bind_domain_sure=true">立即绑定</el-button>
</div> </div>
</el-alert> </el-alert>
</div> </div>
@ -119,11 +118,15 @@ export default {
padding-top: 30px; padding-top: 30px;
} }
.domian-tip-bar { .domain-tip-bar {
line-height: 43px; line-height: 43px;
color: #fff; color: #fff;
font-weight: 500; font-weight: 500;
font-size: medium; font-size: medium;
margin-right: 20px; margin-right: 20px;
} }
/deep/ .el-alert {
padding: 2px 16px;
}
</style> </style>

@ -2,7 +2,7 @@
<el-container class="container"> <el-container class="container">
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0" name="viewport"> <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0" name="viewport">
<el-main v-if="report_flag" style="margin-top: -60px"> <el-main v-if="report_flag" class="dialog" style="margin-top: -60px">
<a style="color: #3888fa" @click="report_flag=false">&lt;&nbsp;返回下载页</a> <a style="color: #3888fa" @click="report_flag=false">&lt;&nbsp;返回下载页</a>
<el-header style="text-align: center;height: 20px;margin-top: 10px">举报详情</el-header> <el-header style="text-align: center;height: 20px;margin-top: 10px">举报详情</el-header>
@ -39,7 +39,7 @@
<el-row v-if="captcha.captcha_image" style="margin-top: 10px;margin-bottom: -10px"> <el-row v-if="captcha.captcha_image" style="margin-top: 10px;margin-bottom: -10px">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="report_info.authcode" clearable maxlength="6" placeholder="请输入图片验证码"/> <el-input v-model="report_info.verify_code" clearable maxlength="6" placeholder="请输入图片验证码"/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-image <el-image
@ -288,16 +288,15 @@
<el-link :underline="false" type="warning">若安装异常请复制UDID发送给管理员</el-link> <el-link :underline="false" type="warning">若安装异常请复制UDID发送给管理员</el-link>
</div> </div>
<div class="footer" style="margin-top: 30%;margin-bottom: 8px"> <div class="footer" style="margin-top: 30%;padding-bottom: 10px">
免责声明<br> 免责声明<br>
本网站仅提供下载托管应用为用户自行上传请甄别应用风险后进行下载<a class="one-key-report" 本网站仅提供下载托管应用为用户自行上传请甄别应用风险后进行下载<a class="one-key-report"
@click="get_auth_code(),report_flag=true">举报</a> @click="get_auth_code(),report_flag=true">举报</a>
</div> </div>
<div v-if="agent!==''"> <div v-if="agent!=='' && ad_info.ad_uri" style="padding-bottom: 80px">
<div v-if="ad_info.ad_uri" style="margin-bottom: 80px"/> <div class="app_bottom_fixed">
<div v-if="ad_info.ad_uri" class="app_bottom_fixed"> <div class="ad_title">
<div class="signhelp-title">
<span><a class="closead" @click="ad_info.ad_uri=undefined">关闭</a></span> <span><a class="closead" @click="ad_info.ad_uri=undefined">关闭</a></span>
</div> </div>
<a :href="ad_info.ad_uri" target="_blank"> <a :href="ad_info.ad_uri" target="_blank">
@ -305,16 +304,23 @@
</a> </a>
</div> </div>
</div> </div>
<div v-else style="padding-bottom: 10px"></div>
</div> </div>
</el-container> </el-container>
<div v-else> <el-container v-else style="text-align: center;margin-top: 60px">
<i v-if="!report_flag"> <el-header>
{{ this.currentappinfo.name | formatName }} <img v-if="mcurrentappinfo.icon_url" :src="mcurrentappinfo.icon_url" class="imgloading"/>
</i> </el-header>
</div> <div v-if="wrong && msg" style="height: 20px;margin-bottom: 10px;"> {{ this.msg }}</div>
<div v-if="!report_flag" style="text-align: center;margin:0 auto;height: 100px">
<span id="qrcode1" style="display: inline-block"></span>
<div style="margin-top: 10px">{{ this.currentappinfo.name }}</div>
</div>
</el-container>
<div ref="signhelp" class="signhelp screenshots-section"> <div ref="signhelp" class="signhelp screenshots-section">
@ -346,28 +352,21 @@
<script> <script>
import QRCode from 'qrcodejs2' import QRCode from 'qrcodejs2'
import { import {appReport, geetest, getAuthTokenFun, getdownloadurl, getShortAppinfo, gettask} from '@/restful'
appReport, import {checkEmail, checkphone, format_time, getRandomStr} from "@/utils/base/utils";
checkEmail,
checkphone,
geetest,
getAuthTokenFun,
getdownloadurl,
getShortAppinfo,
gettask
} from '@/restful/download'
export default { export default {
name: "FirDownload", name: "FirDownload",
data() { data() {
return { return {
captcha: {"captcha_image": '', "captcha_key": '', "length": 8}, captcha: {"captcha_image": '', "captcha_key": '', "length": 8},
auth_rules: {},
report_info: { report_info: {
'username': '', 'username': '',
'report_type': '', 'report_type': '',
'report_reason': '', 'report_reason': '',
'seicode': '', 'seicode': '',
'authcode': '', 'verify_code': '',
'email': '', 'email': '',
'auth_token': '', 'auth_token': '',
'act': 'email', 'act': 'email',
@ -416,7 +415,10 @@ export default {
timer: '', timer: '',
gomobile: true, gomobile: true,
mobileprovision: '', mobileprovision: '',
ad_info: {ad_pic: '', ad_uri: ''} ad_info: {ad_pic: '', ad_uri: ''},
task_spend_time: 0,
task_msg: '执行中',
err_password: false
} }
}, },
beforeDestroy() { beforeDestroy() {
@ -476,12 +478,12 @@ export default {
} }
let picode = { let picode = {
"authcode": this.report_info.authcode, "verify_code": this.report_info.verify_code,
"captcha_key": this.captcha.captcha_key, "captcha_key": this.captcha.captcha_key,
"report": this.currentappinfo.app_id "report": this.currentappinfo.app_id
}; };
let params = {'act': this.report_info.act, 'target': this.report_info.email, 'ext': picode}; let params = {'act': this.report_info.act, 'target': this.report_info.email, 'ext': picode};
if (this.captcha.geetest) { if (this.auth_rules.geetest) {
geetest(this, this.report_info.email, params, (n_params) => { geetest(this, this.report_info.email, params, (n_params) => {
this.do_get_auth_token(n_params); this.do_get_auth_token(n_params);
}) })
@ -511,7 +513,8 @@ export default {
if (data.code === 1000) { if (data.code === 1000) {
let jdata = data.data; let jdata = data.data;
if (jdata.enable) { if (jdata.enable) {
this.captcha = data.data; this.auth_rules = data.data.auth_rules;
this.captcha = this.auth_rules.captcha;
if (data.data.s_list) { if (data.data.s_list) {
this.report_info_list = data.data.s_list; this.report_info_list = data.data.s_list;
} }
@ -542,37 +545,52 @@ export default {
duration: 0 duration: 0
}); });
}, },
loop_check_task() { loop_check_task(c_count = 1, loop_t, unique_key = getRandomStr()) {
let c_count = 1; if (c_count === 1) {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const loop_t = window.setInterval(res => { loop_t = window.setInterval(res => {
gettask(data => { this.task_spend_time += 1;
c_count += 1; this.msg = this.task_msg + ' ○ ' + this.task_spend_time;
if (c_count > 120) { }, 3000)
window.clearInterval(loop_t); }
} gettask(data => {
if (data.code === 1000) { c_count += 1;
window.clearInterval(loop_t); if (c_count > 30) {
if (data.msg) { window.clearInterval(loop_t)
this.show_err_msg(data.msg); return;
} else { }
if (data.code === 1000) {
window.clearInterval(loop_t);
if (data.data) {
if (data.data.code === 1000) {
this.$message.success('签名完成,请点击下载安装'); this.$message.success('签名完成,请点击下载安装');
this.wrong = false; this.wrong = false;
} this.binary_download();
} else { } else {
if (data.code === 1002) { if (data.data.redirect) {
window.clearInterval(loop_t); window.location.href = data.data.redirect
data.msg = '应用不存在' } else if (data.data.msg) {
} this.show_err_msg(data.data.msg);
if (data.msg) { }
this.msg = data.msg + ' ○ ' + c_count;
} }
} }
}, { } else {
"short": this.$route.params.short, if (data.code === 1002) {
data: {"task_id": this.$route.query.task_id} window.clearInterval(loop_t);
}) data.msg = '应用不存在'
}, 3000) }
if (data.msg) {
this.task_msg = data.msg;
}
if (data.code === 1001) {
this.loop_check_task(c_count, loop_t, unique_key)
}
}
}, {
methods: 'GET',
short: this.$route.params.short,
data: {"task_id": this.$route.query.task_id, "unique_key": unique_key}
})
}, },
jiaocheng(act) { jiaocheng(act) {
let signhelp = this.$refs.signhelp; let signhelp = this.$refs.signhelp;
@ -590,18 +608,83 @@ export default {
window.location.href = this.mobileprovision; window.location.href = this.mobileprovision;
}, },
check_msg() { check_msg() {
if (this.$route.query.udid) { if (this.agent === 'wxandroid' || this.agent === 'wxapple') {
if (this.$route.query.task_id) { return
this.wrong = true; }
this.msg = '签名处理中,请耐心等待'; if (this.$route.query.msg) {
this.loop_check_task() this.wrong = true;
} else if (this.$route.query.msg) { this.show_err_msg(this.$route.query.msg);
this.wrong = true; this.currentappinfo.need_password = false;
this.show_err_msg(this.$route.query.msg); return;
}
if (this.currentappinfo.need_password) {
if (this.currentappinfo.issupersign && !this.$route.query.udid) {
this.currentappinfo.need_password = false
}
if (this.$route.query.password && !this.err_password) {
this.currentappinfo.need_password = false
this.password = this.$route.query.password;
// eslint-disable-next-line no-unused-vars
this.timer = setTimeout(data => {
this.download();
}, 1000);
}
} else {
if (this.$route.query.udid && this.$route.query.task_token) {
this.download()
} }
} }
}, },
download() { download() {
if (this.$route.query.udid && this.$route.query.task_token) {
this.wrong = true;
this.msg = '签名处理中,请耐心等待';
gettask(data => {
if (data.code === 1000) {
if (data.data) {
if (data.data.code === 1000) {
this.wrong = false;
this.currentappinfo.need_password = false
this.binary_download()
} else {
if (data.data.redirect) {
window.location.href = data.data.redirect
} else if (data.data.msg) {
this.show_err_msg(data.data.msg);
}
}
} else if (data.task_id) {
this.$route.query.task_id = data.task_id;
this.loop_check_task()
}
} else if (data.code === 1006) {
this.msg = data.msg;
this.show_err_msg(data.msg);
this.wrong = false;
this.err_password = true
this.currentappinfo.need_password = true
this.password = ''
} else if (data.code === 1005) {
this.msg = data.msg;
this.show_err_msg(data.msg);
this.wrong = true;
} else {
this.show_err_msg(data.msg);
// eslint-disable-next-line no-unused-vars
this.timer = setTimeout(data => {
window.location.href = location.href.replace(location.search, '');
}, 3000);
}
}, {
methods: 'POST',
short: this.$route.params.short,
data: {"task_token": this.$route.query.task_token, 'password': this.password}
})
} else {
this.binary_download()
}
},
binary_download() {
if (this.currentappinfo.app_id) { if (this.currentappinfo.app_id) {
this.isdownload = true; this.isdownload = true;
getdownloadurl(res => { getdownloadurl(res => {
@ -624,7 +707,11 @@ export default {
} else { } else {
if (this.agent !== '') { if (this.agent !== '') {
this.downloadurl = res.data.download_url; this.downloadurl = res.data.download_url;
window.location.href = this.downloadurl; let pwd = ''
if (res.data.password && res.data.password.length > 4) {
pwd = '&password=' + encodeURIComponent(res.data.password)
}
window.location.href = this.downloadurl + pwd;
if (res.data.extra_url !== "") { if (res.data.extra_url !== "") {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
this.timer = setTimeout(data => { this.timer = setTimeout(data => {
@ -671,6 +758,8 @@ export default {
window.location.reload(); window.location.reload();
} }
this.password = ''; this.password = '';
this.err_password = true;
this.currentappinfo.need_password = true;
} }
}, { }, {
'data': { 'data': {
@ -684,16 +773,22 @@ export default {
}) })
} }
}, },
qrcode() { make_qr(qrcode, x) {
let qrcode = document.getElementById("qrcode");
if (qrcode) { if (qrcode) {
new QRCode(qrcode, { new QRCode(qrcode, {
width: 100, width: x,
height: 100, height: x,
text: location.href, // text: location.href, //
}) })
} }
}, },
qrcode() {
this.make_qr(document.getElementById("qrcode"), 100)
// eslint-disable-next-line no-unused-vars
setTimeout(_ => {
this.make_qr(document.getElementById("qrcode1"), 266)
}, 1000);
},
getDownloadTokenFun() { getDownloadTokenFun() {
let params = { let params = {
"short": this.$route.params.short, "short": this.$route.params.short,
@ -710,7 +805,17 @@ export default {
getShortAppinfo(data => { getShortAppinfo(data => {
if (data.code === 1000) { if (data.code === 1000) {
if (data.redirect && data.data) { if (data.redirect && data.data) {
window.location.href = data.data; let href = data.data;
if (this.$route.query.password) {
if (href.indexOf('?') !== -1) {
if (href.indexOf('password=') === -1) {
href = href + '&password=' + this.$route.query.password
}
} else {
href = href + '?password=' + this.$route.query.password
}
}
window.location.href = href;
return return
} }
if (data.ad && data.ad.ad_uri) { if (data.ad && data.ad.ad_uri) {
@ -821,6 +926,9 @@ export default {
if (this.mcurrentappinfo.release_type === 2 && !this.currentappinfo.issupersign) { if (this.mcurrentappinfo.release_type === 2 && !this.currentappinfo.issupersign) {
this.signhelplist = this.inhousehelplist; this.signhelplist = this.inhousehelplist;
} }
if (this.agent !== '') {
this.check_msg();
}
} else if (data.code === 1002) { } else if (data.code === 1002) {
window.location.href = location.href.replace(location.search, ''); window.location.href = location.href.replace(location.search, '');
} else if (data.code === 302 && data.data) { } else if (data.code === 302 && data.data) {
@ -894,15 +1002,7 @@ export default {
this.getDownloadTokenFun(); this.getDownloadTokenFun();
this.full_url = location.href; this.full_url = location.href;
this.qrcode(); this.qrcode();
if (this.agent !== '') {
this.check_msg();
}
}, filters: { }, filters: {
formatName: function (name) {
if (name) {
return name.replace("麻将", "").replace("斗地主", "").replace("棋牌", "")
}
},
getiOStype: function (type) { getiOStype: function (type) {
let ftype = ''; let ftype = '';
if (type === 1) { if (type === 1) {
@ -913,10 +1013,7 @@ export default {
return ftype return ftype
}, },
formatTime: function (stime) { formatTime: function (stime) {
if (stime) { return format_time(stime)
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
}
} }
} }
}; };
@ -925,6 +1022,27 @@ export default {
</script> </script>
<style scoped> <style scoped>
.imgloading {
margin-top: 143px;
height: 100px;
width: 100px;
border-radius: 50%;
-webkit-animation: rotate 6s linear infinite;
animation: rotate 6s linear infinite;
background: 0
}
.dialog {
position: fixed;
width: 390px;
background-color: #fff;
left: 50%;
top: 50%;
padding: 20px;
box-shadow: 0 0 5px rgba(0, 0, 0, .5);
transform: translate(-50%, -50%)
}
.one-key-report { .one-key-report {
color: #fff; color: #fff;
background-color: #32B2A7; background-color: #32B2A7;
@ -1447,6 +1565,19 @@ button:focus {
.main > header .scan-tips { .main > header .scan-tips {
display: none display: none
} }
.dialog {
height: 100%;
width: 100%;
margin: 0;
left: 0;
top: 60px;
bottom: 0;
border-radius: 0;
padding-top: 30px;
overflow-y: auto;
transform: translate(0, 0)
}
} }
@ -1647,6 +1778,24 @@ button:focus {
} }
} }
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) {
#actions {
display: block
}
}
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: portrait) {
#actions {
display: block
}
}
@media only screen and (min-device-width: 1024px) and (max-device-width: 1366px) and (orientation: landscape) {
#actions {
display: block
}
}
#actions.type-android { #actions.type-android {
display: block display: block
} }
@ -1767,7 +1916,7 @@ body.hidden-overflow {
cursor: move; cursor: move;
} }
.signhelp-title span a { .signhelp-title span a, .ad_title span a {
text-decoration: none; text-decoration: none;
border: 1px solid gray; border: 1px solid gray;
font-size: 12px; font-size: 12px;
@ -1785,4 +1934,13 @@ body.hidden-overflow {
top: 38px; top: 38px;
right: -6px; right: -6px;
} }
.ad_title .closead {
right: -6px;
}
.ad_title span a {
line-height: 40px;
margin-bottom: 20px;
}
</style> </style>

@ -18,7 +18,7 @@
</div> </div>
<div> <div>
<div :underline="false" style="width: 200px; margin: 0 auto; padding: 5px 0;"> <div style="width: 200px; margin: 0 auto; padding: 5px 0;">
<el-link :href="footer.ipcBeiAn.url" :underline="false" rel="nofollow noopener" <el-link :href="footer.ipcBeiAn.url" :underline="false" rel="nofollow noopener"
target="_blank">{{ footer.ipcBeiAn.text }} target="_blank">{{ footer.ipcBeiAn.text }}
</el-link> </el-link>
@ -28,22 +28,12 @@
</template> </template>
<script> <script>
const footer = process.env.base_env.footer
export default { export default {
name: "FirFooter", name: "FirFooter",
data() { data() {
return { return {
footer: { footer
copyright: 'Copyright © 2020-2022 东城飞阳 版权所有.',
ipcBeiAn: {
url: 'https://beian.miit.gov.cn',
text: '粤ICP备2021097895号',
},
gongAnBeiAn: {
url: 'http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=41142202000062',
text: '豫公网安备41142202000062号',
},
}
} }
} }
} }

@ -51,6 +51,8 @@
<el-dropdown-item command="userinfo">个人资料</el-dropdown-item> <el-dropdown-item command="userinfo">个人资料</el-dropdown-item>
<el-dropdown-item command="apitoken">API token</el-dropdown-item> <el-dropdown-item command="apitoken">API token</el-dropdown-item>
<el-dropdown-item command="setdomian">设置域名</el-dropdown-item> <el-dropdown-item command="setdomian">设置域名</el-dropdown-item>
<el-dropdown-item command="download">下载配置</el-dropdown-item>
<el-dropdown-item command="setnotify">消息中心</el-dropdown-item>
<el-dropdown-item v-if="$store.state.userinfo.role>1" command="setadvert">宣传广告 <el-dropdown-item v-if="$store.state.userinfo.role>1" command="setadvert">宣传广告
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item v-if="$store.state.userinfo.storage_active" command="storage">存储管理 <el-dropdown-item v-if="$store.state.userinfo.storage_active" command="storage">存储管理
@ -59,6 +61,7 @@
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item command="myorder">订单信息</el-dropdown-item> <el-dropdown-item command="myorder">订单信息</el-dropdown-item>
<el-dropdown-item command="contact">联系我们</el-dropdown-item> <el-dropdown-item command="contact">联系我们</el-dropdown-item>
<el-dropdown-item command="qrcode">下载码</el-dropdown-item>
<el-dropdown-item command="exit">退出</el-dropdown-item> <el-dropdown-item command="exit">退出</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
@ -133,10 +136,16 @@ export default {
} else if (command === 'setdomian') { } else if (command === 'setdomian') {
this.$router.push({"name": 'FirUserDomain'}) this.$router.push({"name": 'FirUserDomain'})
} else if (command === 'download') {
this.$router.push({"name": 'FirUserDownload'})
} else if (command === 'setadvert') { } else if (command === 'setadvert') {
this.$router.push({"name": 'FirUserAdvert'}) this.$router.push({"name": 'FirUserAdvert'})
} else if (command === 'myorder') { } else if (command === 'myorder') {
this.$router.push({"name": 'FirUserOrders'}) this.$router.push({"name": 'FirUserOrders'})
} else if (command === 'setnotify') {
this.$router.push({"name": 'FirUserNotify'})
} else if (command === 'qrcode') {
this.$router.push({"name": 'FirUserQrcode'})
} else if (command === 'contact') { } else if (command === 'contact') {
this.$router.push({"name": 'FirContact'}) this.$router.push({"name": 'FirContact'})
} else if (command === 'exit') { } else if (command === 'exit') {

@ -44,7 +44,7 @@
<el-form-item v-if="captcha.captcha_image" style="height: 40px"> <el-form-item v-if="captcha.captcha_image" style="height: 40px">
<el-row style="height: 40px"> <el-row style="height: 40px">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="form.authcode" clearable maxlength="6" <el-input v-model="form.verify_code" clearable maxlength="6"
placeholder="请输入验证码" @keyup.enter.native="onSubmit"/> placeholder="请输入验证码" @keyup.enter.native="onSubmit"/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
@ -74,7 +74,29 @@
<div v-if="allow_ways.third && JSON.stringify(allow_ways.third).indexOf('true')!==-1" class="other-way"> <div v-if="allow_ways.third && JSON.stringify(allow_ways.third).indexOf('true')!==-1" class="other-way">
<hr> <hr>
<span class="info">或使用以下账户登录</span> <el-popover
v-model="showuserlist"
placement="top"
title="该微信绑定了多个用户,请选择账户,并点击登录"
trigger="manual"
width="400">
<el-row style="margin-top: 20px">
<el-col v-for="userinfo in userinfo_list" :key="userinfo.uid" :span="8" style="margin-left: 40px">
<div @click="weixinlogin(userinfo)">
<el-card :body-style="{textAlign:'center',padding:'8px',width:'120px'}" shadow="hover">
<!-- <el-image :src="userinfo.head_img"></el-image>-->
<el-avatar :size="100" :src="userinfo.head_img"></el-avatar>
<div style="margin: 5px 0 5px">
<span>{{ userinfo.first_name }}</span>
</div>
</el-card>
</div>
</el-col>
</el-row>
<span slot="reference" class="info">或使用以下账户登录</span>
</el-popover>
<el-popover <el-popover
v-if="allow_ways.third && allow_ways.third.wxp" v-if="allow_ways.third && allow_ways.third.wxp"
@ -103,8 +125,9 @@
</template> </template>
<script> <script>
import {loginFun, set_auth_token, wxLoginFun} from "@/restful"; import {loginFun, wxLoginFun} from "@/restful";
import {checkEmail, checkphone, geetest} from "@/utils"; import {geetest} from "@/utils";
import {checkEmail, checkphone, getRandomStr} from "@/utils/base/utils";
export default { export default {
name: "FirLogin", name: "FirLogin",
@ -113,9 +136,10 @@ export default {
form: { form: {
email: '', email: '',
password: '', password: '',
authcode: '' verify_code: ''
}, },
captcha: {"captcha_image": '', "captcha_key": '', "length": 8}, captcha: {"captcha_image": '', "captcha_key": '', "length": 8},
auth_rules: {},
activeName: 'username', activeName: 'username',
allow_ways: {'third': {}}, allow_ways: {'third': {}},
rutitle: '', rutitle: '',
@ -125,53 +149,75 @@ export default {
wx_login_qr_url: '', wx_login_qr_url: '',
wx_visible: false, wx_visible: false,
loop_flag: false, loop_flag: false,
unique_key: '',
userinfo_list: [],
unique_wid: '',
showuserlist: false
} }
}, },
methods: { methods: {
weixinlogin(userinfo) {
wxLoginFun(data => {
if (data.code === 1000) {
this.set_cookie_and_token(data);
} else if (data.code === 1005) {
this.$message.error(data.msg)
} else {
this.showuserlist = false
this.$message.error(data.msg)
}
}, {
'methods': 'PUT',
'data': {uid: userinfo.uid, token: userinfo.token, 'wid': this.unique_wid}
})
},
set_cookie_and_token(data) { set_cookie_and_token(data) {
this.$cookies.remove("auth_token"); this.$cookies.remove("auth_token");
this.$cookies.set("token", data['token'], 3600 * 24 * 30); this.$cookies.set("token", data['token'], 3600 * 24 * 30);
this.$cookies.set("username", data.userinfo.username, 3600 * 24 * 30); this.$cookies.set("username", data.userinfo.username, 3600 * 24 * 30);
this.$cookies.set("first_name", data.userinfo.first_name, 3600 * 24 * 30); this.$cookies.set("first_name", data.userinfo.first_name, 3600 * 24 * 30);
this.$store.dispatch("doUserinfo", data.userinfo); this.$store.dispatch("doUserinfo", data.userinfo);
set_auth_token();
this.$router.push({name: 'FirApps'}) this.$router.push({name: 'FirApps'})
}, },
loop_get_wx_info(wx_login_ticket) { loop_get_wx_info(wx_login_ticket, c_count = 1, unique_key = getRandomStr()) {
if (wx_login_ticket && wx_login_ticket.length < 3) { if (wx_login_ticket && wx_login_ticket.length < 3) {
this.$message.error("获取登陆码失败,请稍后再试"); this.$message.error("获取登陆码失败,请稍后再试");
return return
} }
let c_count = 1; if (!this.wx_visible) {
// eslint-disable-next-line no-unused-vars return;
const loop_t = window.setInterval(res => { }
if (!this.loop_flag) { wxLoginFun(data => {
window.clearInterval(loop_t); c_count += 1;
if (c_count > 30) {
return;
} }
wxLoginFun(data => { if (data.code === 1000) {
c_count += 1; this.set_cookie_and_token(data);
if (c_count > 120) { } else if (data.code === 1005) {
window.clearInterval(loop_t); this.wx_visible = false;
} this.loop_flag = false;
if (data.code === 1000) { this.$message({
window.clearInterval(loop_t); message: data.msg,
this.set_cookie_and_token(data); type: 'error',
} else if (data.code === 1005) { duration: 30000
window.clearInterval(loop_t); });
this.wx_visible = false; } else if (data.code === 1004) {
this.loop_flag = false; this.loop_flag = false;
this.$message({ } else if (data.code === 1006) {
message: data.msg, return this.loop_get_wx_info(wx_login_ticket, c_count, unique_key)
type: 'error', } else if (data.code === 2000) {
duration: 30000 this.wx_visible = false;
}); this.loop_flag = false;
} this.userinfo_list = data.data;
}, { this.unique_wid = data.wid;
"methods": "POST", this.showuserlist = true
data: {"ticket": wx_login_ticket} }
}) }, {
}, 3000) "methods": "POST",
data: {"ticket": wx_login_ticket, "unique_key": unique_key}
})
// }, 3000)
}, },
wxLogin() { wxLogin() {
@ -189,14 +235,14 @@ export default {
this.loop_flag = false; this.loop_flag = false;
} }
}, { }, {
"methods": "GET", "methods": "GET", "data": {"unique_key": this.unique_key}
}) })
} else { } else {
this.loop_flag = false; this.loop_flag = false;
} }
}, },
is_captcha() { is_captcha() {
let captcha_flag = this.form.authcode.length === this.captcha.length; let captcha_flag = this.form.verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true captcha_flag = true
} }
@ -205,9 +251,9 @@ export default {
onSubmit() { onSubmit() {
let email = this.form.email; let email = this.form.email;
let password = this.form.password; let password = this.form.password;
let authcode = this.form.authcode; let verify_code = this.form.verify_code;
let login_type = 'up'; let login_type = 'up';
let captcha_flag = this.form.authcode.length === this.captcha.length; let captcha_flag = this.form.verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true captcha_flag = true
} }
@ -244,16 +290,16 @@ export default {
}); });
return return
} }
if (password.length > 6) { if (password.length >= 6) {
let params = { let params = {
"username": email, "username": email,
"password": password, "password": password,
"authcode": authcode, "verify_code": verify_code,
"captcha_key": this.captcha.captcha_key, "captcha_key": this.captcha.captcha_key,
"login_type": login_type, "login_type": login_type,
}; };
this.login_disable = true; this.login_disable = true;
if (this.captcha.geetest) { if (this.auth_rules.geetest) {
geetest(this, this.form.email, params, (n_params) => { geetest(this, this.form.email, params, (n_params) => {
this.do_login(n_params); this.do_login(n_params);
}) })
@ -318,10 +364,11 @@ export default {
get_auth_code() { get_auth_code() {
loginFun(data => { loginFun(data => {
if (data.code === 1000) { if (data.code === 1000) {
this.captcha = data.data; this.auth_rules = data.data.auth_rules
this.captcha = this.auth_rules.captcha;
this.allow_ways = data.data.login_type; this.allow_ways = data.data.login_type;
this.register_enable = data.data.register_enable; this.register_enable = data.data.register_enable;
this.form.authcode = ''; this.form.verify_code = '';
this.set_rtitle(); this.set_rtitle();
this.set_activename(); this.set_activename();
} else { } else {
@ -338,6 +385,7 @@ export default {
}, },
mounted() { mounted() {
this.get_auth_code(); this.get_auth_code();
this.unique_key = getRandomStr();
}, created() { }, created() {
} }
} }

@ -29,7 +29,7 @@
<el-form-item v-if="captcha.captcha_image" style="height: 40px"> <el-form-item v-if="captcha.captcha_image" style="height: 40px">
<el-row style="height: 40px"> <el-row style="height: 40px">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="form.authcode" clearable maxlength="6" placeholder="请输入图片验证码"/> <el-input v-model="form.verify_code" clearable maxlength="6" placeholder="请输入图片验证码"/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-image <el-image
@ -88,7 +88,8 @@
<script> <script>
import {getAuthTokenFun, registerFun} from "@/restful"; import {getAuthTokenFun, registerFun} from "@/restful";
import {checkEmail, checkphone, geetest} from "@/utils"; import {geetest} from "@/utils";
import {checkEmail, checkphone} from "@/utils/base/utils";
export default { export default {
name: "FirRegist", name: "FirRegist",
@ -98,7 +99,7 @@ export default {
email: '', email: '',
password: '', password: '',
password2: '', password2: '',
authcode: '', verify_code: '',
srccode: '', srccode: '',
seicode: '', seicode: '',
authtoken: '', authtoken: '',
@ -107,6 +108,7 @@ export default {
}, },
captcha: {"captcha_image": '', "captcha_key": '', "length": 8}, captcha: {"captcha_image": '', "captcha_key": '', "length": 8},
auth_rules: {},
allow_r: false, allow_r: false,
allow_ways: {}, allow_ways: {},
rutitle: '', rutitle: '',
@ -132,9 +134,10 @@ export default {
if (jdata.enable) { if (jdata.enable) {
this.allow_r = true; this.allow_r = true;
this.allow_ways = jdata.register_type; this.allow_ways = jdata.register_type;
this.form.authcode = ''; this.form.verify_code = '';
this.set_rtitle(); this.set_rtitle();
this.captcha = data.data; this.auth_rules = data.data.auth_rules;
this.captcha = this.auth_rules.captcha;
} else { } else {
this.allow_r = false; this.allow_r = false;
this.$message({ this.$message({
@ -181,19 +184,19 @@ export default {
if (checkEmail(this.form.email)) { if (checkEmail(this.form.email)) {
act = 'email' act = 'email'
} }
let authcode = this.form.authcode; let verify_code = this.form.verify_code;
let captcha_flag = authcode.length === this.captcha.length; let captcha_flag = verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true captcha_flag = true
} }
if (captcha_flag) { if (captcha_flag) {
let picode = { let picode = {
"authcode": authcode, "verify_code": verify_code,
"captcha_key": this.captcha.captcha_key, "captcha_key": this.captcha.captcha_key,
"icode": this.form.icode, "icode": this.form.icode,
}; };
let params = {'act': act, 'target': this.form.email, 'ext': picode}; let params = {'act': act, 'target': this.form.email, 'ext': picode};
if (this.captcha.geetest) { if (this.auth_rules.geetest) {
geetest(this, this.form.email, params, (n_params) => { geetest(this, this.form.email, params, (n_params) => {
this.do_get_auth_token(n_params); this.do_get_auth_token(n_params);
}) })
@ -277,8 +280,8 @@ export default {
if (!this.docheck()) { if (!this.docheck()) {
return return
} }
let authcode = this.form.authcode; let verify_code = this.form.verify_code;
let captcha_flag = authcode.length === this.captcha.length; let captcha_flag = verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true captcha_flag = true
} }
@ -292,7 +295,7 @@ export default {
"auth_key": this.form.seicode "auth_key": this.form.seicode
}; };
this.register_disable = true; this.register_disable = true;
if (this.captcha.geetest) { if (this.auth_rules.geetest) {
geetest(this, this.form.email, params, (n_params) => { geetest(this, this.form.email, params, (n_params) => {
this.do_register(n_params); this.do_register(n_params);
}) })

@ -20,7 +20,7 @@
<el-form-item v-if="captcha.captcha_image" style="height: 40px"> <el-form-item v-if="captcha.captcha_image" style="height: 40px">
<el-row style="height: 40px"> <el-row style="height: 40px">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="form.authcode" clearable maxlength="6" <el-input v-model="form.verify_code" clearable maxlength="6"
placeholder="请输入验证码" @keyup.enter.native="onSubmit"/> placeholder="请输入验证码" @keyup.enter.native="onSubmit"/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
@ -72,7 +72,8 @@
<script> <script>
import {loginFun} from "@/restful"; import {loginFun} from "@/restful";
import {checkEmail, checkphone, geetest} from "@/utils"; import {geetest} from "@/utils";
import {checkEmail, checkphone} from "@/utils/base/utils";
export default { export default {
name: "FirResetPwd", name: "FirResetPwd",
@ -81,11 +82,12 @@ export default {
form: { form: {
email: '', email: '',
password: '', password: '',
authcode: '', verify_code: '',
seicode: '', seicode: '',
auth_token: '' auth_token: ''
}, },
captcha: {"captcha_image": '', "captcha_key": '', "length": 8}, captcha: {"captcha_image": '', "captcha_key": '', "length": 8},
auth_rules: {},
activeName: 'username', activeName: 'username',
allow_ways: {}, allow_ways: {},
rutitle: '', rutitle: '',
@ -108,7 +110,7 @@ export default {
} }
}, },
is_captcha() { is_captcha() {
let captcha_flag = this.form.authcode.length === this.captcha.length; let captcha_flag = this.form.verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true captcha_flag = true
} }
@ -116,8 +118,8 @@ export default {
}, },
onSubmit() { onSubmit() {
let email = this.form.email; let email = this.form.email;
let authcode = this.form.authcode; let verify_code = this.form.verify_code;
let captcha_flag = this.form.authcode.length === this.captcha.length; let captcha_flag = this.form.verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true captcha_flag = true
} }
@ -127,7 +129,7 @@ export default {
if (checke || checkp) { if (checke || checkp) {
let params = { let params = {
"username": email, "username": email,
"authcode": authcode, "verify_code": verify_code,
"captcha_key": this.captcha.captcha_key, "captcha_key": this.captcha.captcha_key,
"login_type": 'reset', "login_type": 'reset',
}; };
@ -140,7 +142,7 @@ export default {
params['auth_token'] = auth_token params['auth_token'] = auth_token
} }
this.login_disable = true; this.login_disable = true;
if (this.captcha.geetest) { if (this.auth_rules.geetest) {
geetest(this, this.form.email, params, (n_params) => { geetest(this, this.form.email, params, (n_params) => {
this.do_login(n_params); this.do_login(n_params);
}) })
@ -196,9 +198,10 @@ export default {
get_auth_code() { get_auth_code() {
loginFun(data => { loginFun(data => {
if (data.code === 1000) { if (data.code === 1000) {
this.captcha = data.data; this.auth_rules = data.data.auth_rules;
this.captcha = this.auth_rules.captcha;
this.register_enable = data.data.register_enable; this.register_enable = data.data.register_enable;
this.form.authcode = ''; this.form.verify_code = '';
} else { } else {
this.$message({ this.$message({
message: data.msg, message: data.msg,

@ -43,7 +43,7 @@
</div> </div>
<div class="content-row"> <div class="content-row">
<div v-if="captcha.captcha_image" class="report_captcha"> <div v-if="captcha.captcha_image" class="report_captcha">
<input v-model="report_info.authcode" maxlength="6" placeholder="请输入图片验证码" type="text"/> <input v-model="report_info.verify_code" maxlength="6" placeholder="请输入图片验证码" type="text"/>
<img :src="captcha.captcha_image" alt="验证码" @click="get_auth_code"/> <img :src="captcha.captcha_image" alt="验证码" @click="get_auth_code"/>
</div> </div>
@ -294,15 +294,14 @@
<span style="color: #ebb563">若安装异常请复制UDID发送给管理员</span> <span style="color: #ebb563">若安装异常请复制UDID发送给管理员</span>
</div> </div>
<div class="footer" style="margin-top: 30%;margin-bottom: 8px"> <div class="footer" style="margin-top: 30%;padding-bottom: 10px">
免责声明<br> 免责声明<br>
本网站仅提供下载托管应用为用户自行上传请甄别应用风险后进行下载<a class="one-key-report" 本网站仅提供下载托管应用为用户自行上传请甄别应用风险后进行下载<a class="one-key-report"
@click="get_auth_code(),report_flag=true">举报</a> @click="get_auth_code(),report_flag=true">举报</a>
</div> </div>
<div v-if="agent!==''"> <div v-if="agent!=='' && ad_info.ad_uri" style="padding-bottom: 80px">
<div v-if="ad_info.ad_uri" style="margin-bottom: 80px"/> <div class="app_bottom_fixed">
<div v-if="ad_info.ad_uri" class="app_bottom_fixed"> <div class="ad_title">
<div class="signhelp-title">
<span><a class="closead" @click="ad_info.ad_uri=undefined">关闭</a></span> <span><a class="closead" @click="ad_info.ad_uri=undefined">关闭</a></span>
</div> </div>
<a :href="ad_info.ad_uri" target="_blank"> <a :href="ad_info.ad_uri" target="_blank">
@ -310,17 +309,20 @@
</a> </a>
</div> </div>
</div> </div>
<div v-else style="padding-bottom: 10px"></div>
</div> </div>
</div> </div>
<div v-else> <div v-else style="text-align:center">
<i v-if="!report_flag"> <img v-if="mcurrentappinfo.icon_url" :src="mcurrentappinfo.icon_url" alt="" class="imgloading">
{{ this.currentappinfo.name | formatName }} <div v-if="wrong && msg" style="height: 20px;margin-bottom: 10px;"> {{ this.msg }}</div>
</i> <div v-if="!report_flag && !iserror" style="margin:0 auto;z-index: 10">
<span id="qrcode1" style="display: inline-block"></span>
<div style="margin-top: 10px">{{ this.currentappinfo.name }}</div>
</div>
</div> </div>
<div v-if='iserror' class="main"> <div v-if='iserror' class="main">
<div class="error-container"> <div class="error-container">
<h1>{{ error_msg.head }}</h1> <h1>{{ error_msg.head }}</h1>
@ -328,6 +330,7 @@
</header> </header>
</div> </div>
</div> </div>
</div> </div>
@ -336,28 +339,21 @@
<script> <script>
import QRCode from 'qrcodejs2' import QRCode from 'qrcodejs2'
import { import {appReport, geetest, getAuthTokenFun, getdownloadurl, getShortAppinfo, gettask} from '@/restful/download'
appReport, import {checkEmail, checkphone, format_time, getRandomStr} from "@/utils/base/utils";
checkEmail,
checkphone,
geetest,
getAuthTokenFun,
getdownloadurl,
getShortAppinfo,
gettask
} from '@/restful/download'
export default { export default {
name: "ShortDownload", name: "ShortDownload",
data() { data() {
return { return {
captcha: {"captcha_image": '', "captcha_key": '', "length": 8}, captcha: {"captcha_image": '', "captcha_key": '', "length": 8},
auth_rules: {},
report_info: { report_info: {
'username': '', 'username': '',
'report_type': '', 'report_type': '',
'report_reason': '', 'report_reason': '',
'seicode': '', 'seicode': '',
'authcode': '', 'verify_code': '',
'email': '', 'email': '',
'auth_token': '', 'auth_token': '',
'act': 'email', 'act': 'email',
@ -408,7 +404,10 @@ export default {
timer: '', timer: '',
gomobile: true, gomobile: true,
mobileprovision: '', mobileprovision: '',
ad_info: {ad_pic: '', ad_uri: ''} ad_info: {ad_pic: '', ad_uri: ''},
task_spend_time: 0,
task_msg: '执行中',
err_password: false
} }
}, },
beforeDestroy() { beforeDestroy() {
@ -456,12 +455,12 @@ export default {
} }
let picode = { let picode = {
"authcode": this.report_info.authcode, "verify_code": this.report_info.verify_code,
"captcha_key": this.captcha.captcha_key, "captcha_key": this.captcha.captcha_key,
"report": this.currentappinfo.app_id "report": this.currentappinfo.app_id
}; };
let params = {'act': this.report_info.act, 'target': this.report_info.email, 'ext': picode}; let params = {'act': this.report_info.act, 'target': this.report_info.email, 'ext': picode};
if (this.captcha.geetest) { if (this.auth_rules.geetest) {
geetest(this, this.report_info.email, params, (n_params) => { geetest(this, this.report_info.email, params, (n_params) => {
this.do_get_auth_token(n_params); this.do_get_auth_token(n_params);
}) })
@ -484,7 +483,8 @@ export default {
if (data.code === 1000) { if (data.code === 1000) {
let jdata = data.data; let jdata = data.data;
if (jdata.enable) { if (jdata.enable) {
this.captcha = data.data; this.auth_rules = data.data.auth_rules;
this.captcha = this.auth_rules.captcha;
if (data.data.s_list) { if (data.data.s_list) {
this.report_info_list = data.data.s_list; this.report_info_list = data.data.s_list;
} }
@ -511,36 +511,51 @@ export default {
this.msg = msg; this.msg = msg;
alert(this.msg) alert(this.msg)
}, },
loop_check_task() { loop_check_task(c_count = 1, loop_t, unique_key = getRandomStr()) {
let c_count = 1; if (c_count === 1) {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const loop_t = window.setInterval(res => { loop_t = window.setInterval(res => {
gettask(data => { this.task_spend_time += 1;
c_count += 1; this.msg = this.task_msg + ' ○ ' + this.task_spend_time;
if (c_count > 120) { }, 3000)
window.clearInterval(loop_t); }
} gettask(data => {
if (data.code === 1000) { c_count += 1;
window.clearInterval(loop_t); if (c_count > 30) {
if (data.msg) { window.clearInterval(loop_t);
this.show_err_msg(data.msg); return;
} else { }
if (data.code === 1000) {
window.clearInterval(loop_t);
if (data.data) {
if (data.data.code === 1000) {
this.wrong = false; this.wrong = false;
} this.binary_download();
} else { } else {
if (data.code === 1002) { if (data.data.redirect) {
window.clearInterval(loop_t); window.location.href = data.data.redirect
data.msg = '应用不存在' } else if (data.data.msg) {
} this.show_err_msg(data.data.msg);
if (data.msg) { }
this.msg = data.msg + ' ○ ' + c_count;
} }
} }
}, { } else {
"short": this.$route.params.short, if (data.code === 1002) {
data: {"task_id": this.$route.query.task_id} window.clearInterval(loop_t);
}) data.msg = '应用不存在'
}, 3000) }
if (data.msg) {
this.task_msg = data.msg;
}
if (data.code === 1001) {
this.loop_check_task(c_count, loop_t, unique_key)
}
}
}, {
methods: 'GET',
short: this.$route.params.short,
data: {"task_id": this.$route.query.task_id, "unique_key": unique_key}
})
}, },
jiaocheng(act) { jiaocheng(act) {
let signhelp = this.$refs.signhelp; let signhelp = this.$refs.signhelp;
@ -558,18 +573,83 @@ export default {
window.location.href = this.mobileprovision; window.location.href = this.mobileprovision;
}, },
check_msg() { check_msg() {
if (this.$route.query.udid) { if (this.agent === 'wxandroid' || this.agent === 'wxapple') {
if (this.$route.query.task_id) { return
this.wrong = true; }
this.msg = '签名处理中,请耐心等待'; if (this.$route.query.msg) {
this.loop_check_task() this.wrong = true;
} else if (this.$route.query.msg) { this.show_err_msg(this.$route.query.msg);
this.wrong = true; this.currentappinfo.need_password = false;
this.show_err_msg(this.$route.query.msg); return;
}
if (this.currentappinfo.need_password) {
if (this.currentappinfo.issupersign && !this.$route.query.udid) {
this.currentappinfo.need_password = false
}
if (this.$route.query.password && !this.err_password) {
this.currentappinfo.need_password = false
this.password = this.$route.query.password;
// eslint-disable-next-line no-unused-vars
this.timer = setTimeout(data => {
this.download();
}, 1000);
}
} else {
if (this.$route.query.udid && this.$route.query.task_token) {
this.download()
} }
} }
}, },
download() { download() {
if (this.$route.query.udid && this.$route.query.task_token) {
this.wrong = true;
this.msg = '签名处理中,请耐心等待';
gettask(data => {
if (data.code === 1000) {
if (data.data) {
if (data.data.code === 1000) {
this.wrong = false;
this.currentappinfo.need_password = false
this.binary_download()
} else {
if (data.data.redirect) {
window.location.href = data.data.redirect
} else if (data.data.msg) {
this.show_err_msg(data.data.msg);
}
}
} else if (data.task_id) {
this.$route.query.task_id = data.task_id;
this.loop_check_task()
}
} else if (data.code === 1006) {
this.msg = data.msg;
this.show_err_msg(data.msg);
this.wrong = false;
this.err_password = true;
this.currentappinfo.need_password = true;
this.password = ''
} else if (data.code === 1005) {
this.msg = data.msg;
this.show_err_msg(data.msg);
this.wrong = true;
} else {
this.show_err_msg(data.msg);
// eslint-disable-next-line no-unused-vars
this.timer = setTimeout(data => {
window.location.href = location.href.replace(location.search, '');
}, 3000);
}
}, {
methods: 'POST',
short: this.$route.params.short,
data: {"task_token": this.$route.query.task_token, 'password': this.password}
})
} else {
this.binary_download()
}
},
binary_download() {
if (this.currentappinfo.app_id) { if (this.currentappinfo.app_id) {
this.isdownload = true; this.isdownload = true;
getdownloadurl(res => { getdownloadurl(res => {
@ -592,7 +672,11 @@ export default {
} else { } else {
if (this.agent !== '') { if (this.agent !== '') {
this.downloadurl = res.data.download_url; this.downloadurl = res.data.download_url;
window.location.href = this.downloadurl; let pwd = ''
if (res.data.password && res.data.password.length > 4) {
pwd = '&password=' + encodeURIComponent(res.data.password)
}
window.location.href = this.downloadurl + pwd;
if (res.data.extra_url !== "") { if (res.data.extra_url !== "") {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
this.timer = setTimeout(data => { this.timer = setTimeout(data => {
@ -633,6 +717,8 @@ export default {
window.location.reload(); window.location.reload();
} }
this.password = ''; this.password = '';
this.err_password = true;
this.currentappinfo.need_password = true;
} }
}, { }, {
'data': { 'data': {
@ -646,16 +732,22 @@ export default {
}) })
} }
}, },
qrcode() { make_qr(qrcode, x) {
let qrcode = document.getElementById("qrcode");
if (qrcode) { if (qrcode) {
new QRCode(qrcode, { new QRCode(qrcode, {
width: 100, width: x,
height: 100, height: x,
text: location.href, // text: location.href, //
}) })
} }
}, },
qrcode() {
this.make_qr(document.getElementById("qrcode"), 100)
// eslint-disable-next-line no-unused-vars
setTimeout(_ => {
this.make_qr(document.getElementById("qrcode1"), 266)
}, 1000);
},
getDownloadTokenFun() { getDownloadTokenFun() {
let params = { let params = {
"short": this.$route.params.short, "short": this.$route.params.short,
@ -672,7 +764,17 @@ export default {
getShortAppinfo(data => { getShortAppinfo(data => {
if (data.code === 1000) { if (data.code === 1000) {
if (data.redirect && data.data) { if (data.redirect && data.data) {
window.location.href = data.data; let href = data.data;
if (this.$route.query.password) {
if (href.indexOf('?') !== -1) {
if (href.indexOf('password=') === -1) {
href = href + '&password=' + this.$route.query.password
}
} else {
href = href + '?password=' + this.$route.query.password
}
}
window.location.href = href;
return return
} }
if (data.ad && data.ad.ad_uri) { if (data.ad && data.ad.ad_uri) {
@ -779,6 +881,9 @@ export default {
if (this.mcurrentappinfo.release_type === 2 && !this.currentappinfo.issupersign) { if (this.mcurrentappinfo.release_type === 2 && !this.currentappinfo.issupersign) {
this.signhelplist = this.inhousehelplist; this.signhelplist = this.inhousehelplist;
} }
if (this.agent !== '') {
this.check_msg();
}
} else if (data.code === 1002) { } else if (data.code === 1002) {
window.location.href = location.href.replace(location.search, ''); window.location.href = location.href.replace(location.search, '');
} else if (data.code === 302 && data.data) { } else if (data.code === 302 && data.data) {
@ -855,15 +960,7 @@ export default {
this.getDownloadTokenFun(); this.getDownloadTokenFun();
this.full_url = location.href; this.full_url = location.href;
this.qrcode(); this.qrcode();
if (this.agent !== '') {
this.check_msg();
}
}, filters: { }, filters: {
formatName: function (name) {
if (name) {
return name.replace("麻将", "").replace("斗地主", "").replace("棋牌", "")
}
},
getiOStype: function (type) { getiOStype: function (type) {
let ftype = ''; let ftype = '';
if (type === 1) { if (type === 1) {
@ -874,10 +971,7 @@ export default {
return ftype return ftype
}, },
formatTime: function (stime) { formatTime: function (stime) {
if (stime) { return format_time(stime)
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
}
} }
} }
}; };
@ -886,6 +980,17 @@ export default {
</script> </script>
<style scoped> <style scoped>
.imgloading {
height: 100px;
width: 100px;
border-radius: 50%;
margin: 10px 0 20px;
-webkit-animation: rotate 6s linear infinite;
animation: rotate 6s linear infinite;
background: 0
}
.dialog { .dialog {
position: fixed; position: fixed;
width: 390px; width: 390px;
@ -977,7 +1082,7 @@ export default {
color: #8B8E93 color: #8B8E93
} }
.one-key-report-dialog .content-row .report_captcha { .one-key-report-dialog .content-row .report_captcha, .report_phone {
display: -ms-flexbox; display: -ms-flexbox;
display: flex display: flex
} }
@ -992,6 +1097,7 @@ export default {
width: 125px; width: 125px;
height: 50px; height: 50px;
background: #F5F5F5; background: #F5F5F5;
padding: 5px 10px;
cursor: pointer cursor: pointer
} }
@ -1007,7 +1113,6 @@ export default {
height: 50px; height: 50px;
background-color: #3888fa; background-color: #3888fa;
padding: 5px 10px; padding: 5px 10px;
margin-top: -5px;
font-size: 14px font-size: 14px
} }
@ -1593,6 +1698,7 @@ button:focus {
.dialog { .dialog {
height: 100%; height: 100%;
width: 100%;
margin: 0; margin: 0;
left: 0; left: 0;
top: 60px; top: 60px;
@ -1603,6 +1709,15 @@ button:focus {
transform: translate(0, 0) transform: translate(0, 0)
} }
.one-key-report-dialog .content-row .report_captcha, .report_phone input {
width: 60%;
}
.one-key-report-dialog .content-row .report_captcha, .report_phone img, .btn {
width: 35%;
}
.dialog.animate { .dialog.animate {
transition: left .5s transition: left .5s
} }
@ -1770,6 +1885,7 @@ button:focus {
background: 0 background: 0
} }
@media only screen and (min-device-width: 320px) and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2) { @media only screen and (min-device-width: 320px) and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2) {
#actions { #actions {
display: block display: block
@ -1806,6 +1922,24 @@ button:focus {
} }
} }
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) {
#actions {
display: block
}
}
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: portrait) {
#actions {
display: block
}
}
@media only screen and (min-device-width: 1024px) and (max-device-width: 1366px) and (orientation: landscape) {
#actions {
display: block
}
}
#actions.type-android { #actions.type-android {
display: block display: block
} }
@ -1926,7 +2060,7 @@ body.hidden-overflow {
cursor: move; cursor: move;
} }
.signhelp-title span a { .signhelp-title span a, .ad_title span a {
text-decoration: none; text-decoration: none;
border: 1px solid gray; border: 1px solid gray;
font-size: 12px; font-size: 12px;
@ -1944,4 +2078,13 @@ body.hidden-overflow {
top: 38px; top: 38px;
right: -6px; right: -6px;
} }
.ad_title .closead {
right: -6px;
}
.ad_title span a {
line-height: 40px;
margin-bottom: 20px;
}
</style> </style>

@ -8,14 +8,37 @@
</div> </div>
<div class="badges"> <div class="badges">
<el-tooltip content="复制到剪切板" placement="top"> <el-tooltip content="复制到剪切板" placement="top">
<span v-clipboard:copy="short_full_url" <span v-clipboard:copy="appinfos.preview_url"
v-clipboard:success="copy_success" v-clipboard:success="copy_success"
class="bundleid short" class="bundleid short"
>&nbsp;{{ short_full_url }}</span> >&nbsp;{{ appinfos.preview_url }}</span>
</el-tooltip> </el-tooltip>
<el-tooltip content="点击查看下载码">
<el-popover
placement="right"
width="288">
<div style="text-align: center; margin: 0">
<vue-qr ref="qr"
:correctLevel="qrinfo.correctLevel" :logoCornerRadius="qrinfo.logoCornerRadius"
:logoScale="qrinfo.logoScale"
:logoSrc="icon_url"
:margin="qrinfo.margin" :size="266"
:text="appinfos.preview_url">
</vue-qr>
<el-button size="small" type="primary" @click="save_qr()">保存本地</el-button>
</div>
<span slot="reference" class="short ng-scope">下载码</span>
</el-popover>
</el-tooltip>
<span>{{ master_release.release_type |getapptype }}</span> <span>{{ master_release.release_type |getapptype }}</span>
<el-tooltip content="下载量" placement="top"> <el-tooltip placement="top">
<span><i class="el-icon-cloudy"/><b class="short">{{ appinfos.count_hits }}</b></span> <div slot="content">总下载次数{{ appinfos.count_hits }}<br/>当天下载次数{{ appinfos.today_hits_count }}</div>
<span><i class="el-icon-cloudy"/><b class="short">{{
appinfos.count_hits
}} &nbsp;|&nbsp; {{ appinfos.today_hits_count }} </b></span>
</el-tooltip> </el-tooltip>
<span class="bundleid ng-binding">BundleID<b class="ng-binding"> <span class="bundleid ng-binding">BundleID<b class="ng-binding">
<el-tooltip content="复制到剪切板" placement="top"> <el-tooltip content="复制到剪切板" placement="top">
@ -28,29 +51,10 @@
<span class="version ng-scope">{{ master_release.minimum_os_version }}&nbsp; 或者高版本</span> <span class="version ng-scope">{{ master_release.minimum_os_version }}&nbsp; 或者高版本</span>
<span v-if="appinfos.issupersign" class="short ng-scope">超级签</span> <span v-if="appinfos.issupersign" class="short ng-scope">超级签</span>
<el-popover
v-if="$store.state.userinfo&&$store.state.userinfo.role >1 &&$store.state.userinfo.qrcode_domain_name.length>3 "
placement="right"
width="288">
<div style="text-align: center; margin: 0">
<vue-qr ref="qr"
:correctLevel="qrinfo.correctLevel" :logoCornerRadius="qrinfo.logoCornerRadius"
:logoScale="qrinfo.logoScale"
:logoSrc="icon_url"
:margin="qrinfo.margin" :size="266"
:text="short_url()">
</vue-qr>
<el-button size="small" type="primary" @click="save_qr()">保存本地</el-button>
</div>
<span slot="reference" class="short ng-scope">下载码</span>
</el-popover>
</div> </div>
<div class="actions"> <div class="actions">
<el-button v-if="appinfos.status!==1" type="danger">该应用被封禁,请联系管理员</el-button> <el-button v-if="appinfos.status!==1" type="danger">该应用被封禁,请联系管理员</el-button>
<el-button v-else class="download" icon="el-icon-view" @click="appDownload"> <el-button v-else class="download" icon="el-icon-view" @click="appDownload(appinfos)">
预览 预览
</el-button> </el-button>
</div> </div>
@ -123,13 +127,12 @@ export default {
margin: 20 margin: 20
}, },
icon_url: "", icon_url: "",
appinfos: {status: 1}, appinfos: {status: 1, preview_url: ''},
master_release: {}, master_release: {},
allapp: [], allapp: [],
activity: { activity: {
editing: false editing: false
}, },
short_full_url: '',
} }
}, },
methods: { methods: {
@ -149,12 +152,6 @@ export default {
// //
a.dispatchEvent(new MouseEvent('click')) a.dispatchEvent(new MouseEvent('click'))
}, },
short_url() {
const userinfo = this.$store.state.userinfo;
if (userinfo && userinfo.role > 1 && userinfo.qrcode_domain_name.length > 3) {
return 'http://' + userinfo.qrcode_domain_name + '/' + this.appinfos.short;
}
},
setfunactive(item, index) { setfunactive(item, index) {
for (let key in this.$refs) { for (let key in this.$refs) {
if (key === "qr") continue; if (key === "qr") continue;
@ -170,15 +167,8 @@ export default {
} }
} }
}, },
appDownload() { appDownload(appinfo) {
let routeData = this.$router.resolve({name: 'FirDownload', params: {short: this.appinfos.short}}); window.open(appinfo.preview_url, '_blank', '');
// window.open(routeData.href, '_blank');
let p_url = routeData.href;
// let p_url = location.origin + '/' + this.appinfos.short;
if (this.appinfos.preview_url && this.appinfos.preview_url.length > 6) {
p_url = this.appinfos.preview_url + p_url
}
window.open(p_url, '_blank', '');
}, },
defaulttimeline() { defaulttimeline() {
this.setfunactive('timeline', 5); this.setfunactive('timeline', 5);
@ -229,7 +219,6 @@ export default {
this.master_release = data.data.master_release; this.master_release = data.data.master_release;
this.appinfos["icon_url"] = this.master_release.icon_url; this.appinfos["icon_url"] = this.master_release.icon_url;
this.$store.dispatch('doucurrentapp', this.appinfos); this.$store.dispatch('doucurrentapp', this.appinfos);
this.short_full_url = this.appinfos.preview_url + "/" + this.appinfos.short;
} else if (data.code === 1003) { } else if (data.code === 1003) {
this.$router.push({name: 'FirApps'}); this.$router.push({name: 'FirApps'});
} else { } else {

@ -12,9 +12,10 @@
</el-button> </el-button>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="使用空间" style="width: 56%">
<el-input v-model="currentapp.storage_used" :disabled="true" :value="currentapp.storage_used"/>
</el-form-item>
<el-form-item label="应用名称" style="width: 56%"> <el-form-item label="应用名称" style="width: 56%">
<el-input v-model="currentapp.name"/> <el-input v-model="currentapp.name"/>
</el-form-item> </el-form-item>

@ -1,22 +1,258 @@
<template> <template>
<div style="margin-top: 20px;width: 66%;margin-left: 8%"> <div style="margin-top: 20px;width: 66%;margin-left: 8%">
<el-dialog <el-dialog
:close-on-click-modal="false" :close-on-click-modal="false"
:close-on-press-escape="false" :close-on-press-escape="false"
:visible.sync="bind_domain_sure" :visible.sync="bind_domain_sure"
title="绑定应用专属下载页域名" title="绑定应用专属下载页域名"
width="666px"> width="666px">
<bind-domain v-if="bind_domain_sure" :app_id="this.currentapp.app_id" :domain_type="2" <bind-domain v-if="bind_domain_sure" :app_id="this.currentapp.app_id" :c_domain_name="this.currentapp.domain_name"
transitionName="bind-app-domain"/> :domain_type="2" transitionName="bind-app-domain"/>
</el-dialog> </el-dialog>
<el-form label-width="80px"> <el-dialog
<el-form-item label="访问密码" label-width="200px"> :close-on-click-modal="false"
:close-on-press-escape="false"
:visible.sync="download_password_sure"
center
title="应用下载授权码设置"
width="900px">
<el-dialog
:visible.sync="makeTokenVisible"
append-to-body
center
title="下载授权码生成"
width="500px">
<el-dialog
:visible.sync="addUdidVisible"
append-to-body
center
title="添加设备udid授权"
width="500px">
<el-tag style="margin: 10px 0">可以输入多个udid,以空格或者回车分割</el-tag>
<el-input
v-model="makeTokenInfo.udids"
:rows="17"
placeholder="可以输入多个udid,以空格或者回车分割"
type="textarea">
</el-input>
<span slot="footer">
<el-button @click="addUdidVisible=false,makeTokenInfo.udids=''">取消</el-button>
<el-button @click="addUididFunc">确定</el-button>
</span>
</el-dialog>
<el-form ref="form" :model="makeTokenInfo" label-width="110px">
<el-form-item label="指定授权码" style="width: 400px">
<el-tag>当存在指定授权码授权码长度和生成数量被禁用</el-tag>
<el-input v-model="makeTokenInfo.token" clearable placeholder="输入指定下载授权码"></el-input>
</el-form-item>
<el-form-item label="授权码长度">
<el-input-number v-model="makeTokenInfo.token_length" :disabled="tokendisable" :max="32"
:min="4"></el-input-number>
<el-tag style="margin-left: 5px">授权码长度为4-32之间</el-tag>
</el-form-item>
<el-form-item label="生成数量">
<el-input-number v-model="makeTokenInfo.token_number" :disabled="tokendisable" :max="1024"
:min="1" :step="20"></el-input-number>
<el-tag style="margin-left: 5px">单次最多可生成1024个</el-tag>
</el-form-item>
<el-form-item label="最大使用次数">
<el-input-number v-model="makeTokenInfo.token_max_used_number" :disabled="makeTokenInfo.bind_status"
:max="1024"
:min="0"></el-input-number>
<el-tag style="margin-left: 5px">0表示不限使用次数</el-tag>
</el-form-item>
<el-form-item v-if="currentapp.type===1 && currentapp.issupersign" label="是否绑定设备">
<el-tooltip placement="top">
<div slot="content">
<span v-if="makeTokenInfo.bind_status"> 开启绑定设备</span>
<span v-else> 关闭绑定设备</span>
</div>
<el-switch
v-model="makeTokenInfo.bind_status"
:active-value="true"
:inactive-value="false"
active-color="#13ce66"
inactive-color="#ff4949">
</el-switch>
</el-tooltip>
<el-tag style="margin-left: 5px">绑定设备之后二次自动进行设备授权下载应用</el-tag>
</el-form-item>
<el-form-item v-if="currentapp.type===1 && currentapp.issupersign && makeTokenInfo.bind_status===true"
label="添加授权设备">
<el-button size="mini" @click="addudid">
添加
</el-button>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="cancelDownloadToken">取消</el-button>
<el-button @click="makeDownloadToken">生成</el-button>
</span>
</el-dialog>
<el-input
v-model="dpwdsearch"
:placeholder="dpwdtitle"
clearable
style="width: 30%;margin-right: 30px;margin-bottom: 10px"/>
<el-button icon="el-icon-search" type="primary" @click="handleSearch(1)">
搜索
</el-button>
<div style="float: right;width: 400px;text-align: right">
<el-button style="margin:0 10px 5px" @click="makeTokenVisible=true">
生成下载授权码
</el-button>
<el-button plain style="margin: 0 10px 5px" type="warning" @click="cleanToken('all')">清空所有授权码</el-button>
<el-button plain style="margin: 5px 10px 5px" type="warning" @click="cleanToken('invalid')">清理无效授权码</el-button>
<el-button plain style="margin: 5px 10px 5px" type="warning" @click="cleanToken('some')">清理选中授权码</el-button>
</div>
<el-table
v-loading="loading"
:data="app_download_token_list"
border
stripe
style="width: 100%"
@selection-change="tokenHandleSelectionChange">
<el-table-column
align="center"
type="selection"
width="55">
</el-table-column>
<el-table-column
align="center"
label="授权码"
prop="token"
>
<template slot-scope="scope">
<el-popover
placement="top-start"
trigger="hover"
width="200">
<p>
<el-tag v-clipboard:copy="scope.row.token" v-clipboard:success="copy_success" type="success">
<el-link :underline="false">点击复制授权码</el-link>
</el-tag>
</p>
<p>
<el-tag v-clipboard:copy="format_copy_text(scope.row.token)" v-clipboard:success="copy_success">
<el-link :underline="false">点击复制授权码下载链接</el-link>
</el-tag>
</p>
<el-link slot="reference" :underline="false">{{ scope.row.token }}</el-link>
</el-popover>
</template>
</el-table-column>
<el-table-column
:formatter="tokenformatter"
align="center"
label="生成日期"
prop="create_time"
width="160">
</el-table-column>
<el-table-column
align="center"
label="使用次数"
prop="used_count"
width="100"
>
<template slot-scope="scope">
<span v-if="scope.row.max_limit_count === 0">不限次数</span>
<span v-else>{{ scope.row.used_count }}</span>
</template>
</el-table-column>
<el-table-column
align="center"
label="最大可用次数"
prop="max_limit_count"
width="110">
<template slot-scope="scope">
<span v-if="scope.row.max_limit_count === 0">不限次数</span>
<span v-else>{{ scope.row.max_limit_count }}</span>
</template>
</el-table-column>
<div v-if="currentapp.type===1 && currentapp.issupersign">
<el-table-column
align="center"
label="绑定设备"
prop="bind_status"
width="60">
<template slot-scope="scope">
<el-tag v-if="scope.row.bind_status"></el-tag>
<el-tag v-else type="info"></el-tag>
</template>
</el-table-column>
<el-table-column
align="center"
label="设备ID"
prop="bind_udid">
<template slot-scope="scope">
<el-tooltip content="点击复制到剪贴板">
<el-link v-clipboard:copy="scope.row.bind_udid"
v-clipboard:success="copy_success"
:underline="false">{{ scope.row.bind_udid }}
</el-link>
</el-tooltip>
</template>
</el-table-column>
</div>
<el-table-column
align="center"
fixed="right"
label="操作"
width="90">
<template slot-scope="scope">
<div>
<el-tooltip :content="get_reset_title()" placement="top">
<el-button
size="mini"
@click="resetDownloadUsed(scope.row)">重置
</el-button>
</el-tooltip>
</div>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 20px">
<el-pagination
:current-page.sync="pagination.currentPage"
:page-size="pagination.pagesize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total,sizes, prev, pager, next"
@size-change="tokenHandleSizeChange"
@current-change="tokenHandleCurrentChange">
</el-pagination>
</div>
<span slot="footer">
<el-button @click="closeDownloadTokenInfo">关闭</el-button>
</span>
</el-dialog>
<el-form label-width="80px">
<el-form-item label="下载授权码" label-width="200px">
<el-tooltip placement="top"> <el-tooltip placement="top">
<div slot="content"> <div slot="content">
{{ passwordtip.msg }}<br> {{ passwordtip.msg }}<br>
<div v-if="passwordtip.val === 'on'"> <div v-if="passwordtip.val === 'on'">
<el-link :underline="false" icon="el-icon-edit" @click="setaccesspassword">修改</el-link> <el-link :underline="false" icon="el-icon-edit" @click="handleSearch(1)">查看配置下载授权码</el-link>
</div> </div>
</div> </div>
<el-switch <el-switch
@ -29,7 +265,12 @@
</el-switch> </el-switch>
</el-tooltip> </el-tooltip>
<el-link :underline="false" style="margin-left: 20px">设置密码之后用户需要输入密码才可以下载该应用</el-link> <el-popover
placement="top"
trigger="hover">
<el-link :underline="false" icon="el-icon-edit" @click="handleSearch(1)">查看配置下载授权码</el-link>
<el-link slot="reference" :underline="false" style="margin-left: 20px">设置授权码之后用户需要输入授权码才可以下载该应用</el-link>
</el-popover>
</el-form-item> </el-form-item>
@ -93,9 +334,10 @@
</template> </template>
<script> <script>
import {apputils,} from "@/restful" import {appDownloadToken, apputils,} from "@/restful"
import {deepCopy} from "@/utils"; import {deepCopy} from "@/utils";
import BindDomain from "@/components/base/BindDomain"; import BindDomain from "@/components/base/BindDomain";
import {format_time} from "@/utils/base/utils";
export default { export default {
name: "FirAppInfossecurity", name: "FirAppInfossecurity",
@ -115,9 +357,159 @@ export default {
wxeasy_disable: false, wxeasy_disable: false,
defualt_dtitle: '专属下载页域名', defualt_dtitle: '专属下载页域名',
bind_domain_sure: false, bind_domain_sure: false,
download_password_sure: false,
app_download_token_list: [],
loading: false,
makeTokenVisible: false,
addUdidVisible: false,
tokendisable: false,
pagination: {"currentPage": 1, "total": 0, "pagesize": 10},
dpwdsearch: '',
dpwdtitle: '输入下载授权码',
makeTokenInfo: {
token: '',
token_length: 6,
token_number: 20,
token_max_used_number: 0,
bind_status: false,
udids: ''
},
multipleSelection: [],
} }
}, },
methods: { methods: {
addUididFunc() {
let udids = this.makeTokenInfo.udids
if (udids && udids.length > 10) {
this.makeDownloadToken()
} else {
this.$message.error('设备udid不存在,请检查')
}
},
get_reset_title() {
let title = '重置使用次数'
if (this.currentapp.type === 1 && this.currentapp.issupersign) {
title = '重置使用次数和授权设备ID'
}
return title
},
addudid() {
this.makeTokenInfo.udids = ''
this.addUdidVisible = true
},
format_copy_text(token) {
return this.currentapp.preview_url + "?password=" + token
},
copy_success() {
this.$message.success('复制剪切板成功');
},
cleanToken(act) {
appDownloadToken(data => {
this.loading = false
if (data.code === 1000) {
this.$message.success("操作成功")
this.handleSearch(1)
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'PUT',
app_id: this.currentapp.app_id,
data: {'act': act, 'tokens': this.format_token(this.multipleSelection)}
})
},
format_token(token_info_list) {
let format_token_list = []
for (let token_info of token_info_list) {
format_token_list.push(token_info.token)
}
return format_token_list
},
tokenHandleSelectionChange(val) {
this.multipleSelection = val;
},
cancelDownloadToken() {
this.makeTokenInfo = {
token: '',
token_length: 6,
token_number: 20,
token_max_used_number: 0,
bind_status: false,
udids: ''
}
this.makeTokenVisible = false
this.addUdidVisible = false
},
resetDownloadUsed(info) {
appDownloadToken(data => {
this.loading = false
if (data.code === 1000) {
this.$message.success("重置成功")
this.showDownloadBase()
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'PUT', app_id: this.currentapp.app_id, data: {'token': info.token}
})
},
makeDownloadToken() {
appDownloadToken(data => {
this.loading = false
if (data.code === 1000) {
this.cancelDownloadToken()
this.$message.success("下载授权码生成成功")
this.showDownloadBase()
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'POST', app_id: this.currentapp.app_id, data: this.makeTokenInfo
})
},
handleSearch(val) {
this.pagination.currentPage = val;
this.showDownloadBase()
},
showDownloadBase() {
this.showDownloadToken({
"size": this.pagination.pagesize,
"page": this.pagination.currentPage,
"dpwdsearch": this.dpwdsearch.replace(/^\s+|\s+$/g, "")
})
},
showDownloadToken(data) {
this.loading = true
appDownloadToken(data => {
this.loading = false
if (data.code === 1000) {
this.download_password_sure = true;
this.app_download_token_list = data.data
this.pagination.total = data.count
} else {
this.$message.error("获取失败了 " + data.msg)
}
}, {
methods: 'GET', app_id: this.currentapp.app_id, data: data
})
},
closeDownloadTokenInfo() {
this.download_password_sure = false;
},
tokenHandleSizeChange(val) {
this.pagination.pagesize = val;
this.pagination.currentPage = 1;
this.showDownloadBase();
},
tokenHandleCurrentChange(val) {
this.pagination.currentPage = val;
this.showDownloadBase();
},
// eslint-disable-next-line no-unused-vars
tokenformatter(row, column) {
return format_time(row.create_time)
},
set_default_flag() { set_default_flag() {
this.passwordflag = false; this.passwordflag = false;
this.showdownloadflag = false; this.showdownloadflag = false;
@ -144,7 +536,7 @@ export default {
}); });
}, },
setbuttondefaltpass(currentapp) { setbuttondefaltpass(currentapp) {
if (currentapp.password === '') { if (currentapp.need_password === false) {
this.passwordtip.val = 'off'; this.passwordtip.val = 'off';
this.showpasswordevent("off"); this.showpasswordevent("off");
} else { } else {
@ -191,57 +583,6 @@ export default {
this.setxeasytypeshow(currentapp); this.setxeasytypeshow(currentapp);
this.setwxredirectshow(currentapp); this.setwxredirectshow(currentapp);
}, },
passwordswitch(state) {
this.passwordflag = false;
this.showpasswordevent(state);
this.passwordtip.val = state;
this.passwordflag = true;
},
setaccesspassword() {
this.$prompt('', '请设置访问密码', {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
inputValue: `${this.currentapp.password}`,
}).then(({value}) => {
value = value.replace(/\s+/g, "");
if (this.currentapp.password === value) {
if (value === '') {
this.$message({
type: 'success',
message: '访问密码未变'
});
this.passwordflag = false;
this.setbuttondefaltpass(this.currentapp);
return
} else {
return
}
}
this.saveappinfo({
"password": value,
});
if (value === '') {
this.passwordswitch("off");
this.$message({
type: 'success',
message: '设置成功,取消密码访问'
});
} else {
this.passwordtip.msg = '访问密码:' + value;
this.$message({
type: 'success',
message: '设置成功,访问密码是: ' + value
});
}
this.currentapp.password = value;
this.$store.dispatch('doucurrentapp', this.currentapp)
}).catch(() => {
if (this.currentapp.password === '') {
this.passwordswitch("off")
}
});
},
showdownloadevent(newval) { showdownloadevent(newval) {
if (newval === "on") { if (newval === "on") {
if (this.showdownloadflag) { if (this.showdownloadflag) {
@ -264,19 +605,21 @@ export default {
showpasswordevent(newval) { showpasswordevent(newval) {
if (newval === "on") { if (newval === "on") {
if (this.passwordflag) { if (this.passwordflag) {
this.setaccesspassword() this.saveappinfo({
} else { "need_password": 1,
this.passwordtip.msg = '访问密码:' + this.currentapp.password; });
this.currentapp.need_password = 1;
} }
this.passwordtip.msg = '已经开启授权码下载功能'
} else { } else {
if (this.passwordflag) { if (this.passwordflag) {
this.saveappinfo({ this.saveappinfo({
"password": '', "need_password": 0,
}); });
this.currentapp.need_password = 0;
} }
this.currentapp.password = ''; this.passwordtip.msg = '未开启授权码下载功能'
this.$store.dispatch('doucurrentapp', this.currentapp);
this.passwordtip.msg = '无访问密码'
} }
}, },
@ -324,12 +667,12 @@ export default {
this.currentapp = this.$store.state.currentapp; this.currentapp = this.$store.state.currentapp;
this.set_default_flag(); this.set_default_flag();
this.orgcurrentapp = deepCopy(this.currentapp); this.orgcurrentapp = deepCopy(this.currentapp);
if (!this.currentapp.domain_name || this.currentapp.domain_name.length < 3) {
if (this.$store.state.userinfo.domain_name && this.$store.state.userinfo.domain_name.length > 3) {
this.defualt_dtitle = this.$store.state.userinfo.domain_name;
}
}
this.setbuttondefault(this.currentapp); this.setbuttondefault(this.currentapp);
if (this.currentapp.type === 1 && this.currentapp.issupersign) {
this.dpwdtitle = '输入下载授权码或者设备udid'
} else {
this.dpwdtitle = '输入下载授权码'
}
} }
}, },
mounted() { mounted() {
@ -342,6 +685,14 @@ export default {
'$store.state.currentapp': function () { '$store.state.currentapp': function () {
this.appinit(); this.appinit();
}, },
'makeTokenInfo.token': function () {
this.tokendisable = !!(this.makeTokenInfo.token && this.makeTokenInfo.token.length > 0);
},
'makeTokenInfo.bind_status': function () {
if (this.makeTokenInfo.bind_status) {
this.makeTokenInfo.token_max_used_number = 1;
}
}
}, computed: {} }, computed: {}
} }
</script> </script>

@ -1,7 +1,16 @@
<template> <template>
<div style="margin-top: 20px;width: 66%;margin-left: 8%"> <div style="margin-top: 20px;width: 72%;margin-left: 8%">
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="false"
:title="appletoapp_title"
:visible.sync="bind_appletoapp_sure"
width="1166px">
<apple-developer-bind-app v-if="bind_appletoapp_sure" :app_id="currentapp.app_id"
transitionName="bind_appletoapp_app"/>
</el-dialog>
<el-form v-if="currentapp.type === 1 && $store.state.userinfo.supersign_active" label-width="80px"> <el-form v-if="currentapp.type === 1 && $store.state.userinfo.supersign_active" label-width="80px">
@ -21,10 +30,35 @@
<el-button v-if="!currentapp.issupersign && currentapp.count !== 0" plain <el-button v-if="!currentapp.issupersign && currentapp.count !== 0" plain
size="small" style="margin-left: 20px" type="info" @click="clean_app">清理该应用脏数据 size="small" style="margin-left: 20px" type="info" @click="clean_app">清理该应用脏数据
</el-button> </el-button>
<el-link v-else :underline="false" style="margin-left: 20px">超级签名iOS专用需要配置好苹果开发者账户方可开启</el-link> <!-- <el-link v-else :underline="false" style="margin-left: 20px">超级签名iOS专用需要配置好苹果开发者账户方可开启</el-link>-->
<el-tooltip content="点击查看所使用开发者信息" placement="top">
<el-link :disabled="supersign_disable" :underline="false" style="margin-left: 20px;color: #3875cc;"
@click="$router.push({name:'FirSuperSignBase',params:{act:'iosdeveloper'},query:{appidseach: currentapp.bundle_id}})">
使用了 {{ currentapp.developer_used_count }} 开发者
</el-link>
</el-tooltip>
</el-form-item> </el-form-item>
<el-form-item label="自动更新"
label-width="200px">
<el-switch
v-model="currentapp.change_auto_sign"
:active-value="true"
:disabled="supersign_disable"
:inactive-value="false"
active-color="#13ce66"
inactive-color="#ff4949"
@change="saveappinfo({change_auto_sign:currentapp.change_auto_sign})">
</el-switch>
<el-link :underline="false" style="margin-left: 20px">开启自动更新新包入库或签名相关数据更改系统将会自动更新签名包</el-link>
</el-form-item>
<el-form-item label="专属配置" label-width="200px">
<el-button :disabled="supersign_disable" @click="bindAppletoapp">配置专属苹果开发账户</el-button>
<el-link :disabled="supersign_disable" :underline="false" style="margin-left: 20px" @click="bindAppletoapp"> 拥有
{{ currentapp.private_developer_number }} 个专属苹果开发者使用了 {{ currentapp.private_developer_used_number }} 个设备数
</el-link>
</el-form-item>
<el-form-item label="签名限额" label-width="200px"> <el-form-item label="签名限额" label-width="200px">
@ -38,7 +72,7 @@
>保存 >保存
</el-button> </el-button>
<el-tooltip content="点击查看使用详情" placement="top"> <el-tooltip content="点击查看使用详情" placement="top">
<el-link :underline="false" style="margin-left: 20px" type="primary" <el-link :disabled="supersign_disable" :underline="false" style="margin-left: 20px" type="primary"
@click="$router.push({name:'FirSuperSignBase',params:{act:'useddevices'},query:{bundleid: currentapp.bundle_id}})"> @click="$router.push({name:'FirSuperSignBase',params:{act:'useddevices'},query:{bundleid: currentapp.bundle_id}})">
已经使用 <a style="color: #dd6161;font-size: larger">{{ currentapp.supersign_used_number }}</a> 已经使用 <a style="color: #dd6161;font-size: larger">{{ currentapp.supersign_used_number }}</a>
个设备额度 个设备额度
@ -80,6 +114,34 @@
>保存 >保存
</el-button> </el-button>
</el-form-item> </el-form-item>
<el-divider/>
<el-form-item label="设备异常等待跳转"
label-width="200px">
<el-switch
v-model="currentapp.abnormal_redirect"
:active-value="true"
:disabled="supersign_disable"
:inactive-value="false"
active-color="#13ce66"
inactive-color="#ff4949"
@change="saveappinfo({abnormal_redirect:currentapp.abnormal_redirect})">
</el-switch>
<el-link :underline="false" style="margin-left: 20px">该配置仅当开启异常设备注册等待有效若启用该配置根据规则会自动跳转链接</el-link>
</el-form-item>
<el-divider/>
<el-form-item label="签名不足跳转链接" label-width="200px">
<el-input v-model="currentapp.supersign_redirect_url" :disabled="supersign_disable"
clearable prefix-icon="el-icon-s-promotion"
style="width: 60%;margin-right: 10px"/>
<el-button :disabled="supersign_disable"
@click="saveappinfo({supersign_redirect_url:currentapp.supersign_redirect_url})"
>保存
</el-button>
<el-link :underline="false">当设备余额不足无法进行签名可配置该链接跳转到第三方下载平台</el-link>
</el-form-item>
<el-divider/>
</el-form> </el-form>
<el-link v-else :underline="false" type="warning"> 该用户暂未开通超级签权限,请联系管理员申请开通</el-link> <el-link v-else :underline="false" type="warning"> 该用户暂未开通超级签权限,请联系管理员申请开通</el-link>
@ -89,11 +151,13 @@
</template> </template>
<script> <script>
import {apputils,} from "@/restful" import {appSignInfo,} from "@/restful"
import {deepCopy} from "@/utils"; import {deepCopy} from "@/utils";
import AppleDeveloperBindApp from "@/components/base/AppleDeveloperBindApp";
export default { export default {
name: "FirAppInfossupersign", name: "FirAppInfossupersign",
components: {AppleDeveloperBindApp},
data() { data() {
return { return {
currentapp: {}, currentapp: {},
@ -104,11 +168,16 @@ export default {
sign_type_list: [], sign_type_list: [],
sign_type: 0, sign_type: 0,
defualt_dtitle: '', defualt_dtitle: '',
defualt_dtitle_name: '' defualt_dtitle_name: '',
bind_appletoapp_sure: false,
appletoapp_title: ''
} }
}, },
methods: { methods: {
bindAppletoapp() {
this.appletoapp_title = "专属签名开发者信息 " + this.currentapp.name + "-" + this.currentapp.bundle_id;
this.bind_appletoapp_sure = true;
},
set_default_flag() { set_default_flag() {
this.showsupersignflag = false; this.showsupersignflag = false;
}, },
@ -123,13 +192,14 @@ export default {
spinner: 'el-icon-loading', spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)' background: 'rgba(0, 0, 0, 0.7)'
}); });
apputils(data => { appSignInfo(data => {
if (data.code === 1000) { if (data.code === 1000) {
this.$message.success('数据更新成功'); this.$message.success('数据更新成功');
} else { } else {
this.$message.error('操作失败,' + data.msg); this.$message.error('操作失败,' + data.msg);
this.$store.dispatch('doucurrentapp', this.orgcurrentapp); this.$store.dispatch('doucurrentapp', this.orgcurrentapp);
} }
this.getappinfo();
loading.close(); loading.close();
}, { }, {
"methods": "PUT", "methods": "PUT",
@ -137,6 +207,20 @@ export default {
"data": data "data": data
}); });
}, },
getappinfo() {
appSignInfo(data => {
if (data.code === 1000) {
for (let name of Object.keys(data.data)) {
this.currentapp[name] = data.data[name]
}
} else {
this.$message.error('数据获取失败,' + data.msg);
}
}, {
"methods": "GET",
"app_id": this.currentapp.app_id,
});
},
setbuttonsignshow(currentapp) { setbuttonsignshow(currentapp) {
if (currentapp.issupersign === true) { if (currentapp.issupersign === true) {
this.supersignevent("on"); this.supersignevent("on");
@ -188,6 +272,9 @@ export default {
}, },
appinit() { appinit() {
this.currentapp = this.$store.state.currentapp; this.currentapp = this.$store.state.currentapp;
if (this.currentapp.app_id) {
this.getappinfo();
}
this.set_default_flag(); this.set_default_flag();
this.orgcurrentapp = deepCopy(this.currentapp); this.orgcurrentapp = deepCopy(this.currentapp);
this.set_default_ms(this.currentapp); this.set_default_ms(this.currentapp);

@ -4,7 +4,7 @@
:color="app.master_color" :color="app.master_color"
:timestamp="app.created_time|formatdatatimeline" :timestamp="app.created_time|formatdatatimeline"
placement="top"> placement="top">
<el-card> <el-card :body-style="{border: app.is_master ? '#2abb9d 1px solid' : ''}">
<div class="directive-view-release"> <div class="directive-view-release">
<i v-if="! app.is_master" class="el-icon-cloudy"/> <i v-if="! app.is_master" class="el-icon-cloudy"/>
@ -78,8 +78,9 @@
</el-tooltip> </el-tooltip>
<el-button class="tooltip-top" @click="previewRelase(app)"><i class="el-icon-view"/> <span <el-button v-if="!currentapp.issupersign" class="tooltip-top" @click="previewRelease(app)"><i
class="ng-binding">预览</span> class="el-icon-view"/> <span
class="ng-binding">预览该版本</span>
</el-button> </el-button>
<el-button v-if="! app.is_master" class="tooltip-top" @click="make_master_release(app)"> <el-button v-if="! app.is_master" class="tooltip-top" @click="make_master_release(app)">
@ -97,12 +98,12 @@
</div> </div>
</el-card> </el-card>
</el-timeline-item> </el-timeline-item>
<el-button v-if="has_next" class="time-line-more" @click="getapptimelineFun('more')">显示更多版本</el-button> <el-button v-if="has_next" class="time-line-more" @click="getAppTimelineFun('more')">显示更多版本</el-button>
</el-timeline> </el-timeline>
</template> </template>
<script> <script>
import {getdownloadurl, releaseapputils} from "@/restful" import {releaseapputils} from "@/restful"
export default { export default {
name: "FirAppInfostimeline", name: "FirAppInfostimeline",
@ -125,37 +126,25 @@ export default {
} }
}, },
downloadPackage(app) { downloadPackage(app) {
getdownloadurl(res => {
if (res.code === 1000) {
window.location.href = res.data.download_url;
} else {
this.$message.error(res.msg);
}
}, {
'data': {
'token': app.download_token,
'short': this.currentapp.short,
'release_id': app.release_id,
"password": this.currentapp.password,
"isdownload": true,
},
'app_id': this.currentapp.app_id
})
},
previewRelase(app) {
let routeData = this.$router.resolve({
name: 'FirDownload',
params: {short: this.currentapp.short},
query: {release_id: app.release_id}
});
let p_url = routeData.href;
if (this.currentapp.preview_url && this.currentapp.preview_url.length > 6) {
p_url = this.currentapp.preview_url + p_url
}
window.open(p_url, 'target', '');
releaseapputils(res => {
if (res.code === 1000) {
window.location.href = res.data.download_url;
} else {
this.$message.error(res.msg);
}
}, {
methods: 'POST',
app_id: this.currentapp.app_id,
release_id: app.release_id,
data: {'token': app.download_token, 'short': this.currentapp.short}
}
);
}, },
getapptimelineFun(act = '') { previewRelease(app) {
window.open(this.currentapp.preview_url + '?release_id=' + app.release_id, '_blank', '');
},
getAppTimelineFun(act = '') {
const loading = this.$loading({ const loading = this.$loading({
lock: true, lock: true,
text: '加载中', text: '加载中',
@ -206,7 +195,7 @@ export default {
message: '删除成功', message: '删除成功',
type: 'success' type: 'success'
}); });
this.getapptimelineFun(); this.getAppTimelineFun();
} else { } else {
this.$message({ this.$message({
@ -231,10 +220,6 @@ export default {
}); });
this.release_apps = data.data.release_apps; this.release_apps = data.data.release_apps;
this.currentapp = data.data.currentapp; this.currentapp = data.data.currentapp;
// this.getapptimelineFun();
// this.currentapp["icon_url"] = this.currentapp.master_release.icon_url;
// this.$store.dispatch('doucurrentapp', this.currentapp);
} else { } else {
this.$message({ this.$message({
message: '更新失败,请联系管理员', message: '更新失败,请联系管理员',
@ -293,7 +278,7 @@ export default {
}, watch: {}, }, watch: {},
computed: {}, mounted() { computed: {}, mounted() {
this.$store.dispatch('doappInfoIndex', [[5, 5], [5, 5]]); this.$store.dispatch('doappInfoIndex', [[5, 5], [5, 5]]);
this.getapptimelineFun(); this.getAppTimelineFun();
}, filters: { }, filters: {
downcontent(content) { downcontent(content) {
if (content) { if (content) {

@ -80,6 +80,16 @@
<el-button type="danger" @click="delApp"> </el-button> <el-button type="danger" @click="delApp"> </el-button>
</span> </span>
</el-dialog> </el-dialog>
<el-dialog
:title="showudidTitle"
:visible.sync="showudidSync"
width="406px">
<p v-for="udid in showudidList" :key="udid">{{ udid }}</p>
<span slot="footer" class="dialog-footer">
<el-button @click="showudidSync = false">取消</el-button>
<el-button v-clipboard:copy="showudidList" v-clipboard:success="copy_success">复制到剪切板</el-button>
</span>
</el-dialog>
<el-dialog <el-dialog
:center="true" :center="true"
@ -144,7 +154,7 @@
</el-table> </el-table>
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button plain @click="closeUpload"> </el-button> <el-button :disabled="multiuploaddisable" plain @click="closeUpload"> </el-button>
<el-button :disabled="multiuploaddisable" plain type="primary" <el-button :disabled="multiuploaddisable" plain type="primary"
@click="multiuploadFun">开始上传</el-button> @click="multiuploadFun">开始上传</el-button>
</span> </span>
@ -212,7 +222,7 @@
<el-row :gutter="20" style="margin-top: 10px;"> <el-row :gutter="20" style="margin-top: 10px;">
<el-col :span="18"> <el-col :span="18">
<el-input v-model="short"> <el-input v-model="short">
<template slot="prepend">{{ analyseappinfo.short_domain_name }}/</template> <template slot="prepend">{{ analyseappinfo.short_domain_name }}</template>
</el-input> </el-input>
</el-col> </el-col>
</el-row> </el-row>
@ -435,8 +445,8 @@
<div> <div>
<el-tooltip placement="top"> <el-tooltip placement="top">
<div slot="content"> <div slot="content">
1.下载应用100M消耗一次下载次数<br> 1.下载应用 {{ times_info.every_size|diskSize }} 消耗 {{ times_info.base_times / 100 }} 下载次数<br>
2.超级签下载下载消耗次数翻倍<br> 2.私有存储模式下载应用 {{ times_info.every_size|diskSize }} 消耗 {{ times_info.private_times / 100 }} 下载次数<br>
</div> </div>
<span class="name">购买次数</span> <span class="name">购买次数</span>
</el-tooltip> </el-tooltip>
@ -481,12 +491,16 @@
<div class=" app-animator"> <div class=" app-animator">
<div class="card app card-ios"> <div class="card app card-ios">
<el-tooltip>
<i v-if="r.type === 1" class=" type-icon iconfont icon-ios"/> <div slot="content">总下载次数{{ r.count_hits }}<br/>当天下载次数{{ r.today_hits_count }}</div>
<i class="down-icon el-icon-cloudy"> {{ r.count_hits }} &nbsp;|&nbsp; {{ r.today_hits_count }}</i>
</el-tooltip>
<i v-if="r.type === 1" class="type-icon iconfont icon-ios"/>
<i v-if="r.type === 0" class="type-icon iconfont icon-android2"/> <i v-if="r.type === 0" class="type-icon iconfont icon-android2"/>
<div v-if="r.type === 1" class="type-mark"></div> <div v-if="r.type === 1 && r.issupersign" class="type-mark" style="border-top: 48px solid #a2b0f5"></div>
<div v-if="r.type === 0" class="type-mark" style="border-top: 48px solid #A4C639"></div> <div v-if="r.type === 1 && !r.issupersign" class="type-mark"></div>
<div v-if="r.type === 0" class="type-mark" style="border-top: 48px solid #a4c639"></div>
<a class="appicon" @click="appInfos(r)"> <a class="appicon" @click="appInfos(r)">
<img :src="r.master_release.icon_url|make_icon_url" alt="" class="icon ng-isolate-scope" <img :src="r.master_release.icon_url|make_icon_url" alt="" class="icon ng-isolate-scope"
height="100" width="100"></a> height="100" width="100"></a>
@ -577,7 +591,7 @@
</el-container> </el-container>
</template> </template>
<script> <script>
import {analyseApps, apputils, get_package_prices, getapps, getuploadurl, my_order} from "@/restful"; import {analyseApps, apputils, checkCanSign, get_package_prices, getapps, getuploadurl, my_order} from "@/restful";
import { import {
dataURLtoFile, dataURLtoFile,
deepCopy, deepCopy,
@ -606,9 +620,13 @@ export default {
data() { data() {
return { return {
timer: '', timer: '',
showudidTitle: '',
showudidSync: false,
showudidList: [],
multiupload: false, multiupload: false,
multiuploaddisable: false, multiuploaddisable: false,
multiFileList: [], multiFileList: [],
trymultiFileList: [],
default_pay_radio: '', default_pay_radio: '',
default_price_radio: '', default_price_radio: '',
pay_choices: [], pay_choices: [],
@ -645,6 +663,9 @@ export default {
PaymentQuestionMsg: '', PaymentQuestionMsg: '',
} }
}, methods: { }, methods: {
copy_success() {
this.$message.success('复制剪切板成功');
},
check_short(short, callback) { check_short(short, callback) {
apputils(data => { apputils(data => {
if (data.code === 1000 && data.data === 0) { if (data.code === 1000 && data.data === 0) {
@ -657,7 +678,7 @@ export default {
"app_id": short "app_id": short
}) })
}, },
multirun(process, keylist, func) { multirun(process, keylist, func, failback) {
let thr = []; let thr = [];
for (let i = 0; i < process; i++) { for (let i = 0; i < process; i++) {
thr.push(Promise.resolve()) thr.push(Promise.resolve())
@ -671,7 +692,10 @@ export default {
} }
} }
if (failback) {
// eslint-disable-next-line no-unused-vars
thr[process - 1].then(_ => failback())
}
}, },
uploadasync(file) { uploadasync(file) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -695,27 +719,55 @@ export default {
analyseappinfo[name] = data.data[name] analyseappinfo[name] = data.data[name]
} }
if (this.currentfile) { if (this.currentfile) {
this.willuploadApp = true; checkCanSign(res => {
resolve() if (res.code === 1000) {
analyseappinfo.sign = res.data.sign
}
this.willuploadApp = true;
resolve()
}, {methods: 'GET'})
} else { } else {
this.uploadcloud(analyseappinfo, file, true, resolve) this.uploadcloud(analyseappinfo, file, true, resolve)
} }
} else { } else {
this.$message.error("应用 " + analyseappinfo.appname + " 上传token获取失败,请刷新重试") let err_msg = "应用 " + analyseappinfo.appname
if (data.code === 1003) {
err_msg += " 上传token获取失败,请刷新重试"
} else {
err_msg += data.msg
}
this.$message.error(err_msg)
if (this.currentfile) {
this.closeUpload()
} else {
this.trymultiFileList.push(file);
}
resolve()
// this.multiFileList.splice(this.multiFileList.indexOf(file), 1)
} }
loading.close(); loading.close();
}, { }, {
'methods': 'POST', 'methods': 'POST',
'data': {"bundleid": analyseappinfo.bundleid, "type": analyseappinfo.type} 'data': {
"bundleid": analyseappinfo.bundleid,
"type": analyseappinfo.type,
"filesize": analyseappinfo.filesize
}
}); });
} else { } else {
this.$message.error("文件 " + file.raw.name + " 解析失败,请检查是否为APP应用") this.$message.error("文件 " + file.raw.name + " 解析失败,请检查是否为APP应用")
resolve()
this.trymultiFileList.push(file);
// this.multiFileList.splice(this.multiFileList.indexOf(file), 1)
} }
}, err => { }, err => {
loading.close(); loading.close();
this.$message.error("应用解析失败,请检查是否为APP应用"); this.$message.error("应用解析失败,请检查是否为APP应用");
resolve()
this.trymultiFileList.push(file);
// this.multiFileList.splice(this.multiFileList.indexOf(file), 1)
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('Error ', err); console.log('Error ', err);
}); });
@ -728,8 +780,16 @@ export default {
multiuploadFun() { multiuploadFun() {
this.multiuploaddisable = true; this.multiuploaddisable = true;
if (this.multiFileList && this.multiFileList.length > 0) { if (this.multiFileList && this.multiFileList.length > 0) {
this.multirun(3, this.multiFileList, this.uploadasync) // eslint-disable-next-line no-unused-vars
this.multirun(3, this.multiFileList, this.uploadasync, _ => {
// eslint-disable-next-line no-console
console.log('upload file ' + this.trymultiFileList.length + ' failed. try again')
if (this.trymultiFileList && this.trymultiFileList.length > 0) {
this.multirun(3, this.trymultiFileList, this.uploadasync, null)
}
})
} }
}, },
show_package_prices() { show_package_prices() {
get_package_prices(res => { get_package_prices(res => {
@ -777,14 +837,9 @@ export default {
}) })
}, },
showUDID(appinfo) { showUDID(appinfo) {
let udidstr = ''; this.showudidTitle = appinfo.appname + ' UDID';
for (let i = 0; i < appinfo.udid.length; i++) { this.showudidSync = true
udidstr = udidstr + "<p>" + appinfo.udid[i] + "</p>" this.showudidList = appinfo.udid
}
this.$alert(udidstr, appinfo.appname + ' UDID', {
confirmButtonText: '确定',
dangerouslyUseHTMLString: true,
});
}, },
updateappinfo(file, analyseappinfo, multiFlag, binaryFlag, resolve) { updateappinfo(file, analyseappinfo, multiFlag, binaryFlag, resolve) {
if (binaryFlag) { if (binaryFlag) {
@ -956,10 +1011,11 @@ export default {
this.willuploadApp = false; this.willuploadApp = false;
this.uploadflag = false; this.uploadflag = false;
this.analyseappinfo = {}; this.analyseappinfo = {};
this.currentfile = null;
this.multiupload = false; this.multiupload = false;
this.multiuploaddisable = false; this.multiuploaddisable = false;
this.multiFileList = []; this.multiFileList = [];
this.trymultiFileList = [];
this.$refs.upload.clearFiles(); this.$refs.upload.clearFiles();
this.$refs.upload.abort(); this.$refs.upload.abort();
this.uploadprocess = deepCopy(fiveProcess); this.uploadprocess = deepCopy(fiveProcess);
@ -987,7 +1043,7 @@ export default {
if (this.searchfromtype) { if (this.searchfromtype) {
this.getappsFun({"type": this.searchfromtype, 'page': 1, size: 999}); this.getappsFun({"type": this.searchfromtype, 'page': 1, size: 999});
} else { } else {
this.getappsFun({'page': 1, size: 999}); this.getappsFun({'page': 1, size: 999, 'search': keysearch});
} }
} }
}, },
@ -1014,7 +1070,7 @@ export default {
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, ""); let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
let newapplists = []; let newapplists = [];
for (let i = 0; i < this.orgapplists.length; i++) { for (let i = 0; i < this.orgapplists.length; i++) {
if (this.orgapplists[i].name.search(keysearch) >= 0) { if (this.orgapplists[i].name.search(keysearch) >= 0 || this.orgapplists[i].short.search(keysearch) >= 0) {
newapplists.push(this.orgapplists[i]); newapplists.push(this.orgapplists[i]);
} }
} }
@ -1060,9 +1116,6 @@ export default {
}, },
onUploadChange(file, fileList) { onUploadChange(file, fileList) {
if (fileList && fileList.length > 1) {
this.multiupload = true;
}
this.multiFileList = fileList; this.multiFileList = fileList;
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
this.timer = setTimeout(data => { this.timer = setTimeout(data => {
@ -1072,6 +1125,9 @@ export default {
} }
clearTimeout(this.timer); clearTimeout(this.timer);
}, 300); }, 300);
if (fileList && fileList.length > 1) {
this.multiupload = true;
}
}, },
delApp() { delApp() {
let loadingobj = this.$loading({ let loadingobj = this.$loading({
@ -1108,18 +1164,16 @@ export default {
this.$router.push({name: 'FirAppInfostimeline', params: {id: app.app_id}}) this.$router.push({name: 'FirAppInfostimeline', params: {id: app.app_id}})
}, },
appDownload(app) { appDownload(app) {
// this.$router.push({name: 'FirDownload', params: {short: app.short}}); window.open(app.preview_url, '_blank', '');
let routeData = this.$router.resolve({name: 'FirDownload', params: {short: app.short}});
let p_url = routeData.href;
if (app.preview_url && app.preview_url.length > 6) {
p_url = app.preview_url + p_url
}
window.open(p_url, '_blank', '');
} }
}, computed: { }, computed: {
getDelappTitle() { getDelappTitle() {
return `删除应用 ${this.delapp.name}` return `删除应用 ${this.delapp.name}`
}, },
times_info() {
let times_info = this.$store.state.userinfo.times_info
return times_info ? times_info : {}
}
}, },
filters: { filters: {
diskSize, diskSize,
@ -1323,6 +1377,16 @@ export default {
} }
.page-apps .card.app .down-icon {
position: absolute;
top: 9px;
left: 7px;
z-index: 2;
/*color: #1f72ee;*/
color: #B6BDC1;
}
.page-apps .card.app .type-mark { .page-apps .card.app .type-mark {
position: absolute; position: absolute;
top: 0; top: 0;

@ -0,0 +1,286 @@
<template>
<transition :name="transitionName">
<div>
<div style="text-align: center">
<el-transfer
id="transfer"
v-model="choices_data"
:button-texts="['', '']"
:data="app_developer_lists"
:format="{
noChecked: '${total}',
hasChecked: '${checked}/${total}'
}"
:left-default-checked="left_data"
:props="s_props"
:right-default-checked="right_data"
:titles="s_titles"
filterable
style="text-align: left; display: inline-block"
@change="handleChange">
<div slot-scope="{ option }" style="height: 60px;display: block">
<el-popover v-if="app_id" placement="left" trigger="hover">
<el-tag v-if="option.app_private_number > 0">专属应用账户存在其他应用单独使用该开发者</el-tag>
<el-tag v-else type="info">公共应用账户所有应用公用此开发者</el-tag>
<p>开发者ID: {{ option.issuer_id }} </p>
<p>应用签名数: {{ option.app_used_count }} </p>
<p>应用签名数限额: {{ option.app_limit_number }} </p>
<p>已分配专属应用数: {{ option.app_private_number }} </p>
<p>开发者账户已使用设备数: {{ option.developer_used_number }}</p>
<p>开发者账户可用设备数: {{ 100 - option.developer_used_number }}</p>
<p>由于您设置可用设备数: {{ option.usable_number }} ,所以现在可用设备数: {{
option.usable_number - option.developer_used_number > 0
? option.usable_number - option.developer_used_number : 0
}}</p>
<p>证书到期时间: {{ option.cert_expire_time }}</p>
<p v-if="option.app_usable_number > 0">
分配数量:
<el-input-number v-model="option.app_usable_number" :max="100" :min="1"
label="分配数量" size="small"/>
<el-button size="small" style="margin-left: 10px" @click="saveNumber(option)">保存修改</el-button>
</p>
<p>该应用已经消耗 {{ option.app_used_number }} 个设备数</p>
<p>描述: {{ option.description }}</p>
<p>账户状态: {{ option.status_display }}</p>
<div slot="reference" class="name-wrapper">
<div>
<el-tag v-if="option.app_used_number>0 && choices_data.indexOf(option.issuer_id)===-1 " size="mini"
type="warning"> Del
</el-tag>
{{ option.issuer_id }} - {{
option.usable_number - option.developer_used_number > 0
? option.usable_number - option.developer_used_number : 0
}} - {{ option.description }}
</div>
</div>
</el-popover>
<el-popover v-if="issuer_id" placement="left" trigger="hover">
<p>应用ID: {{ option.app_id }} </p>
<p>应用名称: {{ option.name }} </p>
<p>应用BundleId: {{ option.bundle_id }} </p>
<p>短连接: {{ option.short }} </p>
<p v-if="option.app_usable_number>0">
分配数量:
<el-input-number v-model="option.app_usable_number" :max="100" :min="1"
label="分配数量" size="small"/>
<el-button size="small" style="margin-left: 10px" @click="saveNumber(option)">保存修改</el-button>
</p>
<p>使用数量: {{ option.app_used_number }}</p>
<p>描述: {{ option.description }}</p>
<div slot="reference" class="name-wrapper">
<div>
<span v-if="option.app_usable_number>0">{{ option.app_usable_number }} - </span>
<el-tag v-else-if="option.app_used_number>0" size="mini" type="warning"> Del</el-tag>
{{ option.name }} - {{ option.bundle_id }} - {{ option.app_id }}
</div>
</div>
</el-popover>
</div>
<el-button slot="left-footer" class="transfer-footer" @click="getinfos">刷新</el-button>
<el-button slot="right-footer" class="transfer-footer" @click="saveAppleApps">保存</el-button>
<el-link v-if="issuer_id" slot="right-footer" :underline="false" style="margin-left: 20px">已经分配
{{ app_private_used_number }} 还有 {{ 100 - app_private_used_number }} 可分配使用
</el-link>
</el-transfer>
</div>
</div>
</transition>
</template>
<script>
import {developerBindAppFun} from "@/restful";
import {sort_compare} from "@/utils";
import {Loading} from "element-ui";
export default {
name: 'AppleDeveloperBindApp',
props: {
transitionName: {
type: String,
default: 'appledeveloperbind'
},
app_id: {
type: String,
default: undefined
},
issuer_id: {
type: String,
default: undefined
},
},
data() {
return {
app_developer_lists: [],
left_data: [],
right_data: [],
choices_data: [],
choices_data_list: [],
s_props: {},
s_titles: [],
app_private_used_number: 100,
app_limit_number: 0,
};
},
mounted() {
this.getinfos()
},
methods: {
get_choice_data_from_key(key_list) {
let n_data = [];
let field = 'app_id'
if (this.app_id) {
field = 'issuer_id'
}
this.app_private_used_number = 0
for (let i = 0; i < this.app_developer_lists.length; i++) {
for (let j = 0; j < key_list.length; j++) {
if (key_list[j] === this.app_developer_lists[i][field]) {
n_data.push(this.app_developer_lists[i])
this.app_private_used_number += this.app_developer_lists[i]['app_usable_number']
}
}
}
return [n_data, this.app_private_used_number]
},
handleChange(value, direction, movedKeys) {
if (this.issuer_id && direction === 'right') {
//
// for (let i = 0; i < movedKeys.length; i++) {
// let app_info = this.get_choice_data_from_key([movedKeys[i]])[0][0]
// if (!(app_info && app_info.app_used_number > 0)) {
// this.choices_data.splice(this.choices_data.indexOf(movedKeys[i]), 1)
// }
// }
}
if (this.app_id && direction === 'right') {
for (let i = 0; i < movedKeys.length; i++) {
let developer_info = this.get_choice_data_from_key([movedKeys[i]])[0][0]
if (!(developer_info && developer_info.app_used_count)) {
this.choices_data.splice(this.choices_data.indexOf(movedKeys[i]), 1)
}
}
}
this.get_choice_data_from_key(value)
},
check_used_number() {
if (this.get_choice_data_from_key(this.choices_data)[1] > 100) {
this.$message.warning("超出最大分配额度,请注意,尽量保证所有应用分配额度总和为 100")
}
},
saveNumber(infos) {
developerBindAppFun(data => {
if (data.code === 1000) {
// this.choices_data = this.format_data(data.data)
} else {
this.$message.error("数据更新失败" + data.msg)
}
this.getinfos()
}, {
methods: 'PUT',
data: {'app_id': this.app_id, 'issuer_id': this.issuer_id, 'infos': infos}
})
},
getinfos() {
if (this.issuer_id) {
this.s_props = {
key: 'app_id',
label: 'name',
}
this.getBindInfo({act: 'apps'})
// eslint-disable-next-line no-unused-vars
setTimeout(_ => {
this.check_used_number()
}, 1000)
} else {
this.s_props = {
key: 'issuer_id',
label: 'issuer_id',
disabled: 'is_disabled'
}
this.s_titles = ['可分配签名开发者账户', '已分配签名开发者账户']
this.getBindInfo({act: 'developer'})
}
this.getBindInfo({})
},
saveAppleApps() {
developerBindAppFun(data => {
if (data.code === 1000) {
this.$message.success("数据保存成功")
} else {
this.$message.error("数据更新失败" + data.msg)
}
this.getinfos()
}, {
methods: 'POST',
data: {'app_id': this.app_id, 'issuer_id': this.issuer_id, 'choices_data': this.choices_data}
})
},
getBindInfo(params) {
let loadingInstance = Loading.service({target: '#transfer'})
if (this.issuer_id) {
params.issuer_id = this.issuer_id
}
if (this.app_id) {
params.app_id = this.app_id
}
developerBindAppFun(data => {
if (data.code === 1000) {
if (params.act === 'apps' || params.act === 'developer') {
this.app_developer_lists = data.data
if (params.act === 'developer') {
this.app_developer_lists.sort(sort_compare('developer_used_number'))
}
if (params.act === 'apps') {
if (data.app_limit_number) {
this.app_limit_number = data.app_limit_number
}
this.s_titles = ['可分配苹果应用', '已分配苹果应用,最多可以分配' + this.app_limit_number + '个应用']
}
this.get_choice_data_from_key(this.choices_data)
} else {
this.choices_data_list = data.data;
this.choices_data = this.format_data(data.data)
this.get_choice_data_from_key(this.choices_data)
}
} else {
this.$message.error("数据获取失败" + data.msg)
}
this.$nextTick(() => { // Loading
loadingInstance.close()
});
}, {methods: 'GET', data: params})
},
format_data(data) {
let n_data = [];
let field = 'app_id'
if (this.app_id) {
field = 'issuer_id'
}
this.app_private_used_number = 0
if (data && data.length > 0) {
for (let i = 0; i < data.length; i++) {
n_data.push(data[i][field])
}
}
return n_data
},
}
}
</script>
<style scoped>
/deep/ .el-transfer-panel {
width: 460px;
}
/*/deep/ .el-transfer-panel__item {*/
/* height: 60px;*/
/*}*/
.transfer-footer {
text-align: center;
float: right;
}
</style>

@ -21,6 +21,7 @@
</div> </div>
请联系域名管理员前往 <strong>{{ domain_name }}</strong> 域名 DNS 管理后台添加如下 CNAME 记录 请联系域名管理员前往 <strong>{{ domain_name }}</strong> 域名 DNS 管理后台添加如下 CNAME 记录
<el-table <el-table
v-loading="is_loading"
:data="domain_tData" :data="domain_tData"
border border
stripe stripe
@ -51,6 +52,11 @@
prop="dns" prop="dns"
width="300"> width="300">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tooltip content="该记录值为私有下载服务器">
<el-tag v-if="scope.row.is_private" style="margin-right: 5px" type="small">Private</el-tag>
</el-tooltip>
<el-tooltip content="点击复制到剪贴板"> <el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.dns" v-clipboard:copy="scope.row.dns" <el-link v-if="scope.row.dns" v-clipboard:copy="scope.row.dns"
v-clipboard:success="copy_success" v-clipboard:success="copy_success"
@ -80,7 +86,7 @@
<el-col :span="6"> <el-col :span="6">
<el-button plain size="small" style="margin-top: 8px" type="danger" <el-button plain size="small" style="margin-top: 8px" type="danger"
@click="remove_domain"> @click="remove_domain">
解除绑定 解除绑定并删除记录
</el-button> </el-button>
</el-col> </el-col>
</el-row> </el-row>
@ -98,13 +104,14 @@
<el-col :span="6"> <el-col :span="6">
<el-button plain size="small" style="margin-top: 8px" type="danger" <el-button plain size="small" style="margin-top: 8px" type="danger"
@click="remove_domain"> @click="remove_domain">
解除绑定 解除绑定并删除记录
</el-button> </el-button>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<el-table <el-table
v-loading="is_loading"
:data="domain_tData" :data="domain_tData"
border border
stripe stripe
@ -126,10 +133,23 @@
label="记录值" label="记录值"
prop="dns" prop="dns"
width="300"> width="300">
<template slot-scope="scope">
<el-tooltip content="该记录值为私有下载服务器">
<el-tag v-if="scope.row.is_private" style="margin-right: 5px" type="small">Private</el-tag>
</el-tooltip>
<el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.dns" v-clipboard:copy="scope.row.dns"
v-clipboard:success="copy_success"
:underline="false">{{ scope.row.dns }}
</el-link>
</el-tooltip>
</template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div v-if="!bind_status" style="text-align: center;margin: 30px 0"> <div v-if="!bind_status" style="text-align: center;margin: 30px 0">
<el-button plain type="success" @click="check_cname">已经修改配置再次检查绑定</el-button> <el-button :disabled="is_loading" plain type="success" @click="check_cname">已经修改配置再次检查绑定</el-button>
</div> </div>
</div> </div>
@ -139,8 +159,8 @@
<el-button :disabled="bind_status|| active===1 " <el-button :disabled="bind_status|| active===1 "
@click="last">上一步 @click="last">上一步
</el-button> </el-button>
<el-button v-if="force_bind" @click="next(1)">强制绑定</el-button> <el-button v-if="force_bind" :disabled="is_loading" @click="next(1)">强制绑定</el-button>
<el-button v-else @click="next(0)">下一步</el-button> <el-button v-else :disabled="is_loading" @click="next(0)">下一步</el-button>
</div> </div>
@ -170,22 +190,30 @@ export default {
type: Boolean, type: Boolean,
default: false default: false
}, },
c_domain_name: {
type: String,
default: ''
}
}, },
data() { data() {
return { return {
active: 1, active: 1,
bind_status: false, bind_status: false,
bind_domain_sure: true, bind_domain_sure: true,
domain_tData: [{'type': 'CNAME', 'host': 'xxx', 'dns': 'demo.xxx.cn', 'is_private': false}],
domain_name: '', domain_name: '',
domain_tData: [{'type': 'CNAME', 'host': 'xxx', 'dns': 'demo.xxx.cn'}],
force_bind: false, force_bind: false,
b_t_msg: '您的账户', b_t_msg: '您的账户',
is_loading: false
} }
}, },
mounted() { mounted() {
if (this.domain_type === 2) { if (this.domain_type === 2) {
this.b_t_msg = '您的应用' this.b_t_msg = '您的应用'
} }
if (this.c_domain_name) {
this.domain_name = this.c_domain_name
}
this.bind_click(); this.bind_click();
}, },
beforeDestroy() { beforeDestroy() {
@ -196,10 +224,13 @@ export default {
this.$message.success('复制剪切板成功'); this.$message.success('复制剪切板成功');
}, },
check_cname() { check_cname() {
this.is_loading = true
domainFun(data => { domainFun(data => {
this.is_loading = false
if (data.code === 1000) { if (data.code === 1000) {
if (this.active++ > 2) this.active = 3; if (this.active++ > 2) this.active = 3;
this.bind_status = true; this.bind_status = true;
this.$message.success("绑定成功")
if (!this.app_id) { if (!this.app_id) {
if (this.domain_type === 1) { if (this.domain_type === 1) {
this.$store.dispatch("dodomainshow", false); this.$store.dispatch("dodomainshow", false);
@ -218,7 +249,7 @@ export default {
this.bind_status = false; this.bind_status = false;
this.$message.error("绑定失败 " + data.msg) this.$message.error("绑定失败 " + data.msg)
} }
}, {methods: 'PUT', data: {app_id: this.app_id, domain_type: this.domain_type}}) }, {methods: 'PUT', data: {app_id: this.app_id, domain_type: this.domain_type, domain_name: this.domain_name}})
}, },
remove_domain() { remove_domain() {
domainFun(data => { domainFun(data => {
@ -237,7 +268,10 @@ export default {
} else { } else {
this.$message.error("解除绑定失败 " + data.msg) this.$message.error("解除绑定失败 " + data.msg)
} }
}, {methods: 'DELETE', data: {app_id: this.app_id, domain_type: this.domain_type}}); }, {
methods: 'DELETE',
data: {app_id: this.app_id, domain_type: this.domain_type, domain_name: this.domain_name}
});
}, },
bind_click() { bind_click() {
this.$store.dispatch("dosetdomainstate", false); this.$store.dispatch("dosetdomainstate", false);
@ -248,7 +282,7 @@ export default {
this.domain_name = data.data.domain_name; this.domain_name = data.data.domain_name;
} }
if (data.data.domain_record) { if (data.data.domain_record) {
this.format_domain_tData(data.data.domain_record); this.format_domain_tData(data.data);
if (this.active++ > 2) this.active = 3; if (this.active++ > 2) this.active = 3;
} }
if (data.data.is_enable) { if (data.data.is_enable) {
@ -260,7 +294,7 @@ export default {
} else { } else {
this.$message.error("绑定失败 " + data.msg) this.$message.error("绑定失败 " + data.msg)
} }
}, {methods: 'GET', data: {app_id: this.app_id, domain_type: this.domain_type}}); }, {methods: 'GET', data: {app_id: this.app_id, domain_type: this.domain_type, domain_name: this.domain_name}});
}, },
format_domain_tData(cname_domain) { format_domain_tData(cname_domain) {
let domain_name_list = this.domain_name.split('.'); let domain_name_list = this.domain_name.split('.');
@ -271,14 +305,15 @@ export default {
domain_name_list.splice(domain_name_list.length - 2, 2); domain_name_list.splice(domain_name_list.length - 2, 2);
this.domain_tData[0].host = domain_name_list.join(".") this.domain_tData[0].host = domain_name_list.join(".")
} }
this.domain_tData[0].dns = cname_domain; this.domain_tData[0].dns = cname_domain.domain_record;
this.domain_tData[0].is_private = cname_domain.is_private;
}, },
next(force_bind) { next(force_bind) {
if (this.active === 1) { if (this.active === 1) {
domainFun(data => { domainFun(data => {
if (data.code === 1000) { if (data.code === 1000) {
if (data.data && data.data.cname_domain) { if (data.data && data.data.domain_record) {
this.format_domain_tData(data.data.cname_domain); this.format_domain_tData(data.data);
if (this.active++ > 2) this.active = 3; if (this.active++ > 2) this.active = 3;
} }
} else { } else {

@ -15,9 +15,13 @@
<el-divider direction="vertical"/> <el-divider direction="vertical"/>
<div class="item"><p style="margin-bottom: 16px">应用举报</p> <div class="item"><p style="margin-bottom: 16px">应用举报</p>
</div> </div>
<p><b>flyapps@126.com</b></p> <div>
<el-tooltip content="拿出手机扫描添加微信" style="margin-top: 33px"> <el-link :href="github" :underline="false"><p><b>FlyApps</b></p></el-link>
<el-image :lazy="true" :src="require('@/assets/wxchat.jpg')" alt="扫描添加微信" style="width: 160px"> </div>
<el-divider/>
<el-tooltip content="拿出手机扫一扫" style="margin-top: 33px">
<el-image :alt="github" :lazy="true" :src="require('@/assets/github.png')"
style="width: 160px">
</el-image> </el-image>
</el-tooltip> </el-tooltip>
@ -30,7 +34,8 @@ export default {
name: "FirContact", name: "FirContact",
data() { data() {
return { return {
bg_img: require("@/assets/imgs/banner_1.jpg") bg_img: require("@/assets/imgs/banner_1.jpg"),
github: process.env.base_env.github
} }
}, },
} }
@ -44,7 +49,7 @@ export default {
.content { .content {
width: 942px; width: 942px;
margin: 80px auto 160px; margin: 10px auto 160px;
text-align: center; text-align: center;
} }

@ -114,12 +114,12 @@ export default {
{ {
logo: require("@/assets/imgs/qq.png"), logo: require("@/assets/imgs/qq.png"),
title: "核心优势3", title: "核心优势3",
content: "<p>利用QQ微信等提供在线解答</p>帮助用户快速准确解决问题和故障" content: "<p>QQ微信等提供在线解答</p>帮助用户快速准确解决问题"
}, },
{ {
logo: require("@/assets/imgs/skill.png"), logo: require("@/assets/imgs/skill.png"),
title: "核心优势4", title: "核心优势4",
content: "<p>由技术支持工程师,负责问题解答</p>需求受理及故障受理" content: "<p>自定义配置,容器化部署</p>升级维护超级便捷"
} }
] ]
} }

@ -21,10 +21,10 @@
<el-link @click="$router.push({name:'FirApps'})">登录</el-link> <el-link @click="$router.push({name:'FirApps'})">登录</el-link>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-link @click="$router.push({name:'FirRegist'})">注册</el-link> <el-link @click="$router.push({name:'FirContact'})">联系我们</el-link>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-link @click="$router.push({name:'FirContact'})">联系我们</el-link> <el-link @click="goGithub">Github</el-link>
</el-col> </el-col>
</el-row> </el-row>
</el-col> </el-col>
@ -64,6 +64,11 @@ export default {
'line-height': '45px', 'line-height': '45px',
background: '#e7eaf1' background: '#e7eaf1'
}, },
github: process.env.base_env.github
}
}, methods: {
goGithub() {
window.open(this.github, '_blank', '');
} }
} }
} }

@ -6,8 +6,8 @@
<p style="color:#b2b2b2">Company News</p> <p style="color:#b2b2b2">Company News</p>
</div> </div>
<div class="nav text-center"> <div class="nav text-center">
<a class="active" href="#">公司新闻</a> <a class="active" href="#">开发历程</a>
<a href="#">行业动态</a> <a href="#">发展足迹</a>
</div> </div>
<ul class="news-container"> <ul class="news-container">
<li v-for="(item,index) in newsList" :key="index"> <li v-for="(item,index) in newsList" :key="index">
@ -40,6 +40,18 @@ export default {
show: false, show: false,
newsList: [ newsList: [
{ {
id: '008',
title: 'ios16系统',
introduce: '超级签名字支持ios16系统签名,只需要手机开启调试模式,即可安装打开',
date: '09-22',
year: '2022'
}, {
id: '007',
title: '私有下载页部署',
introduce: '支持私有下载页部署,方便快捷,稳定访问',
date: '05-18',
year: '2022'
}, {
id: '001', id: '001',
title: '现有版本', title: '现有版本',
introduce: '经过好长时间的编写,平台已经支持超级签名,权限管理,自定义域名,自定义存储', introduce: '经过好长时间的编写,平台已经支持超级签名,权限管理,自定义域名,自定义存储',

File diff suppressed because it is too large Load Diff

@ -54,7 +54,7 @@
action="#" action="#"
drag drag
style="max-height: 200px"> style="max-height: 200px">
<img v-if="upload_pic_b" :src="upload_pic_b" class="avatar"> <img v-if="upload_pic_b" :src="upload_pic_b" alt="" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i> <i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload> </el-upload>
</el-form-item> </el-form-item>
@ -64,6 +64,7 @@
<el-form-item label="展示权重"> <el-form-item label="展示权重">
<el-input-number v-model="editadvertinfo.weight" :max="100" :min="0" <el-input-number v-model="editadvertinfo.weight" :max="100" :min="0"
label="展示权重"/> label="展示权重"/>
<el-tag style="margin-left: 10px">权重越大广告展示频率越高</el-tag>
</el-form-item> </el-form-item>
<el-form-item label="是否发布"> <el-form-item label="是否发布">
<el-switch <el-switch
@ -203,6 +204,7 @@
import {advertinfo} from "@/restful"; import {advertinfo} from "@/restful";
import {VueCropper} from 'vue-cropper' import {VueCropper} from 'vue-cropper'
import {AvatarUploadUtils, dataURLtoFile, getUserInfoFun} from '@/utils' import {AvatarUploadUtils, dataURLtoFile, getUserInfoFun} from '@/utils'
import {format_time} from "@/utils/base/utils";
export default { export default {
name: "FirUserAdvert", name: "FirUserAdvert",
@ -297,6 +299,12 @@ export default {
} }
}, },
beforeAvatarUpload(file, id, act) { beforeAvatarUpload(file, id, act) {
if (!file) {
this.$message.warning("展示图片不存在")
this.bind_advert_sure = false;
this.handleCurrentChange(1)
return
}
return AvatarUploadUtils(this, file, { return AvatarUploadUtils(this, file, {
'app_id': this.$store.state.userinfo.uid, 'app_id': this.$store.state.userinfo.uid,
'upload_key': file.name, 'upload_key': file.name,
@ -388,11 +396,7 @@ export default {
}, },
format_time(stime) { format_time(stime) {
if (stime) { return format_time(stime)
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
}, },
}, mounted() { }, mounted() {
getUserInfoFun(this); getUserInfoFun(this);

@ -6,8 +6,10 @@
:title="domain_title" :title="domain_title"
:visible.sync="bind_domain_sure" :visible.sync="bind_domain_sure"
width="666px"> width="666px">
<bind-domain v-if="bind_domain_sure" :app_id="current_domain_info.app_id" :domain_state="true" <bind-domain v-if="bind_domain_sure" :app_id="current_domain_info.app_id"
:domain_type="current_domain_info.domain_type" transitionName="bind-app-domain"/> :c_domain_name="current_domain_info.domain_name"
:domain_state="true" :domain_type="current_domain_info.domain_type"
transitionName="bind-app-domain"/>
</el-dialog> </el-dialog>
<div> <div>
<el-input <el-input
@ -19,13 +21,17 @@
搜索 搜索
</el-button> </el-button>
<div style="float: right"> <div style="float: right">
<el-button plain type="primary" @click="$store.dispatch('dodomainaction', 1)"> <el-tooltip content="应用安装下载页,多个下载页域名可以避免域名被封导致其他应用也无法访问">
设置下载页域名 <el-button plain type="primary" @click="$store.dispatch('dodomainaction', 1)">
</el-button> 添加下载页域名
<el-button v-if="$store.state.userinfo&&$store.state.userinfo.role >1" plain type="primary" </el-button>
@click="$store.dispatch('dodomainaction', 2)"> </el-tooltip>
设置下载码域名 <el-tooltip content="用与生成预览和下载码的域名">
</el-button> <el-button plain type="primary" @click="$store.dispatch('dodomainaction', 2)">
设置下载码域名
</el-button>
</el-tooltip>
</div> </div>
@ -42,6 +48,9 @@
label="绑定域名" label="绑定域名"
prop="domain_name"> prop="domain_name">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tooltip content="该域名解析到了私有下载服务器上面">
<el-tag v-if="scope.row.is_private" style="margin-right: 5px" type="small">Private</el-tag>
</el-tooltip>
<el-tooltip content="点击复制到剪贴板"> <el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.domain_name" v-clipboard:copy="scope.row.domain_name" <el-link v-if="scope.row.domain_name" v-clipboard:copy="scope.row.domain_name"
v-clipboard:success="copy_success" v-clipboard:success="copy_success"
@ -71,6 +80,28 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
align="center"
label="开启https"
prop="is_https"
width="110">
<template slot-scope="scope">
<el-tooltip content="点击关闭" placement="top">
<div slot="content">
<span v-if="scope.row.is_https">
点击关闭 HTTPS 访问
</span>
<span v-else>
点击开启支持 HTTPS 访问
</span>
</div>
<el-button v-if="scope.row.is_https" size="small" type="success"
@click="changeHttpsFun(scope.row, false)">已开启
</el-button>
<el-button v-else size="small" type="info" @click="changeHttpsFun(scope.row, true)">未开启</el-button>
</el-tooltip>
</template>
</el-table-column>
<el-table-column <el-table-column
@ -95,7 +126,30 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
align="center"
label="跳转权重"
prop="weight"
width="110">
<template slot-scope="scope">
<el-popover v-if="scope.row.domain_type === 1" placement="top" trigger="hover">
<p>绑定域名{{ scope.row.domain_name }}</p>
<p>域名类型{{ format_domain_type(scope.row) }}</p>
<p>权重越大下载域名使用频率越高</p>
<p>
跳转权重:
<el-input-number v-model="scope.row.weight" :max="100" :min="1"
label="跳转权重" size="small"/>
<el-button size="small" style="margin-left: 10px" @click="saveWeight(scope.row)">保存修改</el-button>
</p>
<div slot="reference" class="name-wrapper">
<el-link :underline="false" plain size="small">{{ scope.row.weight }}
</el-link>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column <el-table-column
:formatter="format_create_time" :formatter="format_create_time"
align="center" align="center"
@ -124,9 +178,10 @@
<script> <script>
import {domaininfo} from "@/restful"; import {domaininfo, personalConfigInfo} from "@/restful";
import BindDomain from "@/components/base/BindDomain"; import BindDomain from "@/components/base/BindDomain";
import {getUserInfoFun} from '@/utils' import {format_choices, getUserInfoFun} from '@/utils'
import {format_time} from "@/utils/base/utils";
export default { export default {
name: "FirUserDomain", name: "FirUserDomain",
@ -141,9 +196,81 @@ export default {
domain_type_choices: [], domain_type_choices: [],
current_domain_info: {'domain_type': 1, 'app_id': null}, current_domain_info: {'domain_type': 1, 'app_id': null},
loading: false, loading: false,
configVisible: false,
config_lists: [],
short_download_uri: ''
} }
}, },
methods: { methods: {
changeHttpsFun(domain_info, is_https) {
domain_info['is_https'] = is_https
domaininfo(data => {
if (data.code === 1000) {
this.$message.success("https支持修改成功")
} else {
this.$message.error("修改失败 " + data.msg)
}
}, {methods: 'PUT', data: domain_info})
},
configFun() {
personalConfigInfo(data => {
if (data.code === 1000) {
if (data.data.length === 1) {
this.short_download_uri = data.data[0].value
}
} else {
this.$message.error("获取数据失败了 " + data.msg)
}
}, {
methods: 'GET'
}, 'short_download_uri')
personalConfigInfo(data => {
if (data.code === 1000) {
this.config_lists = data.data
this.configVisible = true
} else {
this.$message.error("获取数据失败了 " + data.msg)
}
}, {
methods: 'GET'
}, 'preview_route')
},
changeConfig(info) {
personalConfigInfo(data => {
if (data.code === 1000) {
this.$message.success("操作成功")
this.configFun()
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'PUT', data: {config_key: info.key, config_value: info.value}
}, 'preview_route')
},
updateConfig() {
personalConfigInfo(data => {
if (data.code === 1000) {
this.$message.success("操作成功")
this.configFun()
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'DELETE'
}, 'preview_route')
},
saveWeight(domain_info) {
domaininfo(data => {
if (data.code === 1000) {
this.$message.success("权重修改成功")
} else {
this.$message.error("权重修改失败 " + data.msg)
}
}, {methods: 'PUT', data: domain_info})
},
show_bind_domain_info(domain_info) { show_bind_domain_info(domain_info) {
this.bind_domain_sure = true; this.bind_domain_sure = true;
let app_id = null; let app_id = null;
@ -152,6 +279,7 @@ export default {
} }
this.current_domain_info.app_id = app_id; this.current_domain_info.app_id = app_id;
this.current_domain_info.domain_type = domain_info.domain_type; this.current_domain_info.domain_type = domain_info.domain_type;
this.current_domain_info.domain_name = domain_info.domain_name;
this.domain_title = this.format_domain_type(domain_info) + ' 绑定详情'; this.domain_title = this.format_domain_type(domain_info) + ' 绑定详情';
}, },
appInfos(app_info) { appInfos(app_info) {
@ -195,39 +323,25 @@ export default {
this.loading = false; this.loading = false;
}, {methods: 'GET', data: params}) }, {methods: 'GET', data: params})
}, },
format_choices(key, obj) {
for (let i = 0; i < obj.length; i++) {
if (key === obj[i].id) {
return obj[i].name
}
}
return "未知"
},
format_domain_type(row) { format_domain_type(row) {
if (row.domain_type === 2) { if (row.domain_type === 2) {
return "应用" + row.app_info.name + "域名" return "应用" + row.app_info.name + "域名"
} }
return this.format_choices(row.domain_type, this.domain_type_choices) return format_choices(row.domain_type, this.domain_type_choices)
}, },
format_create_time(row) { format_create_time(row) {
return this.format_time(row.created_time) return format_time(row.created_time)
}, },
format_time(stime) {
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
},
}, mounted() { }, mounted() {
getUserInfoFun(this); getUserInfoFun(this);
this.get_data_from_tabname(); this.get_data_from_tabname();
}, watch: { }, watch: {
'$store.state.domian_show_state': function () { '$store.state.domain_show_state': function () {
if (this.$store.state.domian_show_state) { if (this.$store.state.domain_show_state) {
this.bind_domain_sure = false; this.bind_domain_sure = false;
this.get_data_from_tabname(); this.get_data_from_tabname();
} }

@ -0,0 +1,470 @@
<template>
<el-main>
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="false"
:visible.sync="configVisible"
center
title="下载页部署配置"
width="666px">
<el-card v-for="info in config_lists" :key="info.key" class="box-card" shadow="hover"
style="margin-bottom: 20px">
<div slot="header" class="clearfix">
<span><el-tag size="medium" type="info">配置KEY</el-tag> <el-tag size="medium">{{ info.key }}</el-tag></span>
<div style="float: right">
<el-switch
v-model="info.value"
active-color="#13ce66"
active-text="启用"
active-value="true"
inactive-color="#ff4949"
inactive-text="关闭"
inactive-value="false"
@change="changeConfig(info)">
</el-switch>
</div>
</div>
<el-tag size="medium" type="info">描述信息</el-tag>
{{ info.title }}
</el-card>
<el-divider></el-divider>
<el-card style="margin-top: 20px">
<div slot="header" class="clearfix">
<el-tag size="medium" type="success">下载页源码及操作文档</el-tag>
</div>
<div v-if="short_download_list.length>0">
<div style="margin: 10px" v-for="short_download_uri in short_download_list" :key="short_download_uri.key">
<el-tag size="medium" type="info">{{short_download_uri.title}}</el-tag>
<el-link style="margin-left: 10px" :href="short_download_uri.value" target="_blank">点击下载</el-link>
</div>
</div>
</el-card>
<div slot="footer" class="dialog-footer">
<el-button @click="updateConfig">恢复默认值</el-button>
<el-button @click="configVisible=false">取消</el-button>
</div>
</el-dialog>
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="false"
:visible.sync="addCnameVisible"
center
title="添加下载页服务器域名"
width="800px">
<el-steps :active="p_active" finish-status="success" :align-center="true">
<el-step title="添加服务器域名"></el-step>
<el-step title="验证服务器域名所有权"></el-step>
<el-step title="添加成功"></el-step>
</el-steps>
<el-container>
<div v-if="p_active===0" style="margin-top: 20px">
<el-tag style="margin: 10px 5px">1.如果是cdn等含有CNAME直接添加cdn的 CNAME </el-tag>
<el-tag style="margin: 10px 5px">2.如果是服务器需要将域名解析到服务器然后添加该解析的域名</el-tag>
<div style="width: 600px;text-align: center;margin: auto">
<el-form ref="form" :model="addSerInfo" label-width="120px">
<el-form-item label="下载服务器域名">
<el-input v-model="addSerInfo.domain_record" placeholder="cdn的cname或者服务器的域名,必填"></el-input>
</el-form-item>
<el-form-item label="下载服务器地址">
<el-input v-model="addSerInfo.ip_address" placeholder="如果有服务器地址,添加服务器ip,否则填写服务器域名"></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input type="textarea" v-model="addSerInfo.description"></el-input>
</el-form-item>
</el-form>
<div style="margin-top: 20px;">
<el-button type="primary" @click="addCnameSer">下一步</el-button>
</div>
</div>
</div>
<div v-else-if="p_active===1" style="margin-top: 30px;text-align: center">
请联系域名管理员前往 <strong>{{ cnameInfo.domain_record }}</strong> 域名 DNS 管理后台添加如下 {{ cnameInfo.r_type }} 记录
<el-table
:data="domain_tData"
border
stripe
v-loading="loading"
style="width: 100%;margin-top: 20px">
<el-table-column
align="center"
label="记录类型"
prop="r_type"
width="100">
</el-table-column>
<el-table-column
align="center"
label="主机记录"
prop="host_r"
>
<template slot-scope="scope">
<el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.host_r" v-clipboard:copy="scope.row.host_r"
v-clipboard:success="copy_success"
:underline="false">{{ scope.row.host_r }}
</el-link>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
align="center"
label="记录值"
prop="cname_r"
width="300">
<template slot-scope="scope">
<el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.cname_r" v-clipboard:copy="scope.row.cname_r"
v-clipboard:success="copy_success"
:underline="false">{{ scope.row.cname_r }}
</el-link>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<el-alert :closable="false"
show-icon
style="margin-top: 30px"
title="请在域名DNS配置成功后,点击“下一步”按钮"
type="warning"/>
<div style="margin-top: 20px;text-align: center;">
<el-button type="primary" @click="p_active=0">上一步</el-button>
<el-button type="primary" @click="checkCnameSer" :disabled="loading">下一步</el-button>
</div>
</div>
</el-container>
</el-dialog>
<div>
<el-input
v-model="search_key"
clearable
placeholder="输入下载服务器域名"
style="width: 30%;margin-right: 30px;margin-bottom: 5px"/>
<el-button icon="el-icon-search" type="primary" @click="handleCurrentChange(1)">
搜索
</el-button>
<div style="float: right">
<el-tooltip content="下载页配置,可以定制部署私有下载页">
<el-button plain type="primary" @click="configFun">
下载页配置
</el-button>
</el-tooltip>
<el-button plain type="primary" @click="addDownloadSer">
添加下载页服务器
</el-button>
</div>
<el-table
v-loading="loading"
:data="cname_info_list"
border
stripe
style="width: 100%">
<el-table-column
align="center"
fixed
label="下载服务器域名"
prop="domain_name">
<template slot-scope="scope">
<el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.domain_record" v-clipboard:copy="scope.row.domain_record"
v-clipboard:success="copy_success"
:underline="false">{{ scope.row.domain_record }}
</el-link>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
align="center"
label="下载服务器地址"
prop="ip_address">
</el-table-column>
<el-table-column
align="center"
label="状态"
prop="is_enable"
width="110">
<template slot-scope="scope">
<el-tooltip v-if="scope.row.is_enable === true" content="已经绑定成功" placement="left-start">
<el-button size="small" type="success">成功
</el-button>
</el-tooltip>
<el-tooltip v-else content="点击激活服务器域名绑定信息" placement="left-start">
<el-button size="small" type="warning" @click="continueBind(scope.row)">继续绑定
</el-button>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
:formatter="format_create_time"
align="center"
label="域名绑定时间"
prop="created_time"
width="170"
>
</el-table-column>
<el-table-column
align="center"
label="备注"
prop="description">
</el-table-column>
<el-table-column
align="center"
label="操作"
prop="is_enable"
width="100">
<template slot-scope="scope">
<el-tooltip content="删除该下载页服务器" placement="left-start">
<el-button size="small" type="danger" @click="delCnameSer(scope.row)">删除
</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<div style="margin-top: 20px;margin-bottom: 20px">
<el-pagination
:current-page.sync="pagination.currentPage"
:page-size="pagination.pagesize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total,sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handleCurrentChange">
</el-pagination>
</div>
</el-main>
</template>
<script>
import {dCnameInfoFun, personalConfigInfo} from "@/restful";
import {getUserInfoFun} from '@/utils'
import {format_time} from "@/utils/base/utils";
export default {
name: "FirUserDomain",
data() {
return {
addSerInfo: {},
cnameInfo: {},
domain_tData: [],
p_active: 0,
cname_info_list: [],
search_key: "",
pagination: {"currentPage": 1, "total": 0, "pagesize": 10},
loading: false,
configVisible: false,
addCnameVisible: false,
config_lists: [],
short_download_list: []
}
},
methods: {
continueBind(cname_info) {
this.addSerInfo = cname_info
this.addCnameVisible = true
this.addCnameSer()
},
delCnameSer(cname_info) {
this.$confirm('若下载域名绑定到该解析,同时也会删除绑定的下载域名,是否继续删除?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
dCnameInfoFun(data => {
if (data.code === 1000) {
this.$message.success("删除成功")
this.get_data_from_tabname();
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'DELETE',
data: {'domain_record': cname_info.domain_record}
})
})
},
checkCnameSer() {
this.loading=true
dCnameInfoFun(data => {
this.loading=false
if (data.code === 1000) {
this.$message.success(this.addSerInfo.domain_record + " 添加成功")
this.get_data_from_tabname();
this.addDownloadSer()
this.addCnameVisible = false
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'PUT',
data: {'act': 'check', 'domain_record': this.addSerInfo.domain_record}
})
},
addCnameSer() {
let domain_record = this.addSerInfo.domain_record
let ip_address = this.addSerInfo.ip_address
if (domain_record && domain_record.length > 6 && ip_address && ip_address.length > 6) {
dCnameInfoFun(data => {
if (data.code === 1000) {
this.p_active = 1
this.cnameInfo = data.data
this.domain_tData = [this.cnameInfo]
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'POST',
data: this.addSerInfo
})
} else {
this.$message.error("参数有误")
}
},
addDownloadSer() {
this.addSerInfo = {}
this.cnameInfo = {}
this.p_active = 0
this.addCnameVisible = true
},
configFun() {
personalConfigInfo(data => {
if (data.code === 1000) {
this.short_download_list = data.data
} else {
this.$message.error("获取数据失败了 " + data.msg)
}
}, {
methods: 'GET'
}, 'short_download_uri')
personalConfigInfo(data => {
if (data.code === 1000) {
this.config_lists = data.data
this.configVisible = true
} else {
this.$message.error("获取数据失败了 " + data.msg)
}
}, {
methods: 'GET'
}, 'preview_route')
},
changeConfig(info) {
personalConfigInfo(data => {
if (data.code === 1000) {
this.$message.success("操作成功")
this.configFun()
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'PUT', data: {config_key: info.key, config_value: info.value}
}, 'preview_route')
},
updateConfig() {
personalConfigInfo(data => {
if (data.code === 1000) {
this.$message.success("操作成功")
this.configFun()
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'DELETE'
}, 'preview_route')
},
copy_success() {
this.$message.success('复制剪切板成功');
},
handleSizeChange(val) {
this.pagination.pagesize = val;
this.get_data_from_tabname({
"size": this.pagination.pagesize,
"page": 1
})
},
handleCurrentChange(val) {
this.pagination.currentPage = val;
this.get_data_from_tabname({
"size": this.pagination.pagesize,
"page": this.pagination.currentPage
})
},
get_data_from_tabname(data = {}) {
data.search_key = this.search_key.replace(/^\s+|\s+$/g, "");
this.UserCnameInfoFun(data)
},
UserCnameInfoFun(params) {
this.loading = true;
dCnameInfoFun(data => {
if (data.code === 1000) {
this.cname_info_list = data.data;
this.pagination.total = data.count;
} else {
this.$message.error("域名绑定信息获取失败")
}
this.loading = false;
}, {methods: 'GET', data: params})
},
format_create_time(row) {
return format_time(row.created_time)
},
}, mounted() {
getUserInfoFun(this);
this.get_data_from_tabname();
}
}
</script>
<style scoped>
.el-main {
margin: 20px auto 100px;
width: 1166px;
position: relative;
padding-bottom: 1px;
color: #9b9b9b;
-webkit-font-smoothing: antialiased;
border-radius: 1%;
}
</style>

@ -0,0 +1,733 @@
<template>
<el-main>
<el-dialog :close-on-click-modal="false" :destroy-on-close="true" :title="title"
:visible.sync="dialogReceiverVisible" width="780px">
<el-form ref="recevierform" :model="addreceiverinfo"
label-width="80px" style="margin:0 auto;">
<el-form-item label="姓名">
<el-input v-model="addreceiverinfo.receiver_name"/>
</el-form-item>
<el-form-item label="邮箱">
<el-input v-model="addreceiverinfo.email"/>
</el-form-item>
<el-form-item v-if="captcha.captcha_image" style="height: 40px">
<el-row style="height: 40px">
<el-col :span="16">
<el-input v-model="addreceiverinfo.verify_code" clearable maxlength="6"
placeholder="请输入验证码" @keyup.enter.native="onSubmit"/>
</el-col>
<el-col :span="8">
<el-image
:src="captcha.captcha_image"
fit="contain"
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px" @click="get_auth_code">
</el-image>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="16">
<el-input v-model="addreceiverinfo.seicode" clearable
placeholder="邮箱验证码" prefix-icon="el-icon-mobile"/>
</el-col>
<el-col :span="8">
<el-button plain style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px" type="info"
@click="onGetCode">获取验证码
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<div id="captcha" ref="captcha"></div>
</el-form-item>
<el-form-item label="微信">
<el-tag v-if="addreceiverinfo.wxopenid && addreceiverinfo.wxopenid.length > 6">
{{ addreceiverinfo.wxopenid }}
</el-tag>
<el-popover v-else
v-model="wx_visible"
placement="top"
title="打开微信扫一扫进行绑定"
trigger="manual">
<div>
<el-image :src="wx_login_qr_url" style="width: 176px;height: 166px"/>
</div>
<el-button slot="reference" size="small" @click="wxLogin">绑定微信</el-button>
</el-popover>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="addreceiverinfo.description"/>
</el-form-item>
<el-button @click="saveReceiver">保存</el-button>
<el-button @click="cancelReceiver">取消</el-button>
</el-form>
</el-dialog>
<el-dialog :close-on-click-modal="false" :destroy-on-close="true" :visible.sync="dialogChangeReceiverVisible"
title="修改消息接收人">
<div style="text-align: left;margin-bottom: 10px">
<div style="margin-bottom: 10px">
<el-tag type="warning">提醒如果以下消息接收人的信息有变更请到消息接收人管理中进行修改</el-tag>
</div>
消息类型{{ notifyConfigMsg.message_type }} - {{ notifyConfigMsg.config_name }}
</div>
<el-table
ref="multipleTable"
:data="receiver_info_lists"
border
stripe
style="width: 100%"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
align="center"
fixed
label="姓名"
prop="receiver_name"
width="100">
</el-table-column>
<el-table-column
align="center"
label="邮箱"
prop="email"
width="200">
</el-table-column>
<el-table-column
align="center"
label="微信"
prop="domain_name"
width="260">
<template slot-scope="scope">
<el-popover v-if="scope.row.weixin.openid" placement="top" trigger="hover">
<div style="float: right;margin-top: 10px">
<el-image :src="scope.row.weixin.head_img_url" style="width: 80px;height: 80px"/>
</div>
<p>昵称: {{ scope.row.weixin.nickname }}</p>
<p>性别: {{ scope.row.weixin.sex|sex_filter }}</p>
<p>住址: {{ scope.row.weixin.address }}</p>
<p>微信ID: {{ scope.row.weixin.openid }}</p>
<div slot="reference" class="name-wrapper">
<el-tag>{{ scope.row.weixin.openid }}</el-tag>
</div>
</el-popover>
<el-tag v-else> /</el-tag>
</template>
</el-table-column>
<el-table-column
align="center"
label="备注"
prop="description">
</el-table-column>
</el-table>
<div style="text-align: left;margin-bottom: 10px;margin-top: 10px">
<el-button plain size="small" type="primary" @click="addRecevier">
新增消息接收人
</el-button>
</div>
<span slot="footer">
<el-button @click="updateConfig(undefined)">保存</el-button>
<el-button @click="dialogChangeReceiverVisible=false">取消</el-button>
</span>
</el-dialog>
<el-tabs v-model="activeName" tab-position="top" type="border-card" @tab-click="handleClick">
<el-tab-pane label="基本接收管理" name="config">
<el-table
:data="[{name:'消息类型',email:'邮件通知',weixin:'微信通知',user:'消息接收人'}]"
:show-header="false"
border
stripe
style="width: 100%">
<el-table-column
align="center"
prop="name"
width="200">
</el-table-column>
<el-table-column
align="center"
prop="email"
width="200">
</el-table-column>
<el-table-column
align="center"
prop="weixin"
width="200">
</el-table-column>
<el-table-column
align="center"
prop="user">
</el-table-column>
</el-table>
<el-collapse v-model="activeConfig">
<el-collapse-item v-for="info in message_type_choices" :key="info.id" :disabled="info.disabled"
:name="info.id">
<template slot="title">
<div style="width: 200px">
<h2>{{ info.name }}</h2>
</div>
</template>
<el-table
:data="info.data"
:show-header="false"
border
stripe
style="width: 100%">
<el-table-column
align="center"
fixed
prop="config_name"
width="200">
</el-table-column>
<el-table-column
align="center"
fixed
prop="enable_email"
width="200">
<template slot-scope="scope">
<el-checkbox v-model="scope.row.enable_email" @change="updateConfig(scope.row)">邮件</el-checkbox>
</template>
</el-table-column>
<el-table-column
align="center"
fixed
prop="enable_weixin"
width="200">
<template slot-scope="scope">
<el-checkbox v-model="scope.row.enable_weixin" @change="updateConfig(scope.row)">微信</el-checkbox>
</template>
</el-table-column>
<el-table-column
align="center"
fixed
prop="senders">
<template slot-scope="scope">
<el-tag v-for="user in scope.row.senders" :key="user.id" style="margin-right: 3px" type="info">
{{ user.receiver_name }}
</el-tag>
<el-tag type="success">
<el-link :underline="false" type="success" @click="changeNotifyConfig(scope.row)">修改</el-link>
</el-tag>
</template>
</el-table-column>
</el-table>
</el-collapse-item>
</el-collapse>
</el-tab-pane>
<el-tab-pane label="消息接收人管理" name="receiver">
<div style="float: right;margin-bottom: 10px">
<el-button plain type="primary" @click="addRecevier">
新增消息接收人
</el-button>
</div>
<el-table
v-loading="loading"
:data="receiver_info_lists"
border
stripe
style="width: 100%">
<el-table-column
align="center"
fixed
label="姓名"
prop="receiver_name"
width="100">
</el-table-column>
<el-table-column
align="center"
label="邮箱"
prop="email"
width="200">
</el-table-column>
<el-table-column
align="center"
label="微信"
prop="domain_name">
<template slot-scope="scope">
<el-popover v-if="scope.row.weixin.openid" placement="top" trigger="hover">
<div style="float: right;margin-top: 10px">
<el-image :src="scope.row.weixin.head_img_url" style="width: 80px;height: 80px"/>
</div>
<p>昵称: {{ scope.row.weixin.nickname }}</p>
<p>性别: {{ scope.row.weixin.sex|sex_filter }}</p>
<p>住址: {{ scope.row.weixin.address }}</p>
<p>微信ID: {{ scope.row.weixin.openid }}</p>
<div slot="reference" class="name-wrapper">
<el-tag>{{ scope.row.weixin.openid }}</el-tag>
</div>
</el-popover>
<el-tag v-else> /</el-tag>
</template>
</el-table-column>
<el-table-column
:formatter="formatter"
align="center"
label="添加时间"
prop="create_time"
width="170">
</el-table-column>
<el-table-column
align="center"
label="备注"
prop="description">
</el-table-column>
<el-table-column
align="center"
label="操作"
width="120">
<template slot-scope="scope">
<!-- <el-button size="small" type="text" @click="editReceiver(scope.row)">编辑</el-button>-->
<el-button size="small" type="text" @click="deleteReceiver(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="阈值设置" name="threshold" style="text-align: center">
<el-tag style="margin: 20px 0"> 低于阈值设置将会出发报警若您配置了消息接收将会将报警信息推送至关联的接收人</el-tag>
<div style="margin: auto;width: 700px;height: 100%">
<el-form ref="form" :model="threshold" label-width="180px">
<el-form-item label="下载次数不足阈值设置">
<el-input-number v-model="threshold.notify_available_downloads" :min="0"
style="width: 300px;margin: 0 20px"></el-input-number>
<el-button @click="notifyThreshold('update')">保存修改</el-button>
</el-form-item>
<el-form-item label="签名次数不足阈值设置">
<el-input-number v-model="threshold.notify_available_signs" :min="0"
style="width: 300px;margin: 0 20px"></el-input-number>
<el-button @click="notifyThreshold('update')">保存修改</el-button>
</el-form-item>
</el-form>
</div>
</el-tab-pane>
</el-tabs>
</el-main>
</template>
<script>
import {notifyConfigInfo, NotifyInfoFun, notifyReceiverInfo, wxBindFun, wxLoginFun} from "@/restful";
import {geetest, getUserInfoFun} from "@/utils";
import {checkEmail, checkphone, format_time, getRandomStr} from "@/utils/base/utils";
export default {
name: "FirUserNotify",
data() {
return {
dialogReceiverVisible: false,
captcha: {"captcha_image": '', "captcha_key": '', "length": 8},
change_type: {email: false, sms: false},
wx_visible: false,
wx_login_qr_url: '',
unique_key: '',
userinfo: {},
addreceiverinfo: {description: '', wxopenid: '', email: '', receiver_name: '', verify_code: '', seicode: ''},
message_type_choices: [],
activeConfig: [],
dialogChangeReceiverVisible: false,
notifyConfigSelection: [],
notifyConfigMsg: {},
title: '',
isedit: false,
activeName: 'config',
receiver_info_lists: [],
loading: false,
threshold: {}
}
}, methods: {
onGetCode() {
this.addreceiverinfo.auth_token = '';
this.addreceiverinfo.seicode = '';
this.onSubmit();
},
onSubmit() {
let email = this.addreceiverinfo.email;
let verify_code = this.addreceiverinfo.verify_code;
let captcha_flag = this.addreceiverinfo.verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true
}
if (captcha_flag) {
let checke = checkEmail(this.addreceiverinfo.email);
let checkp = checkphone(this.addreceiverinfo.email);
if (checke || checkp) {
let params = {
"target": email,
"verify_code": verify_code,
"captcha_key": this.captcha.captcha_key,
"n_type": 'notify',
};
let seicode = this.addreceiverinfo.seicode;
let auth_token = this.addreceiverinfo.auth_token;
if (seicode && seicode.length > 3) {
params['seicode'] = seicode
}
if (auth_token && auth_token.length > 3) {
params['auth_token'] = auth_token
}
if (this.auth_rules.geetest) {
geetest(this, this.addreceiverinfo.email, params, (n_params) => {
this.do_notify_sender(n_params)
}, NotifyInfoFun)
} else {
this.do_notify_sender(params)
}
} else {
this.$message({
message: '邮箱或手机号输入有误',
type: 'error'
});
}
} else {
this.$message({
message: '验证码有误',
type: 'warning'
});
}
},
do_notify_sender(params) {
NotifyInfoFun(data => {
if (data.code === 1000) {
this.$message.success("验证码发送成功")
this.addreceiverinfo.auth_token = data.data.auth_token;
} else {
this.$message.error(data.msg)
}
}, {
'methods': 'POST',
'data': params
})
},
get_auth_code() {
NotifyInfoFun(data => {
if (data.code === 1000) {
this.auth_rules = data.data.auth_rules;
this.captcha = this.auth_rules.captcha;
this.change_type = data.data.change_type;
this.addreceiverinfo.captcha_key = this.captcha.captcha_key;
if (this.addreceiverinfo.verify_code) {
this.addreceiverinfo.verify_code = '';
}
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}, {
"methods": "GET",
"data": {}
});
},
addRecevier() {
this.addreceiverinfo = {description: '', wxopenid: '', email: '', receiver_name: '', verify_code: '', seicode: ''}
this.dialogReceiverVisible = true
this.title = '新增消息接收人'
this.get_auth_code()
},
editReceiver(receiver) {
this.title = '编辑 ' + receiver.receiver_name + ' 消息接收人'
this.isedit = true
this.addreceiverinfo = receiver
this.dialogReceiverVisible = true
},
updateConfig(config = undefined) {
let data = {}
if (config) {
data = {
'config': config.id,
'enable_email': config.enable_email,
'enable_weixin': config.enable_weixin,
'm_type': config.message_type
}
} else {
let receiver_ids = []
for (let receiver of this.notifyConfigSelection) {
receiver_ids.push(receiver.id)
}
data = {'config': this.notifyConfigMsg.id, 'receiver': receiver_ids, 'm_type': this.notifyConfigMsg.m_type}
}
notifyConfigInfo(data => {
if (data.code === 1000) {
this.$message.success('保存成功')
if (!config) {
this.dialogChangeReceiverVisible = false;
}
this.notifyConfigInfoFun();
} else {
this.$message.error('保存失败,' + data.msg);
}
}, {"methods": 'PUT', "data": data});
},
changeNotifyConfig(info) {
let params = {"config": info.id, "message_type": info.m_type}
this.dialogChangeReceiverVisible = true;
notifyReceiverInfo(data => {
if (data.code === 1000) {
this.receiver_info_lists = data.data;
this.notifyConfigMsg = data.config
// eslint-disable-next-line no-unused-vars
this.$nextTick(res => {
data.data.forEach(row => {
for (let sender of data.senders) {
if (sender.sender === row.id) {
this.$refs.multipleTable.toggleRowSelection(row);
}
}
});
})
} else {
this.$message.error('获取失败,' + data);
}
this.loading = false;
}, {"methods": 'GET', "data": params});
},
handleSelectionChange(val) {
this.notifyConfigSelection = val;
},
// eslint-disable-next-line no-unused-vars
handleClick(tab, event) {
this.get_data_from_tabname(tab.name);
},
cancelReceiver() {
this.dialogReceiverVisible = false;
this.addreceiverinfo = {description: '', wxopenid: '', email: '', receiver_name: '', verify_code: '', seicode: ''}
},
saveReceiver() {
notifyReceiverInfo(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
this.dialogReceiverVisible = false;
if (this.dialogChangeReceiverVisible) {
this.changeNotifyConfig(this.notifyConfigMsg)
} else {
this.get_data_from_tabname(this.activeName);
}
} else {
this.$message.error('操作失败,' + data.msg);
}
}, {"methods": 'POST', 'data': this.addreceiverinfo});
},
deleteReceiver(sinfo) {
this.$confirm(`将要删除接收人 ${sinfo.receiver_name}, 是否继续?`, '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
notifyReceiverInfo(data => {
if (data.code === 1000) {
this.$message.success('删除成功');
this.notifyReceiverInfoFun();
}
}, {"methods": 'DELETE', 'data': {'id': sinfo.id}})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
notifyReceiverInfoFun() {
this.loading = true;
notifyReceiverInfo(data => {
if (data.code === 1000) {
this.receiver_info_lists = data.data;
} else {
this.$message.error('获取失败,' + data);
}
this.loading = false;
}, {"methods": 'GET'});
},
setActiveConfig(message_type_choices) {
let activeConfig = []
for (let i of message_type_choices) {
activeConfig.push(i.id)
}
this.activeConfig = activeConfig;
},
notifyConfigInfoFun() {
this.loading = true;
notifyConfigInfo(data => {
if (data.code === 1000) {
this.receiver_info_lists = data.data;
this.message_type_choices = data.message_type_choices;
this.setActiveConfig(this.message_type_choices);
} else {
this.$message.error('获取失败,' + data);
}
this.loading = false;
}, {"methods": 'GET'});
},
notifyThreshold(act) {
notifyConfigInfo(data => {
if (data.code === 1000) {
this.threshold = data.data
if (act !== 'get') {
this.$message.success('保存成功')
}
} else {
this.$message.error('获取失败,' + data);
}
this.loading = false;
}, {"methods": 'POST', 'data': {'act': act, 'threshold': this.threshold}});
},
loop_get_wx_info(wx_login_ticket, c_count = 1, unique_key = getRandomStr()) {
if (wx_login_ticket && wx_login_ticket.length < 3) {
this.$message.error("获取登陆码失败,请稍后再试");
return
}
if (!this.wx_visible) {
return;
}
wxLoginFun(data => {
c_count += 1;
if (c_count > 30) {
return;
}
if (data.code === 1000) {
if (this.userinfo.uid === data.data.uid) {
if (data.data.to_user) {
this.addreceiverinfo.wxopenid = data.data.to_user
}
this.$message.success("绑定成功");
this.wx_visible = false;
}
} else if (data.code === 1005) {
this.$message({
message: data.msg,
type: 'error',
duration: 30000
});
} else if (data.code === 1004) {
this.loop_flag = false;
} else if (data.code === 1006) {
return this.loop_get_wx_info(wx_login_ticket, c_count, unique_key)
}
}, {
"methods": "POST",
data: {"ticket": wx_login_ticket, "unique_key": unique_key}
})
},
wxLogin() {
this.wx_visible = !this.wx_visible;
this.wx_login_qr_url = '';
if (this.wx_visible) {
wxBindFun(data => {
if (data.code === 1000) {
this.wx_login_qr_url = data.data.qr;
this.loop_get_wx_info(data.data.ticket);
} else {
this.$message.error(data.msg);
this.wx_visible = false;
}
}, {
"methods": "POST", "data": {"unique_key": this.unique_key, "w_type": 'notify'}
})
}
},
// eslint-disable-next-line no-unused-vars
formatter(row, column) {
return format_time(row.create_time)
},
// eslint-disable-next-line no-unused-vars
get_data_from_tabname(tabname, data = {}) {
this.$router.push({"name": 'FirUserNotify', params: {act: tabname}});
if (tabname === "config") {
this.notifyConfigInfoFun();
} else if (tabname === "receiver") {
this.notifyReceiverInfoFun();
} else if (tabname === "threshold") {
this.notifyThreshold('get');
}
},
}, mounted() {
getUserInfoFun(this);
this.userinfo = this.$store.state.userinfo;
this.unique_key = getRandomStr();
if (this.$route.params.act) {
let activeName = this.$route.params.act;
let activeName_list = ["config", "receiver", "threshold"];
for (let index in activeName_list) {
if (activeName_list[index] === activeName) {
this.activeName = activeName;
this.get_data_from_tabname(activeName);
return
}
}
}
this.get_data_from_tabname(this.activeName);
}, filters: {
sex_filter: function (x) {
let ret = '未知';
if (x === 1) {
ret = '男'
} else if (x === 2) {
ret = '女'
}
return ret;
},
}, watch: {
'$store.state.userinfo': function () {
this.userinfo = this.$store.state.userinfo;
}
}
}
</script>
<style scoped>
.el-main {
text-align: center;
margin: 20px auto 100px;
width: 1166px;
position: relative;
padding-bottom: 1px;
color: #9b9b9b;
-webkit-font-smoothing: antialiased;
border-radius: 1%;
}
</style>

@ -247,9 +247,10 @@
<script> <script>
import {my_order} from "@/restful"; import {my_order, order_sync} from "@/restful";
import {getUserInfoFun} from '@/utils' import {format_choices, getUserInfoFun} from '@/utils'
import VueQr from 'vue-qr'; import VueQr from 'vue-qr';
import {format_time, getRandomStr} from "@/utils/base/utils";
export default { export default {
name: "FirUserOrders", name: "FirUserOrders",
@ -279,6 +280,7 @@ export default {
show_order_info: false, show_order_info: false,
current_order_info: {}, current_order_info: {},
loading: false, loading: false,
unique_key: ''
} }
}, },
methods: { methods: {
@ -297,17 +299,46 @@ export default {
methods: 'PUT', data: {order_number: order.order_number, act: 'cancel'}, methods: 'PUT', data: {order_number: order.order_number, act: 'cancel'},
}) })
}, },
loop_get_order_info(order_number, c_count = 1, unique_key = getRandomStr()) {
if (!this.wx_pay) {
return
}
this.unique_key = unique_key;
order_sync(data => {
c_count += 1;
if (c_count > 30) {
return;
}
if (data.code === 1000) {
this.$message.success(data.msg);
this.get_data_from_tabname();
this.wx_pay = false;
} else if (data.code === 1004) {
this.loop_flag = false;
if (this.unique_key === unique_key) {
this.wx_pay = false;
}
} else if (data.code === 1001) {
return this.loop_get_order_info(order_number, c_count, unique_key)
}
}, {
"methods": "POST",
data: {"order_number": order_number, 'unique_key': unique_key}
})
},
goto_pay(order) { goto_pay(order) {
this.current_order_info = order; this.current_order_info = order;
my_order(res => { my_order(res => {
if (res.code === 1000) { if (res.code === 1000) {
let data = res.data; let data = res.data;
if (data && data.url) { if (data && data.url) {
if (data && data.type === 'WX') { if (data && data.type === 'WX') {
this.pay_code_url = data.url; this.pay_code_url = data.url;
this.wx_pay = true; this.wx_pay = true;
this.$message.success("请用微信扫描支付"); this.$message.success("请用微信扫描支付");
this.loop_get_order_info(order.order_number)
} else if (data && data.type === 'ALI') { } else if (data && data.type === 'ALI') {
let pay_url = data.url; let pay_url = data.url;
if (pay_url && pay_url.length > 10) { if (pay_url && pay_url.length > 10) {
@ -364,25 +395,16 @@ export default {
this.MyOrderFun(data) this.MyOrderFun(data)
}, },
format_choices(key, obj) {
for (let i = 0; i < obj.length; i++) {
if (key === obj[i].id) {
return obj[i].name
}
}
return "未知"
},
format_payment_type(row) { format_payment_type(row) {
return this.format_choices(row.payment_type, this.payment_type_choices) return format_choices(row.payment_type, this.payment_type_choices)
}, },
format_status_type(row) { format_status_type(row) {
return this.format_choices(row.status, this.status_choices) return format_choices(row.status, this.status_choices)
}, },
format_order_type(row) { format_order_type(row) {
return this.format_choices(row.order_type, this.order_type_choices) return format_choices(row.order_type, this.order_type_choices)
}, },
format_actual_amount(row) { format_actual_amount(row) {
@ -390,23 +412,15 @@ export default {
}, },
format_create_time(row) { format_create_time(row) {
return this.format_time(row.created_time) return format_time(row.created_time)
}, },
format_pay_time(row) { format_pay_time(row) {
return this.format_time(row.pay_time) return format_time(row.pay_time)
}, },
format_cancel_time(row) { format_cancel_time(row) {
return this.format_time(row.cancel_time) return format_time(row.cancel_time)
},
format_time(stime) {
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
}, },
MyOrderFun(params) { MyOrderFun(params) {
this.loading = true; this.loading = true;
@ -440,6 +454,7 @@ export default {
this.order_id_seach = out_trade_no; this.order_id_seach = out_trade_no;
} }
this.get_data_from_tabname(); this.get_data_from_tabname();
this.unique_key = getRandomStr();
}, filters: {} }, filters: {}
} }
</script> </script>

@ -100,7 +100,7 @@
</el-form-item> </el-form-item>
<el-form-item v-if="captcha.change_type.sms" label="手机号码"> <el-form-item v-if="change_type.sms" label="手机号码">
<el-row :gutter="36"> <el-row :gutter="36">
<el-col :span="18"> <el-col :span="18">
<el-input ref="phone" v-model="form.mobile" clearable <el-input ref="phone" v-model="form.mobile" clearable
@ -111,7 +111,7 @@
<el-form-item v-if="captcha.captcha_image" label="图片验证码"> <el-form-item v-if="captcha.captcha_image" label="图片验证码">
<el-row :gutter="11"> <el-row :gutter="11">
<el-col :span="12"> <el-col :span="12">
<el-input v-model="form.authcode" clearable maxlength="6" placeholder="请输入图片验证码"/> <el-input v-model="form.verify_code" clearable maxlength="6" placeholder="请输入图片验证码"/>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-image <el-image
@ -123,7 +123,7 @@
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item v-if="captcha.change_type.sms" label="手机验证码"> <el-form-item v-if="change_type.sms" label="手机验证码">
<el-row :gutter="11"> <el-row :gutter="11">
<el-col :span="12"> <el-col :span="12">
<el-input v-model="form.auth_key" clearable maxlength="6" <el-input v-model="form.auth_key" clearable maxlength="6"
@ -222,7 +222,8 @@
<script> <script>
import {changeInfoFun, getAuthcTokenFun, user_certification} from "@/restful"; import {changeInfoFun, getAuthcTokenFun, user_certification} from "@/restful";
import {AvatarUploadUtils, checkphone, geetest, getUserInfoFun} from "@/utils"; import {AvatarUploadUtils, geetest, getUserInfoFun} from "@/utils";
import {checkphone} from "@/utils/base/utils";
export default { export default {
name: "FirUserProfileCertification", name: "FirUserProfileCertification",
@ -232,10 +233,11 @@ export default {
captcha: { captcha: {
captcha_image: '', captcha_image: '',
captcha_key: '', captcha_key: '',
authcode: '', verify_code: '',
length: 8, length: 8,
change_type: {email: false, sms: false}
}, },
change_type: {email: false, sms: false},
auth_rules: {},
user_certification: {'one': '', 'two': '', 'three': ''}, user_certification: {'one': '', 'two': '', 'three': ''},
certification: {}, certification: {},
certification_status: 0, certification_status: 0,
@ -264,7 +266,7 @@ export default {
this.$message.error("身份证输入不合法"); this.$message.error("身份证输入不合法");
return false; return false;
} }
if (this.captcha.change_type.sms) { if (this.change_type.sms) {
let checkp = checkphone(this.form.mobile); let checkp = checkphone(this.form.mobile);
if (!checkp) { if (!checkp) {
this.$message.error("手机号输入不合法"); this.$message.error("手机号输入不合法");
@ -306,15 +308,15 @@ export default {
getsmsemailcode(act, target) { getsmsemailcode(act, target) {
let picode = { let picode = {
"authcode": this.form.authcode, "verify_code": this.form.verify_code,
"captcha_key": this.captcha.captcha_key, "captcha_key": this.captcha.captcha_key,
}; };
if (this.captcha.captcha_image) { if (this.captcha.captcha_image) {
if (!this.form.authcode) { if (!this.form.verify_code) {
this.$message.error("图片验证码输入有误"); this.$message.error("图片验证码输入有误");
return; return;
} }
let captcha_flag = this.form.authcode.length === this.captcha.length; let captcha_flag = this.form.verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) { if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true captcha_flag = true
} }
@ -331,7 +333,7 @@ export default {
} }
let params = {'act': act, 'target': target, 'ext': picode, 'user_id': target, 'ftype': 'certification'}; let params = {'act': act, 'target': target, 'ext': picode, 'user_id': target, 'ftype': 'certification'};
if (this.captcha.geetest) { if (this.auth_rules.geetest) {
geetest(this, this.form.mobile, params, (n_params) => { geetest(this, this.form.mobile, params, (n_params) => {
this.get_phone_code(n_params); this.get_phone_code(n_params);
}) })
@ -359,12 +361,14 @@ export default {
}, },
get_auth_code() { get_auth_code() {
if (this.form.authcode) { if (this.form.verify_code) {
this.form.authcode = ''; this.form.verify_code = '';
} }
changeInfoFun(data => { changeInfoFun(data => {
if (data.code === 1000) { if (data.code === 1000) {
this.captcha = data.data; this.change_type = data.data.change_type;
this.auth_rules = data.data.auth_rules;
this.captcha = this.auth_rules.captcha
if (this.captcha.captcha_key) { if (this.captcha.captcha_key) {
this.form.captcha_key = this.captcha.captcha_key; this.form.captcha_key = this.captcha.captcha_key;
} }

@ -11,6 +11,22 @@
微信授权登录用户信息 微信授权登录用户信息
</span> </span>
<el-tag style="margin-bottom: 10px">授权用户将可以使用微信扫码直接登录</el-tag> <el-tag style="margin-bottom: 10px">授权用户将可以使用微信扫码直接登录</el-tag>
<el-button plain style="float: right;margin-top: 10px" @click="wx_web_login_fun"> 点击扫码同步微信用户信息</el-button>
<el-dialog
:visible.sync="show_web_visible"
append-to-body
center
title="手机微信扫码更新个人信息"
width="360px">
<vue-qr ref="qr"
:correctLevel="qrinfo.correctLevel"
:logoCornerRadius="qrinfo.logoCornerRadius"
:logoScale="qrinfo.logoScale"
:margin="qrinfo.margin"
:size="300" :text="wx_web_login_url"
class="code-wrap">
</vue-qr>
</el-dialog>
<el-table <el-table
:data="wx_user_list" :data="wx_user_list"
border border
@ -104,10 +120,17 @@
</el-form-item> </el-form-item>
<el-form-item label="用户UID">
<el-row :gutter="36">
<el-col :span="18">
<el-input :value="userinfo.uid" disabled/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="手机号码"> <el-form-item label="手机号码">
<el-row :gutter="36"> <el-row :gutter="36">
<el-col :span="16"> <el-col :span="16">
<el-input ref="phone" v-model="userinfo.mobile" :disabled="!captcha.change_type.sms" <el-input ref="phone" v-model="userinfo.mobile" :disabled="!change_type.sms"
:readonly="editphone !== true" clearable maxlength="11" placeholder="手机" :readonly="editphone !== true" clearable maxlength="11" placeholder="手机"
prefix-icon="el-icon-mobile"/> prefix-icon="el-icon-mobile"/>
</el-col> </el-col>
@ -115,7 +138,7 @@
<el-button icon="el-icon-edit" @click="changePhoneValue"> <el-button icon="el-icon-edit" @click="changePhoneValue">
</el-button> </el-button>
</el-col> </el-col>
<el-col v-if="editphone === true && captcha.change_type.sms" :span="5"> <el-col v-if="editphone === true && change_type.sms" :span="5">
<el-button class="save-button" plain type="success" <el-button class="save-button" plain type="success"
@click="savePhone"> @click="savePhone">
保存 保存
@ -124,11 +147,11 @@
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item v-if="editphone === true && captcha.captcha_image && captcha.change_type.sms" label="图片验证码" <el-form-item v-if="editphone === true && captcha.captcha_image && change_type.sms" label="图片验证码"
style="height: 40px"> style="height: 40px">
<el-row :gutter="36" style="height: 40px"> <el-row :gutter="36" style="height: 40px">
<el-col :span="14"> <el-col :span="14">
<el-input v-model="userinfo.authcode" clearable maxlength="6" placeholder="请输入图片验证码"/> <el-input v-model="userinfo.verify_code" clearable maxlength="6" placeholder="请输入图片验证码"/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-image <el-image
@ -140,7 +163,7 @@
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item v-if="editphone === true && captcha.change_type.sms" label="手机验证码"> <el-form-item v-if="editphone === true && change_type.sms" label="手机验证码">
<el-row :gutter="36"> <el-row :gutter="36">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="userinfo.auth_key" clearable maxlength="6" <el-input v-model="userinfo.auth_key" clearable maxlength="6"
@ -160,7 +183,7 @@
<el-form-item label="邮箱地址"> <el-form-item label="邮箱地址">
<el-row :gutter="36"> <el-row :gutter="36">
<el-col :span="16"> <el-col :span="16">
<el-input ref="email" v-model="userinfo.email" :disabled="!captcha.change_type.email" <el-input ref="email" v-model="userinfo.email" :disabled="!change_type.email"
:readonly="editemail !== true" clearable maxlength="20" placeholder="邮箱" :readonly="editemail !== true" clearable maxlength="20" placeholder="邮箱"
prefix-icon="el-icon-bank-card"/> prefix-icon="el-icon-bank-card"/>
</el-col> </el-col>
@ -168,7 +191,7 @@
<el-button icon="el-icon-edit" @click="changeemailValue"> <el-button icon="el-icon-edit" @click="changeemailValue">
</el-button> </el-button>
</el-col> </el-col>
<el-col v-if="editemail === true && captcha.change_type.email" :span="5"> <el-col v-if="editemail === true && change_type.email" :span="5">
<el-button class="save-button" plain type="success" <el-button class="save-button" plain type="success"
@click="saveemail"> @click="saveemail">
保存 保存
@ -178,11 +201,11 @@
</el-form-item> </el-form-item>
<el-form-item v-if="editemail === true && captcha.captcha_image && captcha.change_type.email" label="图片验证码" <el-form-item v-if="editemail === true && captcha.captcha_image && change_type.email" label="图片验证码"
style="height: 40px"> style="height: 40px">
<el-row :gutter="36" style="height: 40px"> <el-row :gutter="36" style="height: 40px">
<el-col :span="14"> <el-col :span="14">
<el-input v-model="userinfo.authcode" clearable maxlength="6" placeholder="请输入图片验证码"/> <el-input v-model="userinfo.verify_code" clearable maxlength="6" placeholder="请输入图片验证码"/>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-image <el-image
@ -194,7 +217,7 @@
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item v-if="editemail === true && captcha.change_type.email" label="邮箱验证码"> <el-form-item v-if="editemail === true && change_type.email" label="邮箱验证码">
<el-row :gutter="36"> <el-row :gutter="36">
<el-col :span="16"> <el-col :span="16">
<el-input v-model="userinfo.auth_key" clearable maxlength="6" <el-input v-model="userinfo.auth_key" clearable maxlength="6"
@ -279,16 +302,21 @@
</template> </template>
<script> <script>
import {changeInfoFun, getAuthcTokenFun, userinfos, wxLoginFun, wxutils} from '@/restful' import {changeInfoFun, getAuthcTokenFun, userinfos, wxBindFun, wxLoginFun, wxutils, wxWebScanFun} from '@/restful'
import {deepCopy, geetest} from "@/utils"; import {deepCopy, geetest} from "@/utils";
import VueQr from 'vue-qr';
import {getRandomStr} from "@/utils/base/utils";
export default { export default {
name: "FirUserProfileInfo", name: "FirUserProfileInfo",
components: {
VueQr
},
data() { data() {
return { return {
userinfo: { userinfo: {
srccode: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg', srccode: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
authcode: '' verify_code: ''
}, },
orguserinfo: {}, orguserinfo: {},
editphone: false, editphone: false,
@ -296,28 +324,89 @@ export default {
editdomain_name: false, editdomain_name: false,
edituser_name: false, edituser_name: false,
editposition: false, editposition: false,
captcha: {"captcha_image": '', "captcha_key": '', "length": 8, change_type: {email: false, sms: false}}, captcha: {"captcha_image": '', "captcha_key": '', "length": 8},
change_type: {email: false, sms: false},
auth_rules: {},
form: {}, form: {},
wx_login_qr_url: '', wx_login_qr_url: '',
wx_visible: false, wx_visible: false,
loop_flag: false,
show_wx_visible: false, show_wx_visible: false,
show_web_visible: false,
qrinfo: {
logoScale: 0.3,
logoCornerRadius: 12,
correctLevel: 3,
margin: 20
},
wx_web_login_url: '',
wx_user_list: [], wx_user_list: [],
pagination: {"currentPage": 1, "total": 0, "pagesize": 999}, pagination: {"currentPage": 1, "total": 0, "pagesize": 999},
unique_key: ''
} }
}, methods: { }, methods: {
loop_get_wx_web_info(wx_login_ticket, c_count = 1, unique_key = getRandomStr()) {
if (wx_login_ticket && wx_login_ticket.length < 3) {
this.$message.error("获取登陆码失败,请稍后再试");
return
}
if (!this.show_web_visible) {
return;
}
wxLoginFun(data => {
c_count += 1;
if (c_count > 30) {
return;
}
if (data.code === 1000) {
if (this.userinfo.uid === data.data.uid) {
this.$message.success("更新成功");
this.show_web_visible = false;
this.get_wx_user_list();
}
} else if (data.code === 1006) {
return this.loop_get_wx_web_info(wx_login_ticket, c_count, unique_key)
} else {
this.$message({
message: data.msg,
type: 'error',
duration: 30000
});
this.show_web_visible = false;
}
}, {
"methods": "POST",
data: {"ticket": wx_login_ticket, "unique_key": unique_key}
})
},
wx_web_login_fun() {
wxWebScanFun(data => {
if (data.code === 1000) {
this.show_web_visible = true;
this.wx_web_login_url = data.data;
this.loop_get_wx_web_info(data.ticket)
} else {
this.$message.error("信息获取失败")
}
}, {methods: 'GET'})
},
delete_wx_u(wx_user_info) { delete_wx_u(wx_user_info) {
this.$confirm(`此操作将导致微信用户 “${wx_user_info.nickname}” 无法通过扫码登录, 是否继续删除?`, '提示', { this.$prompt(`此操作将导致微信用户 “${wx_user_info.nickname}” 无法通过扫码登录, 是否继续删除?`, '警告', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' inputPlaceholder: '该用户的登录密码',
}).then(() => { inputValidator: this.validFun
}).then(({value}) => {
this.wxUtilsFun({ this.wxUtilsFun({
methods: 'DELETE', methods: 'POST',
data: { data: {
"size": this.pagination.pagesize, "size": this.pagination.pagesize,
"page": this.pagination.currentPage, "page": this.pagination.currentPage,
"openid": wx_user_info.openid, "openid": wx_user_info.openid,
"confirm_pwd": value
} }
}) })
}).catch(() => { }).catch(() => {
@ -332,6 +421,8 @@ export default {
if (data.code === 1000) { if (data.code === 1000) {
this.wx_user_list = data.data; this.wx_user_list = data.data;
this.show_wx_visible = true this.show_wx_visible = true
} else if (data.code === 1001) {
this.$message.error(data.msg)
} else { } else {
this.$message.error("获取授权列表失败") this.$message.error("获取授权列表失败")
} }
@ -346,72 +437,66 @@ export default {
} }
}) })
}, },
loop_get_wx_info(wx_login_ticket) { loop_get_wx_info(wx_login_ticket, c_count = 1, unique_key = getRandomStr()) {
if (wx_login_ticket && wx_login_ticket.length < 3) { if (wx_login_ticket && wx_login_ticket.length < 3) {
this.$message.error("获取登陆码失败,请稍后再试"); this.$message.error("获取登陆码失败,请稍后再试");
return return
} }
let c_count = 1; if (!this.wx_visible) {
// eslint-disable-next-line no-unused-vars return;
const loop_t = window.setInterval(res => { }
if (!this.loop_flag) { wxLoginFun(data => {
window.clearInterval(loop_t); c_count += 1;
if (c_count > 30) {
return;
} }
wxLoginFun(data => { if (data.code === 1000) {
c_count += 1; if (this.userinfo.uid === data.data.uid) {
if (c_count > 120) { this.$message.success("绑定成功");
window.clearInterval(loop_t); this.wx_visible = false;
}
if (data.code === 1000) {
window.clearInterval(loop_t);
if (this.userinfo.uid === data.userinfo.uid) {
this.$message.success("绑定成功");
this.wx_visible = false;
this.loop_flag = false;
}
} else if (data.code === 1005) {
window.clearInterval(loop_t);
this.$message({
message: data.msg,
type: 'error',
duration: 30000
});
} }
}, { } else if (data.code === 1005) {
"methods": "POST", this.$message({
data: {"ticket": wx_login_ticket} message: data.msg,
}) type: 'error',
}, 3000) duration: 30000
});
} else if (data.code === 1004) {
this.loop_flag = false;
} else if (data.code === 1006) {
return this.loop_get_wx_info(wx_login_ticket, c_count, unique_key)
}
}, {
"methods": "POST",
data: {"ticket": wx_login_ticket, "unique_key": unique_key}
})
}, },
wxLogin() { wxLogin() {
this.wx_visible = !this.wx_visible; this.wx_visible = !this.wx_visible;
this.wx_login_qr_url = ''; this.wx_login_qr_url = '';
if (this.wx_visible) { if (this.wx_visible) {
userinfos(data => { wxBindFun(data => {
if (data.code === 1000) { if (data.code === 1000) {
this.wx_login_qr_url = data.data.qr; this.wx_login_qr_url = data.data.qr;
this.loop_flag = true;
this.loop_get_wx_info(data.data.ticket); this.loop_get_wx_info(data.data.ticket);
} else { } else {
this.$message.error(data.msg); this.$message.error(data.msg);
this.wx_visible = false; this.wx_visible = false;
this.loop_flag = false;
} }
}, { }, {
"methods": "POST", "methods": "POST", "data": {"unique_key": this.unique_key, "w_type": 'login'}
}) })
} else {
this.loop_flag = false;
} }
}, },
get_auth_code() { get_auth_code() {
changeInfoFun(data => { changeInfoFun(data => {
if (data.code === 1000) { if (data.code === 1000) {
this.captcha = data.data; this.auth_rules = data.data.auth_rules;
this.captcha = this.auth_rules.captcha;
this.change_type = data.data.change_type;
this.userinfo.captcha_key = this.captcha.captcha_key; this.userinfo.captcha_key = this.captcha.captcha_key;
if (this.userinfo.authcode) { if (this.userinfo.verify_code) {
this.userinfo.authcode = ''; this.userinfo.verify_code = '';
} }
} else { } else {
this.$message({ this.$message({
@ -446,7 +531,7 @@ export default {
auth_token: this.userinfo.auth_token, auth_token: this.userinfo.auth_token,
auth_key: this.userinfo.auth_key, auth_key: this.userinfo.auth_key,
captcha_key: this.userinfo.captcha_key, captcha_key: this.userinfo.captcha_key,
authcode: this.userinfo.authcode, verify_code: this.userinfo.verify_code,
}; };
}, },
saveemail() { saveemail() {
@ -553,14 +638,14 @@ export default {
}, },
getsmsemailcode(act, target) { getsmsemailcode(act, target) {
let picode = { let picode = {
"authcode": this.userinfo.authcode, "verify_code": this.userinfo.verify_code,
"captcha_key": this.captcha.captcha_key, "captcha_key": this.captcha.captcha_key,
}; };
let params = { let params = {
'act': act, 'target': target, 'ext': picode 'act': act, 'target': target, 'ext': picode
}; };
if (this.captcha.geetest) { if (this.auth_rules.geetest) {
geetest(this, this.form.email, target, params, (n_params) => { geetest(this, target, params, (n_params) => {
this.do_get_auth_token(n_params); this.do_get_auth_token(n_params);
}) })
} else { } else {
@ -572,7 +657,7 @@ export default {
// this.updateUserInfo({"methods":false}); // this.updateUserInfo({"methods":false});
this.userinfo = this.$store.state.userinfo; this.userinfo = this.$store.state.userinfo;
this.orguserinfo = deepCopy(this.$store.state.userinfo); this.orguserinfo = deepCopy(this.$store.state.userinfo);
this.unique_key = getRandomStr();
}, watch: { }, watch: {
'$store.state.userinfo': function () { '$store.state.userinfo': function () {

@ -0,0 +1,317 @@
<template>
<el-main>
<el-row style="margin-bottom: 5px">
<el-col :span="3">
<el-radio-group v-model="searchfromtype">
<el-radio-button icon="el-icon-mobile-phone" label="android"><i
class="iconfont icon-android2"/>
</el-radio-button>
<el-radio-button label="ios"><i class="iconfont icon-ios"/>
</el-radio-button>
</el-radio-group>
</el-col>
<el-col :span="8">
<el-row>
<el-col :span="18">
<el-input
v-model="keysearch"
clearable
placeholder="请输入名称搜索"
@click="searchapps"
@keyup.enter.native="searchapps">
</el-input>
</el-col>
<el-col :span="6">
<el-button icon="el-icon-search" @click="searchFun">
</el-button>
</el-col>
</el-row>
</el-col>
<el-col :offset="5" :span="8">
<el-row>
<el-col :span="8">
<el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" style="margin-top: 10px"
@change="handleCheckAllChange">当前页全选{{ checkedQrcodes.length }}
</el-checkbox>
</el-col>
<el-col :span="13" style="float: right">
<el-button @click="savemany">批量保存选中下载码到本地</el-button>
</el-col>
</el-row>
</el-col>
</el-row>
<el-row id="showqr">
<el-checkbox-group v-model="checkedQrcodes" @change="handlecheckedQrcodesChange">
<el-col v-for="appinfo in qrcode_info_list" :key="appinfo.app_id" :span="5" style="margin-left: 40px">
<el-checkbox :label="appinfo.app_id" border style="height: 320px">
<el-card :body-style="{textAlign:'center',padding:'8px'}" shadow="hover">
<vue-qr :callback="qrback"
:correctLevel="qrinfo.correctLevel" :logoCornerRadius="qrinfo.logoCornerRadius"
:logoScale="qrinfo.logoScale"
:logoSrc="appinfo.master_release.icon_url"
:margin="qrinfo.margin" :qid="appinfo.app_id"
:size="200"
:text="appinfo.preview_url">
</vue-qr>
<div style="margin: 5px 0 5px">
<i v-if="appinfo.type === 1" class=" type-icon iconfont icon-ios"/>
<i v-if="appinfo.type === 0" class="type-icon iconfont icon-android2"/>
&nbsp;
<span>{{ appinfo.name }}</span>
<div class="bottom clearfix">
<el-popover placement="top" trigger="hover">
<span v-clipboard:copy="appinfo.preview_url"
v-clipboard:success="copy_success"
>{{ appinfo.preview_url }}</span>
<div slot="reference" class="name-wrapper">
<el-button plain size="small" type="primary" @click="go_download(appinfo)">预览</el-button>
<el-button plain size="small" type="primary" @click="save_qr(appinfo)">保存本地</el-button>
</div>
</el-popover>
</div>
</div>
</el-card>
</el-checkbox>
</el-col>
</el-checkbox-group>
</el-row>
</el-main>
</template>
<script>
import {qrcodeinfo} from "@/restful";
import {getScrollHeight, getScrollTop, getUserInfoFun, getWindowHeight} from '@/utils'
import VueQr from 'vue-qr';
export default {
name: "FirUserQrcode",
components: {
VueQr
},
data() {
return {
has_next: false,
firstloadflag: true,
query: {'page': 1, size: 20},
searchflag: false,
keysearch: '',
searchfromtype: '',
loadingobj: null,
checkAll: false,
checkedQrcodes: [],
isIndeterminate: false,
allQrcodeAppid: [],
qrcode_info_list: [],
orgqrcode_info_list: [],
qrcode_img_info: {},
qrinfo: {
logoScale: 0.3,
logoCornerRadius: 12,
correctLevel: 3,
margin: 10
},
}
},
methods: {
copy_success() {
this.$message.success('复制剪切板成功');
},
auto_load() {
if (getScrollTop() + getWindowHeight() >= getScrollHeight()) {
if (this.has_next) { //
if (this.autoloadflag) {
this.autoloadflag = false;
if (this.qrcode_info_list.length === 0) {
this.query.page = 1;
} else {
this.query.page += 1;
}
if (this.searchfromtype !== '') {
this.query.type = this.searchfromtype;
}
this.UserQrcodeFun(this.query);
}
}
}
},
init_value() {
this.checkAll = false;
this.checkedQrcodes = [];
this.isIndeterminate = false;
this.qrcode_info_list = [];
this.orgqrcode_info_list = [];
this.checkedQrcodes = [];
this.allQrcodeAppid = [];
this.qrcode_img_info = {};
},
searchFun() {
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
if (keysearch === '') {
this.searchflag = false;
this.init_value();
this.query.page = 1;
if (this.searchfromtype) {
this.UserQrcodeFun({"type": this.searchfromtype});
} else {
this.UserQrcodeFun({});
}
} else {
this.searchflag = true
}
if (this.searchflag) {
this.qrcode_info_list = [];
this.orgqrcode_info_list = [];
if (this.searchfromtype) {
this.UserQrcodeFun({"type": this.searchfromtype, 'page': 1, size: 999});
} else {
this.UserQrcodeFun({'page': 1, size: 999});
}
}
},
searchapps() {
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
let newqrcode_info_list = [];
for (let i = 0; i < this.orgqrcode_info_list.length; i++) {
if (this.orgqrcode_info_list[i].name.search(keysearch) >= 0) {
newqrcode_info_list.push(this.orgqrcode_info_list[i]);
}
}
if (keysearch === "") {
this.qrcode_info_list = this.orgqrcode_info_list.slice();
} else {
this.qrcode_info_list = newqrcode_info_list.slice();
}
},
get_appinfo_from_appid(app_id) {
for (let i = 0; i < this.qrcode_info_list.length; i++) {
if (app_id === this.qrcode_info_list[i].app_id) {
return this.qrcode_info_list[i];
}
}
},
savemany() {
for (let i = 0; i < this.checkedQrcodes.length; i++) {
this.save_qr(this.get_appinfo_from_appid(this.allQrcodeAppid[i]))
}
},
handleCheckAllChange(val) {
this.checkedQrcodes = val ? this.allQrcodeAppid : [];
this.isIndeterminate = false;
},
handlecheckedQrcodesChange(value) {
let checkedCount = value.length;
this.checkAll = checkedCount === this.allQrcodeAppid.length;
this.isIndeterminate = checkedCount > 0 && checkedCount < this.allQrcodeAppid.length;
},
go_download(appinfo) {
window.open(appinfo.preview_url, '_blank', '');
},
qrback(dataUrl, id) {
this.qrcode_img_info[id] = dataUrl;
this.allQrcodeAppid.push(id);
},
save_qr(appinfo) {
let dtype = "I";
if (appinfo.master_release.release_type === 0) {
dtype = "A";
}
let a = document.createElement('a');
//
a.download = appinfo.name + '_' + dtype + "_下载码";
a.href = this.qrcode_img_info[appinfo.app_id];
//
a.dispatchEvent(new MouseEvent('click'))
},
UserQrcodeFun(params) {
this.loadingobj = this.$loading({
lock: true,
text: '加载中',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
qrcodeinfo(data => {
if (data.code === 1000) {
if (this.firstloadflag) {
window.addEventListener('scroll', this.auto_load);
this.firstloadflag = false
}
this.autoloadflag = true;
this.qrcode_info_list = this.qrcode_info_list.concat(data.data);
this.has_next = data.has_next;
this.orgqrcode_info_list = this.qrcode_info_list.slice(); //
this.hdata = data.hdata;
this.searchapps();
} else {
this.$message.error("信息获取失败")
}
this.loadingobj.close();
}, {methods: 'GET', data: params})
},
}, mounted() {
getUserInfoFun(this);
this.UserQrcodeFun()
},
watch: {
// eslint-disable-next-line no-unused-vars
keysearch: function (val, oldVal) {
// this.searchapps()
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
if (keysearch === "") {
this.searchFun()
}
},
// eslint-disable-next-line no-unused-vars
searchfromtype: function (val, oldVal) {
this.qrcode_info_list = [];
this.query.page = 1;
// this.keysearch='';
this.searchFun();
// this.getappsFun({"type": this.searchfromtype});
},
}
}
</script>
<style scoped>
.el-main {
margin: 20px auto 100px;
width: 1166px;
position: relative;
padding-bottom: 1px;
color: #9b9b9b;
-webkit-font-smoothing: antialiased;
border-radius: 1%;
}
.bottom {
margin-top: 10px;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
#showqr /deep/ .el-checkbox__input {
display: none;
}
</style>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save