Compare commits

...

428 Commits

Author SHA1 Message Date
isummer e12779d654 修复容器部署失败问题,重新编写本地部署文档 2 years ago
isummer ac590563a7 修复超级签共享用户下载文件失败问题 2 years ago
isummer 8fc1d353c9 管理后台增加重置密码功能 2 years ago
isummer 35b64d74de 修复flyapps容器构建失败问题,优化部署文档 2 years ago
isummer a064988f56 修复flyapps容器构建失败问题,优化部署文档 2 years ago
isummer adff361c11 修复非苹果设备导致余额不足问题 2 years ago
isummer 419e39eaaa 修复非苹果设备导致余额不足问题 2 years ago
isummer 9d9cb3f2cb Merge remote-tracking branch 'origin/master' 2 years ago
isummer dd802dccbb 优化代码 2 years ago
isummer 1262bff671 优化代码 2 years ago
isummer 289737ed00 修复异常,优化代码 2 years ago
isummer dc7b491534 修复新苹果,新应用无法设置专属签名问题 2 years ago
isummer eed0f5194c 增加苹果设备类型区分,优化苹果设备注册量及展示 2 years ago
isummer e0599585be 修复前端应用超级签名展示异常问题 2 years ago
isummer 42451f2cb0 修复超级签权限问题 2 years ago
isummer 646162593e 增加打包时间 2 years ago
isummer 200494fcf3 增加打包时间 2 years ago
isummer fe5a7605f0 增加打包时间 2 years ago
isummer f70351d8e5 修复容器启动异常问题 2 years ago
isummer 9e2f40383a 优化代码,增加邀请码配置,修改极验弹窗模式 2 years ago
isummer debe95791e 优化容器部署 2 years ago
isummer a3887825c9 更新依赖包,支持python39环境 2 years ago
isummer 8a8f9ef7fe 升级python环境为python39 2 years ago
isummer 8e325e6cde 优化容器启停异常 2 years ago
isummer 92961cfa61 flyapps-cli增加容器访问 2 years ago
isummer 8c25aab63c flyapps-cli增加容器访问 2 years ago
isummer 0cc5084390 修复授权绑定设备添加重复问题 2 years ago
isummer 4d98981485 容器部署支持管理后台访问 2 years ago
isummer a7a1472533 苹果应用授权码支持添加绑定设备 2 years ago
isummer 332d12bddf 容器部署优化 2 years ago
isummer 6439824468 优化docker部署方式 2 years ago
isummer 42478f21d9 1.修复授权码为一次,超级签苹果包下载失败问题 2 years ago
isummer 2c67305f8a 增加通过命令给用户充值次数 2 years ago
isummer 79037d5248 修复应用上传超级签检测问题 2 years ago
isummer 525ec097d7 更新readme 2 years ago
isummer a35ca0b087 更新配置文件信息 2 years ago
isummer d9ac93ccc1 no message 2 years ago
isummer 1ff3da6896 修复管理页面预览展示 2 years ago
isummer cbad06e95d 应用时间更新修复 2 years ago
isummer b093d7133b 页面底部信息配置迁移 2 years ago
isummer 7aa0471ba9 增加应用当天下载次数展示 2 years ago
isummer 2224ce27c7 优化代码 2 years ago
isummer d8b124c1a7 用户下载页私有配置功能 2 years ago
isummer d1a5d69ac8 增加客户端独立部署功能 2 years ago
isummer 8a8d2f2400 增加预览下载页路由模式 2 years ago
isummer 49832ca702 修复新注册用户 默认域名绑定问题 2 years ago
isummer 7075bc7ed6 修改存储添加失败问题 2 years ago
MMXX 7d70f381a1 geetest 增加代理访问 2 years ago
MMXX 15a506450c 增加管理后台应用当天下载次数展示 2 years ago
isummer 93c13fa776 日志增加function名称 2 years ago
MMXX c8f02465d0 修复异常注册问题 2 years ago
isummer 2e240735dc 优化下载次数展示 2 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逻辑 3 years ago
MMXX 878183dbce 修改日志存储时间 3 years ago
MMXX bebc8417eb 优化应用域名展示 3 years ago
MMXX de511bf90c 状态展示优化 3 years ago
MMXX a8c1df58ee 优化代码,增加设备删除控制,增加设备被封状态 3 years ago
MMXX ef335e118a 优化代码 3 years ago
MMXX 03f66df7b9 优化代码,修复多个开发者,下载不是最新签名包问题,增加批量修改开发者状态 3 years ago
MMXX 4d4ee64a65 优化显示异常提示 3 years ago
MMXX 04ccfe77a3 优化代码,增加苹果账户备注查询功能 3 years ago
MMXX d085d86a26 苹果超级签名优化 3 years ago
MMXX 8a60be6f4f 优化代码 3 years ago
MMXX 869d5ee7bb 优化代码 3 years ago
MMXX 9572382a9e 优化代码 3 years ago
MMXX 7ad5bc8161 优化签名代码逻辑 3 years ago
MMXX 59d7d905d3 优化代码 3 years ago
MMXX c361d740bf 优化代码 3 years ago
MMXX 17c1f0612a 优化缓存代码 3 years ago
MMXX 2b5325ff32 优化缓存代码 3 years ago
MMXX 09257733f8 更新苹果api接口 3 years ago
MMXX 5dd1dd0681 后端更新下载码逻辑 3 years ago
MMXX 979b6f0386 更新前端页面 3 years ago
MMXX e8e6ea962a 修复已知问题 3 years ago
MMXX c50005e7c7 修复激活问题 3 years ago
MMXX 465aea9744 修复页面udid展示不全问题 3 years ago
MMXX 7c2a29b86d 优化代码 3 years ago
MMXX f8418f6414 增加签名应用限额 3 years ago
youngS def6c9dfc8 优化代码 3 years ago
youngS 205505cb0e 优化超级签 获取开发者账户逻辑,增加微信删除必须输入密码限制 3 years ago
youngS 106ae636a2 优化代码 3 years ago
youngS 2e96183c06 修复展示问题 3 years ago
youngS 216bc367c4 优化代码 3 years ago
youngS e559267de3 增加苹果开发者备注显示信息 3 years ago
youngS f04c4411c7 修复已知问题,修正 专属账户,其他应用禁止使用 3 years ago
youngS a60a7d6011 优化代码 3 years ago
youngS d3fc7327a6 增加应用专属开发者功能 3 years ago
youngS 4decc14bf8 优化代码 3 years ago
nineven d657c709f6 优化代码 3 years ago
nineven 73d783b97d 优化代码 3 years ago
nineven b18dfa7a14 优化代码 3 years ago
youngS 0d26e5ac7b 优化代码 3 years ago
youngS bda9cf994d 优化代码 3 years ago
youngS 9d0d36ca63 优化api请求时间 3 years ago
youngS 26e6efeaf4 优化代码,增加苹果api访问代理 3 years ago
youngS 49c35275b6 修复设备消耗查询问题 3 years ago
youngS 92ba4fc258 增加下载码展示 3 years ago
youngS c8a69acb84 增加下载码展示 3 years ago
youngS 7932dde3b4 修复bug 3 years ago
youngS 55f5f65e15 修复已知问题,增加设备消化统计 3 years ago
youngS fd43dcfea4 优化代码 3 years ago
youngS 4c41efa46f 修改 查询开发者策略 按照最小注册设备数进行查找 3 years ago
youngS d3e0c53b8f 修复数据存储异常问题 3 years ago
youngS bd77da66b7 修复自定义id名 3 years ago
youngS ebed9b1aa4 修复只有一个证书激活失败的问题 3 years ago
youngS 0c5c4a6ce7 修复异常问题 3 years ago
youngS b3899d4a97 修复异常问题 3 years ago
youngS 27b8a21556 数据库优化 3 years ago
youngS bbf9664fc7 修复异常问题 3 years ago
youngS 86467ef2fd 优化展示 3 years ago
youngS 2aedb458ee 优化前端展示 3 years ago
youngS 2e491db16c 代码bug修复 3 years ago
youngS 8a59b8d329 代码bug修复 3 years ago
youngS fce8a09d5e 优化代码 3 years ago
fxC 29328d293b 修复mac环境下启动服务失败 3 years ago
fxC 2ec7fb05eb 修复已知问题 3 years ago
youngS 341cf21367 签名优化 3 years ago
youngS 6412e36d0b 优化代码 3 years ago
youngS 8bf925e9df 优化代码 3 years ago
youngS d550d20998 管理后台增加超级签充值功能,前端页面增加udid展示 3 years ago
youngS c170086609 签名余额限制,开发者账户加锁,修复账单数量统计错误问题 3 years ago
youngS bd8c8d00cc 超级签名并发优化,签名余额还未限制,下个版本限制 3 years ago
youngS a2c5d1bd3f 超级签名并发优化,签名余额还未限制,下个版本限制 3 years ago
youngS 169347ca16 下载页举报按钮优化 3 years ago
youngS 372455bcb3 下载页举报按钮优化 3 years ago
youngS d943b5527d 修复已知问题 3 years ago
youngS 5316ed83c1 优化日志保存记录 3 years ago
youngS c173262a0b 修复微信绑定bug 3 years ago
fxC 5be7115e91 兼容老API接口 3 years ago
youngS f30aafb5d9 优化代码 3 years ago
fxC 34971b792a fix some bug 3 years ago
youngS f49c067015 前端代码格式化 3 years ago
youngS 002c90d5e3 优化代码,修改已知问题 3 years ago
youngS 4c29d32d67 下载页面增加举报功能 3 years ago
youngS 819c9a7017 管理后台增加举报信息 3 years ago
youngS 479abff102 下载页面增加举报功能 3 years ago
youngS 20ac8eadda 优化代码 3 years ago
youngS 1cc05645ec 优化代码 3 years ago
youngS 2ee4352544 证书导出功能优化 3 years ago
youngS 7702fa5209 开发者keyid和存储secert加密存储处理 3 years ago
youngS 50da6044a4 增加设备消化账单 3 years ago
youngS 7d781de388 优化代码 3 years ago
youngS ab52639caf 优化代码 3 years ago
youngS 5232ba5fe6 优化代码 3 years ago
youngS 47861511fc 优化单用户签名流程 3 years ago
fxC 0a20226dd7 reformat code and import 3 years ago
fxC cb4fc0b827 no message 3 years ago
youngS 674bf66827 共有池 3 years ago
fxC abd2db398d 一些优化 3 years ago
youngS 9d3b5c582e 共有池 3 years ago
youngS abbe709d27 共有池 3 years ago
fxC b3637c6844 签名共有池实现 3 years ago
youngS 9b7590146a 优化下载页跳转 3 years ago
youngS 7309eaf9b5 修复绑定异常问题 3 years ago
youngS edd603b8e9 下载域名权限问题 3 years ago
youngS 9b69021337 优化cli.py 上传脚本 3 years ago
youngS e7d4df5941 修复新应用短连接重复异常上传问题 3 years ago
youngS b772d2921e 优化代码下载 3 years ago
youngS dd5d0ed0ed 优化代码下载 3 years ago
youngS dc2fa091cb 增加自定义广告功能 3 years ago
youngS d34073301b 增加自定义广告功能 3 years ago
youngS fac7eccc05 优化代码下载 3 years ago
youngS 4533e04850 修复域名下载问题 3 years ago
youngS 0e2578738f 优化docker部署 3 years ago
youngS 08c6b7b87f 优化代码 3 years ago
youngS 7c5ab20dd9 优化uwsgi 启动服务 3 years ago
youngS 1a9cae8cb7 优化uwsgi 启动服务 3 years ago
youngS e34ca43cfc 签名逻辑优化 3 years ago
youngS 75d5a5df76 增加状态描述文件下载状态 3 years ago
youngS ac8614b54c 服务启动方式被manage.py托管 3 years ago
youngS 1e1385da84 服务启动方式被manage.py托管 3 years ago
youngS 0bac3e55f4 优化微信消息推送模板,管理后台增加微信绑定功能 3 years ago
youngS 820e1df84e 优化代码 3 years ago
youngS 08ffa72eab 管理后台增加应用类型展示 3 years ago
youngS 6861c65b83 管理后台增加应用原包下载功能 3 years ago
youngS 439db9788a 管理后台增加应用原包下载功能 3 years ago
youngS 2feda8800c 管理后台增加域名功能 3 years ago
youngS b10a40d177 下载域名统一管理 3 years ago
youngS 3325814c03 增加域名访问权限控制,用户绑定的域名只能允许本用户应用访问 3 years ago
youngS f6918011ca 增加域名下载码绑定功能,权限大于1的可以使用该功能 3 years ago
youngS d7a31c62b3 优化代码 3 years ago
youngS d154381895 优化代码,增加cli.py 上传进度条,修复签名相关问题 3 years ago
youngS 13a67be0ff 修复超级签名,老包未及时清理问题 3 years ago
youngS b815950f7d 优化代码 3 years ago
youngS 4316398d82 修复存储迁移失败的问题 3 years ago
youngS 749d72b513 优化代码展示 3 years ago
youngS bfb3738d69 优化代码,修复新用户注册一直绑定同一个系统域名问题 3 years ago
youngS 3c80635d5f 增加微信登录开启关闭配置 3 years ago
youngS ed05083bbe 修改异常bug 3 years ago
youngS 31677fb1fd 优化代码 3 years ago
youngS cf1267deb2 增加用户名控制登录 3 years ago
youngS 5988e0e1a9 描述文件bug 3 years ago
youngS 81f1f75fb1 描述文件bug 3 years ago
youngS 08e7eb3476 优化代码 3 years ago
youngS 0148905071 优化获取token逻辑 3 years ago
youngS bab6e083bf 优化代码 3 years ago
youngS adaf7af866 优化代码,增加查询udid的服务器逻辑 3 years ago
youngS e8931df00f 优化描述文件签名逻辑,修复清理临时文件bug 3 years ago
youngS 1dc6e84ba7 下载页面增加udid展示 3 years ago
youngS cac1703957 优化开发者账户同步逻辑 3 years ago
youngS dfcf878416 优化代码,修复bug 3 years ago
youngS 45978aae73 修复设备id不存在返回异常 3 years ago
youngS e84f47170a 修改bug 3 years ago
youngS 69c699aebb 修改只能注册两个用户bug 3 years ago
youngS 19709c7da3 优化获取微信token代码 3 years ago
youngS dc8de286c5 优化获取微信token代码 3 years ago
youngS 9b8412304f 增加微信公众号绑定扫码登录功能 3 years ago
youngS d6ce64c3b6 优化苹果开发者激活检测方式 3 years ago
youngS 4f0c8a302c 优化下载次数统计代码 3 years ago
youngS 3ade1efadb 优化页面展示 3 years ago
youngS 90dceee1c5 修复开发者账户认证键值异常 3 years ago
youngS d460da0024 优化重置密码逻辑 3 years ago
youngS 10297e1616 修复清理临时文件失败的问题 3 years ago
youngS 37df9f4a5d 支持短信找回密码功能 3 years ago
youngS a8e8402eff 修复本地文件连接异常问题 3 years ago
youngS dd218fe745 优化上传失败 3 years ago
youngS 42cb1bfc88 增加js版本展示 3 years ago
youngS d8b02a74ab 优化阿里云oss上传失败重试 3 years ago
youngS 7599e5f3a2 代码优化 3 years ago
youngS 4501b5dc2a 前端页面增加应用上传速度展示 3 years ago
youngS 58e7f8fa6a 实名认证照片存放到服务器默认存储 3 years ago
youngS 436e26af37 前端页面增加批量应用上传功能 3 years ago
youngS cb3738f4de 管理后台 存储和超级签权限修改 3 years ago
youngS aa83de23b5 优化代码 3 years ago
youngS 5169c8a477 超级签自定义名字不能为空 3 years ago
youngS 83509a79de 增加容器部署 3 years ago
youngS 9abdc5f5ae 下载页展示优化 3 years ago
youngS a95a2fb22f 修复超级签密码访问未校验问题,增加post接收速率限制 3 years ago
youngS 1bd0f08a4b 优化代码 3 years ago
youngS cdf778d3f3 苹果开发者过期时间限制 3 years ago
youngS bb5d5829df 优化代码 3 years ago
youngS a5a79867f1 cdn NGINX获取真实ip 3 years ago
youngS 8c1ca01532 优化代码 3 years ago
youngS 4e3af4de35 修复解除合并,未清理cache问题 3 years ago
youngS 586136729a 优化下载页展示 3 years ago
youngS 3c4e5aa6e3 修改下载页面,第三方应用不自动跳转问题 3 years ago
youngS cad1a77f8f 增加服务操作脚本 3 years ago
youngS f7f0896893 优化下载页展示 3 years ago
youngS f3349ecc5b 修复当miniOSversion不存在引发的异常 3 years ago
youngS 7fba632390 修复当miniOSversion不存在引发的异常 3 years ago
youngS aa33e5640b 优化代码 3 years ago
youngS 45d813cca3 优化代码 3 years ago
youngS 95b4b45da8 修复开发者不存在导致签名失败bug 3 years ago
youngS 00032239b0 优化证书检测 3 years ago
youngS 86d5623437 修改p12 证书导入逻辑 3 years ago
youngS d4e4e9b278 修改p12 证书导入逻辑 3 years ago
youngS a6f0cb0174 增加支持p12 证书导入 3 years ago
youngS b40f93a16c 增加支持p12 证书导入 3 years ago
youngS e6f8b2f628 修改超级签方式,isign 部分包无法签名,使用zsign 进行签名 3 years ago
youngS ed841bc8fb 优化实名认证逻辑 4 years ago
youngS a59958e1af 管理后台增加超级签开发展示 4 years ago
youngS 5835b4d6e1 优化代码 4 years ago
youngS 5bac330551 优化代码 4 years ago
youngS 1cc239698b 优化代码,增加超级签密钥证书获取教程 4 years ago
youngS 56cd35565f 超级签默认启动 4 years ago
youngS 30db4882b4 修改前端页面 4 years ago
youngS 19b82de65f 优化首页代码 4 years ago
youngS 179f46b2b3 更新Django版本,增加celery定时任务 4 years ago
youngS 2cb0f1a8f5 超级签下载次数消化更换 4 years ago
youngS 04df14d919 超级签下载次数消化更换 4 years ago
youngS a39e3f623d 优化阿里云浏览器上传授权 4 years ago
youngS 04fe7a2965 修复管理员迁移失败问题 4 years ago
youngS 0df8cf68be 修改超级签名使用任务队列进行签名 4 years ago
youngS 8b0fab87b5 优化代码 4 years ago
youngS fd56fcd88b 删除超级签名,用户和密码添加方式 4 years ago
youngS 2c557a5b12 set default storage cache 4 years ago
youngS 19ba89eec7 超级签下载文件到服务器通过内网通信 4 years ago
youngS 96785fec31 超级签下载文件到服务器通过内网通信 4 years ago
youngS bb422ee67d 超级签下载文件到服务器通过内网通信 4 years ago
youngS 0baaaedc67 修改代码 4 years ago
youngS e53e0843a3 优化代码,增加域名默认数据 4 years ago
nineven 10e9f80067 优化代码 4 years ago
nineven 7a4e73fc2b 优化代码 4 years ago
nineven af21dd2129 优化上传展示 4 years ago
youngS 7c5b2b7270 优化业务支持第三方域名https跳转 4 years ago
youngS 0ac35618e2 reset db install ........... 4 years ago
youngS 76bde18c5a 增加重置密码页面和逻辑 4 years ago
youngS 50d877f2f3 增加域名绑定功能 4 years ago
youngS 710de8432d 增加域名绑定功能 4 years ago
youngS 4a8de02afa 增加域名绑定功能 4 years ago
youngS 250c07c1a6 增加域名绑定功能 4 years ago
youngS a82f2a3ec7 增加域名绑定页面展示 4 years ago
youngS 3da097f6e5 增加默认日志路径 4 years ago
youngS 875445bc09 封禁应用无法下载 4 years ago
youngS a57143f732 增加app 历史版本可查看更多 4 years ago
youngS 9727534b91 vue.config settings 4 years ago
youngS 7100febed9 增加geetest 加载动画展示 4 years ago
youngS a320bd9165 发送邮件支持 ssl 465端口 4 years ago
youngS 2980131dd3 修复oss上传失败bug 4 years ago
youngS 49ce167ce6 修复超级签按钮展示 4 years ago
youngS 0416bf5e15 优化代码 4 years ago
youngS ed18398563 优化前端展示界面 4 years ago
youngS 23f0d90c50 优化前端展示界面 4 years ago
youngS f96fc79822 修复超级签bug 4 years ago
youngS c93df04b18 优化充值问题 4 years ago
youngS 25fb257a53 vue 打包命令修改 4 years ago
youngS 9157a9b43f 优化前端展示界面 4 years ago
  1. 9
      .gitignore
  2. 207
      README.md
  3. 102
      doc/docker.md
  4. BIN
      doc/images/img_1.png
  5. BIN
      doc/images/img_10.png
  6. BIN
      doc/images/img_11.png
  7. BIN
      doc/images/img_2.png
  8. BIN
      doc/images/img_3.png
  9. BIN
      doc/images/img_4.png
  10. BIN
      doc/images/img_5.png
  11. BIN
      doc/images/img_6.png
  12. BIN
      doc/images/img_7.png
  13. BIN
      doc/images/img_8.png
  14. BIN
      doc/images/img_9.png
  15. 166
      doc/local.md
  16. 67
      docker/build/docker-compose.yml
  17. 31
      docker/flyapps/docker-compose.only.yml
  18. 48
      docker/flyapps/docker-compose.yml
  19. 7
      docker/init/clean.sh
  20. 10
      docker/init/download.images
  21. 24
      docker/init/init.sh
  22. 1
      docker/init/timezone
  23. 43
      docker/mariadb/docker-compose.yml
  24. 108
      docker/mariadb/server.cnf
  25. 25
      docker/redis/docker-compose.yml
  26. 6
      docker/scripts/build.sh
  27. 9
      docker/scripts/start_all.sh
  28. 10
      docker/scripts/stop_all.sh
  29. 3
      fir_admin/.env.development
  30. 2
      fir_admin/.env.production
  31. 40
      fir_admin/src/api/app.js
  32. 54
      fir_admin/src/api/developer.js
  33. 10
      fir_admin/src/api/devices.js
  34. 31
      fir_admin/src/api/domain.js
  35. 26
      fir_admin/src/api/order.js
  36. 31
      fir_admin/src/api/report.js
  37. 13
      fir_admin/src/api/storage.js
  38. 8
      fir_admin/src/api/user.js
  39. 31
      fir_admin/src/api/wxbind.js
  40. 153
      fir_admin/src/router/index.js
  41. 2
      fir_admin/src/store/modules/user.js
  42. 15
      fir_admin/src/views/appinfos/AppDetail.vue
  43. 20
      fir_admin/src/views/appinfos/AppReleaseDetail.vue
  44. 41
      fir_admin/src/views/appinfos/AppReleaseList.vue
  45. 48
      fir_admin/src/views/appinfos/list.vue
  46. 4
      fir_admin/src/views/authentication/list.vue
  47. 146
      fir_admin/src/views/domain/Detail.vue
  48. 188
      fir_admin/src/views/domain/list.vue
  49. 2
      fir_admin/src/views/login/index.vue
  50. 15
      fir_admin/src/views/order/Detail.vue
  51. 38
      fir_admin/src/views/order/list.vue
  52. 158
      fir_admin/src/views/report/Detail.vue
  53. 227
      fir_admin/src/views/report/list.vue
  54. 15
      fir_admin/src/views/storage/Detail.vue
  55. 29
      fir_admin/src/views/storage/list.vue
  56. 183
      fir_admin/src/views/supersign/Detail.vue
  57. 117
      fir_admin/src/views/supersign/bill/add.vue
  58. 202
      fir_admin/src/views/supersign/bill/list.vue
  59. 193
      fir_admin/src/views/supersign/developer/Detail.vue
  60. 218
      fir_admin/src/views/supersign/developer/list.vue
  61. 98
      fir_admin/src/views/supersign/devices/list.vue
  62. 3
      fir_admin/src/views/supersign/index.vue
  63. 127
      fir_admin/src/views/userinfos/UserDetail.vue
  64. 36
      fir_admin/src/views/userinfos/list.vue
  65. 157
      fir_admin/src/views/wxbind/Detail.vue
  66. 192
      fir_admin/src/views/wxbind/list.vue
  67. 47
      fir_client/README.md
  68. 11281
      fir_client/package-lock.json
  69. 11
      fir_client/package.json
  70. 16
      fir_client/public/config.js
  71. BIN
      fir_client/public/favicon.ico
  72. 7
      fir_client/public/index.html
  73. 8
      fir_client/public/short.html
  74. 154
      fir_client/src/App.vue
  75. 128
      fir_client/src/Download.vue
  76. BIN
      fir_client/src/assets/github.png
  77. BIN
      fir_client/src/assets/imgs/banner1.png
  78. BIN
      fir_client/src/assets/imgs/banner2.jpg
  79. BIN
      fir_client/src/assets/imgs/banner_1.jpg
  80. BIN
      fir_client/src/assets/imgs/banner_2.jpg
  81. BIN
      fir_client/src/assets/imgs/circle.png
  82. BIN
      fir_client/src/assets/imgs/computer.png
  83. BIN
      fir_client/src/assets/imgs/contact_us_bg.jpg
  84. BIN
      fir_client/src/assets/imgs/qq.png
  85. BIN
      fir_client/src/assets/imgs/service1.jpg
  86. BIN
      fir_client/src/assets/imgs/service2.jpg
  87. BIN
      fir_client/src/assets/imgs/service3.jpg
  88. BIN
      fir_client/src/assets/imgs/service4.jpg
  89. BIN
      fir_client/src/assets/imgs/skill.png
  90. BIN
      fir_client/src/assets/imgs/tel.png
  91. BIN
      fir_client/src/assets/imgs/twitter.png
  92. BIN
      fir_client/src/assets/imgs/weibo.png
  93. BIN
      fir_client/src/assets/imgs/weixin.png
  94. BIN
      fir_client/src/assets/sign/help/sign_help_0.png
  95. BIN
      fir_client/src/assets/sign/help/sign_help_1.png
  96. BIN
      fir_client/src/assets/sign/help/sign_help_2.png
  97. BIN
      fir_client/src/assets/sign/help/sign_help_3.png
  98. BIN
      fir_client/src/assets/sign/help/sign_help_4.png
  99. BIN
      fir_client/src/assets/sign/help/sign_help_5.png
  100. BIN
      fir_client/src/assets/wx_pay.jpg
  101. Some files were not shown because too many files have changed in this diff Show More

9
.gitignore vendored

@ -5,3 +5,12 @@ fir_ser/venv/
fir_client/.idea
fir_admin/.idea/.gitignore
fir_admin/.idea/
fir_client/yarn.lock
fir_download/package-lock.json
fir_client/src/assets/down_left.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
```
#### 超级签名依赖部署
##### fastlane [相关文档](https://github.com/fastlane/fastlane/blob/master/spaceship/docs/DeveloperPortal.md)
```
# fastlane
#1 gem ruby安装
yum install gem -y
yum install ruby-devel -y
yum install gcc gcc-c++ -y
#gem修改为国内镜像
gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
gem sources -l
#https://gems.ruby-china.com
#确保只有 gems.ruby-china.com
#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
source /etc/profile.d/rvm.sh
rvm list known
rvm install 2.7
#3 安装fastlane
gem install fastlane
gem install pry
# 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 #注释掉,要不然会报错
```
##### isign
```
# python2.7
yum update python -y
pip install -U setuptools pip
wget https://github.com/nineaiyu/isign/archive/v1.8.0.tar.gz
tar xvf v1.8.0.tar.gz
cd isign-1.8.0/
python setup.py build
python setup.py install
```
### FlyApp 服务器搭建 [python3 环境]
##### 搭建python env 环境
```
yum install python36 python36-devel redis mariadb-server mariadb-devel -y
python3 -m venv py3
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
```
### 用与应用分发,苹果超级签名
#### 部署前准备
- 备案域名【至少需要一个域名,以下可通过子域名部署】
- API域名
- 前端web域名
- 下载页域名
- 下载页域名可配置多个
- 存储域名(使用阿里云oss存储)
- ssl证书
- API域名证书
- 存储域名证书(使用阿里云oss存储)
- 前端web域名证书(可选)
- Centos8Stream 服务器
- 如果使用oss存储,则带宽为1M,若使用本地存储,则带宽越大越好
- 如果使用超级签,最低配置为2cpu 4G内存,若干不使用签名,则1cpu2G就行
- 阿里云短信或极光短信服务【可选一个,主要用与注册,重置密码】
- 阿里云短信
- 极光短信
- 邮箱服务【可选,用与注册,重置密码,通知信息】
- 阿里云OSS存储【可选】
- [sts授权配置](https://help.aliyun.com/document_detail/100624.html)
- 阿里云CDN【可选,用与加速访问】
- 极验验证【可选,滑动验证服务】
- 微信公众号【可选,用与微信扫描登录】
- 阿里云支付【可选,用与购买下载次数】
- 微信支付【可选,用与购买下载次数】
#### 自用搭建建议
- 阿里云服务器需要1cpu 2G内存,无需系统盘,如果使用超级签,可以适当增加配置
- 需要阿里云OSS存储和阿里云CDN,并且OSS存储和阿里云服务器部署同一个地区
- 可以申请一个极验进行滑动验证,或者开启验证码验证
- 阿里云备案域名:api和前端可以使用一个域名,下载页单独域名
#### 部署必备资料
- 域名证书
- web域名和证书
- api域名和证书
- 下载页域名(可配置证书)
- 存储域名和证书
- 本地存储,则该域名和证书可以和api域名证书一致
- 阿里云oss存储
- 开启cdn,需要新域名和证书
- 不开启,无需域名和证书
- Centos8Stream 服务器
## 部署方式
### 1.[Docker 部署](./doc/docker.md) 【推荐】
### 2.[本地部署](./doc/local.md)
### 功能预览
![img_1.png](./doc/images/img_1.png)
![img_2.png](./doc/images/img_2.png)
![img_3.png](./doc/images/img_3.png)
![img_4.png](./doc/images/img_4.png)
![img_5.png](./doc/images/img_5.png)
![img_6.png](./doc/images/img_6.png)
![img_7.png](./doc/images/img_7.png)
![img_8.png](./doc/images/img_8.png)
![img_9.png](./doc/images/img_9.png)
![img_10.png](./doc/images/img_10.png)
![img_11.png](./doc/images/img_11.png)

@ -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

@ -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

@ -2,4 +2,5 @@
ENV = 'development'
# base api
VUE_APP_BASE_API = 'https://app.hehelucky.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'
# 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'
export function getAppInfos(query) {
export function getAppList(query) {
return request({
url: '/app/info',
method: 'get',
@ -8,23 +8,29 @@ export function getAppInfos(query) {
})
}
export function updateAppInfo(data) {
export function getAppInfos(pk) {
return request({
url: '/app/info',
url: '/app/info/' + pk,
method: 'get'
})
}
export function updateAppInfo(pk, data) {
return request({
url: '/app/info/' + pk,
method: 'put',
data
})
}
export function deleteApp(data) {
export function deleteApp(pk) {
return request({
url: '/app/info',
method: 'delete',
data
url: '/app/info/' + pk,
method: 'delete'
})
}
export function getAppReleaseInfos(query) {
export function getAppReleaseList(query) {
return request({
url: '/app/release/info',
method: 'get',
@ -32,10 +38,24 @@ export function getAppReleaseInfos(query) {
})
}
export function updateReleaseAppInfo(data) {
export function getAppReleaseInfos(pk) {
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',
data
})
}
export function downloadAppReleaseInfos(data) {
return request({
url: '/app/release/info',
method: 'post',
data
})
}

@ -0,0 +1,54 @@
import request from '@/utils/request'
export function getDeveloperList(query) {
return request({
url: '/developer/info',
method: 'get',
params: query
})
}
export function getDeveloperInfo(pk) {
return request({
url: '/developer/info/' + pk,
method: 'get'
})
}
export function updatedeveloperInfo(pk, data) {
return request({
url: '/developer/info/' + pk,
method: 'put',
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
})
}

@ -0,0 +1,10 @@
import request from '@/utils/request'
export function getDevicesList(query) {
return request({
url: '/devices/info',
method: 'get',
params: query
})
}

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

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

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

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

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

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

@ -3,6 +3,8 @@ import Router from 'vue-router'
Vue.use(Router)
const celery_flower = process.env.VUE_APP_BASE_API.split('/')
/* Layout */
import Layout from '@/layout'
@ -137,18 +139,6 @@ export const constantRoutes = [
hidden: true
}
]
},
{
path: '/sign',
component: Layout,
children: [
{
path: 'index',
name: 'index',
component: () => import('@/views/form/index'),
meta: { title: '超级签名', icon: 'form' }
}
]
}, {
path: '/authentication',
component: Layout,
@ -167,7 +157,8 @@ export const constantRoutes = [
hidden: true
}
]
}, {
},
{
path: '/order',
component: Layout,
children: [
@ -186,6 +177,131 @@ export const constantRoutes = [
}
]
},
{
path: '/domain',
component: Layout,
children: [
{
path: 'list',
name: 'domain_info_list',
component: () => import('@/views/domain/list'),
meta: { title: '下载域名管理', icon: 'form' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/domain/Detail'),
name: 'domain_info_edit',
meta: { title: '编辑信息', noCache: true, activeMenu: '/domain/list' },
hidden: true
}
]
},
{
path: '/report',
component: Layout,
children: [
{
path: 'list',
name: 'report_info_list',
component: () => import('@/views/report/list'),
meta: { title: '应用举报信息', icon: 'form' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/report/Detail'),
name: 'report_info_edit',
meta: { title: '编辑信息', noCache: true, activeMenu: '/report/list' },
hidden: true
}
]
},
{
path: '/wxbind',
component: Layout,
children: [
{
path: 'list',
name: 'wxbind_info_list',
component: () => import('@/views/wxbind/list'),
meta: { title: '微信绑定管理', icon: 'form' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/wxbind/Detail'),
name: 'wxbind_info_edit',
meta: { title: '编辑信息', noCache: true, activeMenu: '/wxbind/list' },
hidden: true
}
]
},
{
path: '/supersign',
component: Layout,
redirect: '/supersign/developer',
name: '超级签名',
meta: {
title: '超级签名',
icon: 'nested'
},
children: [
{
path: 'developer',
name: 'developer',
redirect: '/supersign/developer/list',
component: () => import('@/views/supersign/index'),
children: [
{
path: 'list',
name: 'developer_user_info_list',
component: () => import('@/views/supersign/developer/list'),
meta: { title: '苹果开发者', icon: 'form' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/supersign/developer/Detail'),
name: 'developer_user_info_edit',
meta: { title: '编辑信息', noCache: true, activeMenu: '/supersign/list' },
hidden: true
}
]
},
{
path: 'devices',
name: 'devices',
redirect: '/supersign/devices/list',
component: () => import('@/views/supersign/index'),
children: [
{
path: 'list',
name: 'devices_info_list',
component: () => import('@/views/supersign/devices/list'),
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' }
}
]
}
]
},
{
path: '/settings',
component: Layout,
@ -231,7 +347,6 @@ export const constantRoutes = [
}
]
},
{
path: '/nested',
component: Layout,
@ -300,6 +415,16 @@ export const constantRoutes = [
}
]
},
{
path: 'celery-flower-link',
component: Layout,
children: [
{
path: celery_flower[0] + '//' + celery_flower[2] + '/flower',
meta: { title: '任务监控', icon: 'link' }
}
]
},
// 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true }

@ -1,7 +1,7 @@
import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { resetRouter } from '@/router'
const Base64 = require('js-base64').Base64;
const Base64 = require('js-base64').Base64
const getDefaultState = () => {
return {

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

@ -102,7 +102,7 @@
<el-form-item label="ios内测版 udid" label-width="200px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.udid" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" disabled />
<el-input v-if="postForm.udid" :value="JSON.stringify(postForm.udid)" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" disabled />
</el-col>
</el-row>
</el-form-item>
@ -161,28 +161,28 @@ export default {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false
is_edit: false,
id: ''
}
},
computed: {
},
created() {
const id = this.$route.params && this.$route.params.id
const app_id = this.$route.params && this.$route.params.app_id
this.fetchData(id, app_id)
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id, app_id) {
getAppReleaseInfos({ id: id, app_id: app_id }).then(response => {
if (response.data.length === 1) {
this.postForm = response.data[0]
fetchData(id) {
getAppReleaseInfos(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateReleaseAppInfo(this.postForm).then(response => {
updateReleaseAppInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {

@ -2,7 +2,7 @@
<div class="app-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-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-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -67,13 +67,16 @@
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
<router-link :to="{name: 'app_release_info_edit',params:{app_id:listQuery.app_id,id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<el-button type="info" size="mini" @click="download_bin(scope.row)">
下载源文件
</el-button>
</template>
</el-table-column>
</el-table>
@ -83,7 +86,7 @@
</template>
<script>
import { getAppReleaseInfos } from '@/api/app'
import { downloadAppReleaseInfos, getAppReleaseList } from '@/api/app'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
@ -130,12 +133,11 @@ export default {
page: 1,
limit: 10,
app_id: undefined,
download_token: undefined,
release_id: undefined,
sort: '-created_time'
ordering: '-created_time'
},
sortOptions,
type_choices: [],
status_choices: []
sortOptions
}
},
created() {
@ -143,19 +145,30 @@ export default {
this.fetchData()
},
methods: {
download_bin(app_info) {
downloadAppReleaseInfos(
{
'token': app_info.download_token,
'release_id': app_info.release_id,
'app_id': app_info.app_id
}
).then(res => {
if (res.code === 1000) {
window.location.href = res.data.download_url
} else {
this.$message.error(res.msg)
}
})
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getAppReleaseInfos(this.listQuery).then(response => {
this.list = response.data
if (this.list && this.list.length > 0) {
this.type_choices = this.list[0].type_choices
this.status_choices = this.list[0].status_choices
}
this.total = response.total
getAppReleaseList(this.listQuery).then(response => {
this.list = response.data.results
this.total = response.data.count
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.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.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-option v-for="item in type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
@ -12,7 +12,7 @@
<el-option v-for="item in status_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<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-select>
<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 label="应用名称">
<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>
</el-table-column>
<el-table-column label="Bundle_Id" align="center">
@ -71,16 +75,26 @@
{{ scope.row.short }}
</template>
</el-table-column>
<el-table-column label="下载次数" width="100" align="center">
<el-table-column label="下载次数" width="80" align="center">
<template slot-scope="scope">
{{ scope.row.count_hits }}
</template>
</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">
<template slot-scope="scope">
{{ scope.row.master_release.binary_size }}
</template>
</el-table-column>
<el-table-column label="应用类型" width="80" align="center">
<template slot-scope="scope">
{{ scope.row |appTypeNameFilter }}
</template>
</el-table-column>
<el-table-column label="历史版本数" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'app_release_info_list',params:{app_id:scope.row.id}}">
@ -121,13 +135,13 @@
</template>
</el-table-column>
</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>
</template>
<script>
import { getAppInfos, deleteApp } from '@/api/app'
import { deleteApp, getAppList } from '@/api/app'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
@ -157,7 +171,10 @@ export default {
return statusMap[status]
},
appStatusNameFilter(row) {
return baseFilter(row.status,row.status_choices)
return baseFilter(row.status, row.status_choices)
},
appTypeNameFilter(row) {
return baseFilter(row.type, row.type_choices)
},
appStatusFilter(status) {
const statusMap = {
@ -172,13 +189,13 @@ export default {
return {
list: null,
listLoading: true,
total: 0,
count: 0,
listQuery: {
page: 1,
limit: 10,
name: undefined,
bundle_id: undefined,
sort: '-count_hits',
ordering: '-count_hits',
type: undefined,
domain_name: undefined,
user_id: undefined
@ -189,9 +206,14 @@ export default {
}
},
created() {
this.listQuery.user_id = this.$route.params && this.$route.params.user_id
this.listQuery.id = this.$route.params && this.$route.params.app_id
this.fetchData()
},
methods: {
go_download_url(app_info) {
window.open(app_info.preview_url, '_blank', '')
},
deleteApp(app_id) {
this.$confirm('此操作将永久删除该应用, 是否继续?', '提示', {
confirmButtonText: '确定',
@ -199,7 +221,7 @@ export default {
type: 'warning'
}).then(() => {
this.listLoading = true
deleteApp({ id: app_id }).then(response => {
deleteApp(app_id).then(() => {
this.$message.success('删除成功')
this.fetchData()
this.listLoading = false
@ -217,13 +239,13 @@ export default {
},
fetchData() {
this.listLoading = true
getAppInfos(this.listQuery).then(response => {
this.list = response.data
getAppList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.type_choices = this.list[0].type_choices
this.status_choices = this.list[0].status_choices
}
this.total = response.total
this.count = response.data.count
this.listLoading = false
})
}

@ -86,9 +86,7 @@
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>

@ -0,0 +1,146 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.user_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="postForm.app_info&& postForm.app_info.app_id" label="应用ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.app_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="postForm.app_info&& postForm.app_info.app_id" label="应用名称">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.app_info.name" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="域名类型">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.domain_type" class="filter-item" disabled>
<el-option v-for="item in postForm.domain_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="绑定的域名" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.domain_name" />
</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.is_enable" class="filter-item">
<el-option v-for="item in domain_state_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="CnameID" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.cname_id" disabled />
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" :disabled="postForm.id === postForm.used_id" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getDomainInfos, updateDomainInfo } from '@/api/domain'
const domain_state_choices = [
{ id: false, name: '继续绑定' },
{ id: true, name: '绑定成功' }
]
const defaultForm = {
user_id: undefined,
app_info: undefined,
domain_type: undefined,
domain_name: undefined,
cname_id: undefined,
is_enable: undefined,
domain_type_choices: undefined,
created_time: undefined
}
export default {
name: 'OrderDetail',
components: { }, filters: {},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false,
domain_state_choices,
id: ''
}
},
computed: {},
created() {
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id) {
getDomainInfos(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateDomainInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped>
.editor-container {
position: relative;
height: 100%;
}
</style>

@ -0,0 +1,188 @@
<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.domain_name" placeholder="下载域名" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.app_name" placeholder="应用名称" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.domain_type" placeholder="下载域名类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in domain_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<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-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="下载域名" min-width="280px" align="center">
<template slot-scope="scope">
{{ scope.row.domain_name }}
</template>
</el-table-column>
<el-table-column class-name="status-col" label="下载域名类型" width="160" align="center">
<template slot-scope="scope">
<router-link v-if="scope.row.domain_type===2" :to="{name: 'app_info_list',params:{user_id:scope.row.user_id,app_id:scope.row.app_id}}">
<el-tag :type="scope.row.domain_type | domainStatusFilter">{{ scope.row| domainLableFilter }}</el-tag>
</router-link>
<el-tag v-else :type="scope.row.domain_type | domainStatusFilter">{{ scope.row| domainLableFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="绑定状态" width="95" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.is_enable | statusFilter">{{ scope.row| statusLableFilter }}</el-tag>
</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">
<router-link :to="{name: 'domain_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="remove_domain(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 { deleteDomain, getDomainList } from '@/api/domain'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
const domain_state_choices = [
{ id: false, name: '继续绑定' },
{ id: true, name: '绑定成功' }
]
export default {
name: 'DomainInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
if (time) {
return time.split('T')[0]
}
},
domainStatusFilter(status) {
const statusMap = {
'1': 'success',
'0': 'gray',
'2': 'info' }
return statusMap[status]
},
domainLableFilter(row) {
if (row.domain_type === 2) {
return '应用' + row.app_info.name + '域名'
}
return baseFilter(row.domain_type, row.domain_type_choices)
},
statusLableFilter(row) {
return baseFilter(row.is_enable, domain_state_choices)
},
statusFilter(status) {
const statusMap = {
true: 'success',
false: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: {
page: 1,
limit: 10,
user_id: undefined,
ordering: '-created_time',
domain_name: undefined,
app_name: undefined,
domain_type: undefined,
is_enable: undefined
},
sortOptions,
domain_type_choices: [],
domain_state_choices
}
},
created() {
this.fetchData()
}, mounted() {
if (this.$route.params.user_id) {
this.listQuery.user_id = this.$route.params.user_id
}
console.log(this.$router)
},
methods: {
remove_domain(domain_id) {
deleteDomain(domain_id).then(response => {
this.fetchData()
})
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getDomainList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.domain_type_choices = this.list[0].domain_type_choices
}
this.total = response.data.count
this.listLoading = false
})
}
}
}
</script>

@ -66,7 +66,7 @@ export default {
}
}
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'))
} else {
callback()

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

@ -14,7 +14,7 @@
<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-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-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -59,7 +59,7 @@
</el-table-column>
<el-table-column label="实付金额" align="center" width="80px">
<template slot-scope="scope">
{{ scope.row.actual_amount }}
{{ scope.row.actual_amount/100 }}
</template>
</el-table-column>
<el-table-column label="购买数量" align="center" width="90px">
@ -105,7 +105,7 @@
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">
<el-button type="danger" size="mini" @click="remove_order_info(scope.row)">
删除
</el-button>
</template>
@ -117,14 +117,18 @@
</template>
<script>
import { getOrderInfo } from '@/api/order'
import { deleteOrderInfo, getOrderList } from '@/api/order'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'reviewed_time' },
{ label: '创建时间 Descending', key: '-reviewed_time' },
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' },
{ label: '付款时间 Ascending', key: 'pay_time' },
{ label: '付款时间 Descending', key: '-pay_time' },
{ label: '付款金额 Ascending', key: 'actual_amount' },
{ label: '付款金额 Descending', key: '-actual_amount' }
]
export default {
@ -188,7 +192,7 @@ export default {
page: 1,
limit: 10,
user_id: undefined,
sort: '-created_time',
ordering: '-created_time',
payment_type: undefined,
payment_name: undefined,
payment_number: undefined,
@ -205,25 +209,35 @@ export default {
created() {
this.fetchData()
}, mounted() {
if (this.$route.query.user_id) {
this.listQuery.user_id = this.$route.query.user_id
if (this.$route.params.user_id) {
this.listQuery.user_id = this.$route.params.user_id
}
},
methods: {
remove_order_info(order_info) {
deleteOrderInfo(order_info.id).then(response => {
this.list = response.data
if (response.code === 1000) {
this.fetchData()
} else {
this.$message.error('操作失败了 ' + response.msg)
}
})
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getOrderInfo(this.listQuery).then(response => {
this.list = response.data
getOrderList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.payment_type_choices = this.list[0].payment_type_choices
this.status_choices = this.list[0].status_choices
this.order_type_choices = this.list[0].order_type_choices
}
this.total = response.total
this.total = response.data.count
this.listLoading = false
})
}

@ -0,0 +1,158 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="应用ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.app_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="应用名称">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.app_name" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="举报类型">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.report_type" class="filter-item" disabled>
<el-option v-for="item in postForm.report_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="举报原因">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.report_reason" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" disabled />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="举报者姓名" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.username" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="举报联系方式" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.email" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="举报者IP地址" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.remote_addr" disabled />
</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.status" class="filter-item">
<el-option v-for="item in postForm.status_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="处理备注" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.description" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" />
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" :disabled="postForm.id === postForm.used_id" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getAppReportIfo, updateAppReportIfo } from '@/api/report'
const defaultForm = {
app_id: undefined,
app_name: undefined,
bundle_id: undefined,
created_time: undefined,
description: undefined,
email: undefined,
remote_addr: undefined,
report_reason: undefined,
status: undefined,
report_type: undefined,
username: undefined
}
export default {
name: 'ReportDetail',
components: { }, filters: {},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false,
id: ''
}
},
computed: {},
created() {
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id) {
getAppReportIfo(this.id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateAppReportIfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped>
.editor-container {
position: relative;
height: 100%;
}
</style>

@ -0,0 +1,227 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.app_id" placeholder="应用ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.app_name" placeholder="应用名称" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.bundle_id" placeholder="Bundle_Id" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.remote_addr" placeholder="远端地址" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.email" placeholder="联系信息" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.report_type" placeholder="举报类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in report_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.status" placeholder="处理状态" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in status_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: '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="应用名称" width="80px">
<template slot-scope="scope">
{{ scope.row.app_name }}
</template>
</el-table-column>
<el-table-column label="Bundle_Id" align="center">
<template slot-scope="scope">
<span>{{ scope.row.bundle_id }}</span>
</template>
</el-table-column>
<el-table-column label="联系信息" align="center">
<template slot-scope="scope">
<span>{{ scope.row.email }}</span>
</template>
</el-table-column>
<el-table-column label="联系姓名" align="center">
<template slot-scope="scope">
<span>{{ scope.row.username }}</span>
</template>
</el-table-column>
<el-table-column label="举报者IP" align="center">
<template slot-scope="scope">
<span>{{ scope.row.remote_addr }}</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="举报类型" width="95" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.report_type | payStatusFilter">{{ scope.row| payLableFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="处理状态" width="95" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status | certStatusFilter">{{ scope.row| statusLableFilter }}</el-tag>
</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">
<router-link :to="{name: 'report_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="remove_order_info(scope.row)">
删除
</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 { deleteAppReportIfo, getAppReportList } from '@/api/report'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
export default {
name: 'OrderInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
if (time) {
return time.split('T')[0]
}
},
payStatusFilter(status) {
const statusMap = {
'0': 'gray',
'1': 'success' }
return statusMap[status]
},
certStatusFilter(status) {
const statusMap = {
'0': 'success',
'1': 'info',
'2': 'gray' }
return statusMap[status]
},
payLableFilter(row) {
return baseFilter(row.report_type, row.report_type_choices)
},
statusLableFilter(row) {
return baseFilter(row.status, row.status_choices)
},
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,
app_id: undefined,
ordering: '-created_time',
report_type: undefined,
app_name: undefined,
bundle_id: undefined,
email: undefined,
status: undefined,
remote_addr: undefined,
username: undefined
},
sortOptions,
report_type_choices: [],
status_choices: []
}
},
created() {
this.fetchData()
}, mounted() {
if (this.$route.params.user_id) {
this.listQuery.user_id = this.$route.params.user_id
}
},
methods: {
remove_order_info(order_info) {
deleteAppReportIfo(order_info.id).then(response => {
this.list = response.data
if (response.code === 1000) {
this.fetchData()
} else {
this.$message.error('操作失败了 ' + response.msg)
}
})
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getAppReportList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.report_type_choices = this.list[0].report_type_choices
this.status_choices = this.list[0].status_choices
}
this.total = response.data.count
this.listLoading = false
})
}
}
}
</script>

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

@ -9,7 +9,7 @@
<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-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-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -83,9 +83,7 @@
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
@ -95,15 +93,15 @@
</template>
<script>
import { getStorageInfo } from '@/api/storage'
import { getStorageList } from '@/api/storage'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'reviewed_time' },
{ label: '创建时间 Descending', key: '-reviewed_time' },
{ label: '更新时间 Ascending', key: 'created_time' },
{ label: '更新时间 Descending', key: '-created_time' }
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' },
{ label: '更新时间 Ascending', key: 'updated_time' },
{ label: '更新时间 Descending', key: '-updated_time' }
]
export default {
@ -162,21 +160,21 @@ export default {
page: 1,
limit: 10,
name: undefined,
sort: '-created_time',
bucket_name: undefined,
access_key: undefined,
storage_type: undefined,
domain_name: undefined,
user_id: undefined,
used_id: undefined
ordering: '-created_time'
},
sortOptions,
storage_choices: []
}
},
created() {
this.listQuery.user_id = this.$route.params && this.$route.params.user_id
this.fetchData()
},mounted() {
}, mounted() {
if (this.$route.query.user_id) {
this.listQuery.user_id = this.$route.query.user_id
}
@ -188,12 +186,13 @@ export default {
},
fetchData() {
this.listLoading = true
getStorageInfo(this.listQuery).then(response => {
this.list = response.data
getStorageList(this.listQuery).then(response => {
this.list = response.data.results
console.log(this.list)
if (this.list && this.list.length > 0) {
this.storage_choices = this.list[0].storage_choices
}
this.total = response.total
this.total = response.data.count
this.listLoading = false
})
}

@ -1,183 +0,0 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.user_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="存储名称">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.name" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="存储类型">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.storage_type" class="filter-item" disabled>
<el-option v-for="item in postForm.storage_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="更新时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.updated_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="存储描述信息">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.description" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="存储访问access_key" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.access_key" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="存储访问secret_key" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.secret_key" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="存储空间bucket_name" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.bucket_name" />
</el-col>
</el-row>
</el-form-item>
<div v-if="postForm.storage_type===2">
<el-form-item label="阿里云sts_role_arn" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.sts_role_arn" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="阿里云endpoint" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.endpoint" />
</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.download_auth_type" class="filter-item" style="width: 100%">
<el-option v-for="item in postForm.download_auth_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="postForm.download_auth_type===2" label="阿里云cnd_auth_key" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.cnd_auth_key" />
</el-col>
</el-row>
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getStorageInfo, updateStorageInfo } from '@/api/storage'
const defaultForm = {
user_id: undefined,
name: undefined,
storage_type: undefined,
access_key: undefined,
secret_key: undefined,
bucket_name: undefined,
domain_name: undefined,
created_time: undefined,
description: undefined,
sts_role_arn: undefined,
endpoint: undefined,
download_auth_type: undefined,
cnd_auth_key: undefined,
updated_time: undefined,
download_auth_type_choices: []
}
export default {
name: 'StorageDetail',
components: { }, filters: {},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false
}
},
computed: {},
created() {
const id = this.$route.params && this.$route.params.id
this.fetchData(id)
},
methods: {
fetchData(id) {
getStorageInfo({ id: id }).then(response => {
if (response.data.length === 1) {
this.postForm = response.data[0]
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateStorageInfo(this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped>
.editor-container {
position: relative;
height: 100%;
}
</style>

@ -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>

@ -0,0 +1,193 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.user_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="issuer_id">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.issuer_id" />
</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.usable_number" :min="0" :max="100" label="可使用设备数" />
</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.use_number" :min="0" :max="100" label="已消耗设备数" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="账户类型">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.auth_type" class="filter-item" disabled>
<el-option v-for="item in postForm.auth_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="更新时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.updated_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="描述信息">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.description" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="账户状态" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-tooltip :content="postForm.is_actived|userStatusFilter" placement="top">
<el-switch
v-model="postForm.is_actived"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="true"
:inactive-value="false"
/>
</el-tooltip>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="证书过期时间" prop="timestamp" label-width="160px">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.cert_expire_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="private_key_id" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.private_key_id" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="p8key" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.p8key" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="certid" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.certid" />
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getDeveloperInfo, updatedeveloperInfo } from '@/api/developer'
const defaultForm = {
user_id: undefined,
is_actived: undefined,
issuer_id: undefined,
private_key_id: undefined,
p8key: undefined,
certid: undefined,
created_time: undefined,
description: undefined,
usable_number: undefined,
use_number: undefined,
auth_type: undefined,
cert_expire_time: undefined,
updated_time: undefined,
auth_type_choices: []
}
export default {
name: 'DeveloperDetail',
components: { }, filters: {
userStatusFilter(status) {
const statusMap = {
true: '激活,允许使用',
false: '禁用,禁止使用'
}
return statusMap[status]
}
},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false,
id: ''
}
},
computed: {},
created() {
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id) {
getDeveloperInfo(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updatedeveloperInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped>
.editor-container {
position: relative;
height: 100%;
}
</style>

@ -0,0 +1,218 @@
<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.issuer_id" placeholder="issuer_id" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.private_key_id" placeholder="private_key_id" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.certid" placeholder="证书ID" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.description" placeholder="备注" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="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-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="issuer_id" width="300">
<template slot-scope="scope">
{{ scope.row.issuer_id }}
</template>
</el-table-column>
<el-table-column label="private_key_id" align="center">
<template slot-scope="scope">
<span>{{ scope.row.private_key_id }}</span>
</template>
</el-table-column>
<el-table-column label="certid" align="center">
<template slot-scope="scope">
{{ scope.row.certid }}
</template>
</el-table-column>
<el-table-column label="可用设备数" align="center" width="100">
<template slot-scope="scope">
{{ scope.row.usable_number }}
</template>
</el-table-column>
<el-table-column label="消耗设备数" align="center" width="100">
<template slot-scope="scope">
<router-link :to="{name: 'devices_info_list',params:{issuer_id:scope.row.issuer_id}}">
<el-link type="primary"> {{ scope.row.use_number }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="账户类型" width="130" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.auth_type | certStatusFilter">{{ scope.row| certLableFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="是否激活" width="80" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.is_actived | statusFilter">{{ scope.row |StatusNameFilter }}</el-tag>
</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 align="center" prop="created_time" label="证书过期时间" width="120">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.updated_time">
<span>{{ scope.row.cert_expire_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">
<router-link :to="{name: 'developer_user_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<!-- <el-button type="danger" size="mini" @click="deleteApp(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 { getDeveloperList } from '@/api/developer'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' },
{ label: '更新时间 Ascending', key: 'updated_time' },
{ label: '更新时间 Descending', key: '-updated_time' },
{ label: '证书过期时间 Ascending', key: 'cert_expire_time' },
{ label: '证书过期时间 Descending', key: '-cert_expire_time' }
]
export default {
name: 'StorageInfo',
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]
},
StatusNameFilter(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',
issuer_id: undefined,
private_key_id: undefined,
certid: undefined,
description: undefined,
user_id: undefined,
auth_type: undefined
},
sortOptions,
auth_type_choices: [],
status_choices: []
}
},
created() {
this.fetchData()
},
methods: {
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getDeveloperList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.auth_type_choices = this.list[0].auth_type_choices
this.status_choices = this.list[0].status_choices
}
this.total = response.data.count
this.listLoading = false
})
}
}
}
</script>

@ -2,14 +2,13 @@
<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.bucket_name" placeholder="bucket_name" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.access_key" placeholder="access_key" 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.domain_name" placeholder="下载域名" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="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-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter">
<el-input v-model="listQuery.issuer_id" placeholder="开发者issuer_id" 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.name" placeholder="应用名称" 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-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">
@ -37,54 +36,52 @@
</router-link>
</template>
</el-table-column>
<el-table-column label="存储名称">
<el-table-column label="应用ID" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.name }}
<router-link :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="存储访问access_key" align="center">
<el-table-column label="开发者issuer_id" align="center">
<template slot-scope="scope">
<span>{{ scope.row.access_key }}</span>
<router-link :to="{name: 'developer_user_info_edit',params:{id:scope.row.developer_pk}}">
<el-link type="primary"> {{ scope.row.developer_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="存储空间bucket_name" align="center">
<el-table-column label="应用名称" align="center" >
<template slot-scope="scope">
{{ scope.row.bucket_name }}
{{ scope.row.bundle_name }}
</template>
</el-table-column>
<el-table-column label="下载域名" align="center">
<el-table-column label="短连接" align="center" width="100">
<template slot-scope="scope">
{{ scope.row.domain_name }}
{{ scope.row.short }}
</template>
</el-table-column>
<el-table-column class-name="status-col" label="存储类型" width="95" align="center">
<el-table-column label="BundleID" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.storage_type | certStatusFilter">{{ scope.row| certLableFilter }}</el-tag>
{{ scope.row.bundle_id }}
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="创建时间" width="120">
<el-table-column label="设备UDID" align="center" width="350">
<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>
{{ scope.row.device_udid }}
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="更新时间" width="120">
<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.updated_time">
<span>{{ scope.row.updated_time|formatTime }}</span>
<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">
<router-link :to="{name: 'storage_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">
删除
</el-button>
@ -97,19 +94,17 @@
</template>
<script>
import { getStorageInfo } from '@/api/storage'
import { getDevicesList } from '@/api/devices'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'reviewed_time' },
{ label: '创建时间 Descending', key: '-reviewed_time' },
{ label: '更新时间 Ascending', key: 'created_time' },
{ label: '更新时间 Descending', key: '-created_time' }
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
export default {
name: 'StorageInfo',
name: 'DevicesInfo',
components: { Pagination },
directives: { waves },
filters: {
@ -126,8 +121,8 @@ export default {
return statusMap[status]
},
certLableFilter(row) {
for (const v of row.storage_choices) {
if (v.id === row.storage_type) {
for (const v of row.auth_type_choices) {
if (v.id === row.auth_type) {
return v.name
}
}
@ -163,19 +158,20 @@ export default {
listQuery: {
page: 1,
limit: 10,
ordering: '-created_time',
issuer_id: undefined,
short: undefined,
bundle_id: undefined,
name: undefined,
sort: '-created_time',
bucket_name: undefined,
access_key: undefined,
storage_type: undefined,
domain_name: undefined,
user_id: undefined
user_id: undefined,
udid: undefined
},
sortOptions,
storage_choices: []
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: {
@ -185,12 +181,12 @@ export default {
},
fetchData() {
this.listLoading = true
getStorageInfo(this.listQuery).then(response => {
this.list = response.data
getDevicesList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.storage_choices = this.list[0].storage_choices
this.auth_type_choices = this.list[0].auth_type_choices
}
this.total = response.total
this.total = response.data.count
this.listLoading = false
})
}

@ -0,0 +1,3 @@
<template>
<router-view />
</template>

@ -6,21 +6,21 @@
<el-form-item label="UID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.uid" disabled />
<el-input :value="postForm.uid" disabled/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="账号" prop="username">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.username" />
<el-input v-model="postForm.username"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="昵称">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.first_name" />
<el-input v-model="postForm.first_name"/>
</el-col>
</el-row>
</el-form-item>
@ -28,7 +28,9 @@
<el-row :gutter="12">
<el-col :span="16">
<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-col>
</el-row>
@ -37,7 +39,7 @@
<el-row :gutter="12">
<el-col :span="16">
<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-col>
</el-row>
@ -46,7 +48,7 @@
<el-form-item label="邮箱" prop="email">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.email" />
<el-input v-model="postForm.email"/>
</el-col>
</el-row>
</el-form-item>
@ -54,35 +56,37 @@
<el-form-item label="手机" prop="mobile">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.mobile" />
<el-input v-model="postForm.mobile"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="Q Q" prop="number">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.qq" />
<el-input v-model="postForm.qq"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="职位">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.job" />
<el-input v-model="postForm.job"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="公司">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.company" />
<el-input v-model="postForm.company"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="备注">
<el-row :gutter="12">
<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-row>
</el-form-item>
@ -91,7 +95,9 @@
<el-form-item label="用户头像">
<el-row :gutter="12">
<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-row>
</el-form-item>
@ -177,10 +183,16 @@
<el-form-item label="实名认证">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.certification" class="filter-item" placeholder="Please select" :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 v-model="postForm.certification" class="filter-item" placeholder="Please select"
: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>
<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>
@ -194,38 +206,49 @@
<el-form-item label="下载域名" prop="domain_name">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.domain_name" />
<router-link v-if="postForm.id" :to="{name: 'domain_info_list',params:{user_id:postForm.id}}">
<el-button type="primary">查看下载域名绑定信息</el-button>
</router-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="下载次数" prop="download_times">
<el-row :gutter="12">
<el-col :span="8">
<el-input :value="postForm.download_times" disabled />
<el-input :value="postForm.download_times" disabled/>
</el-col>
<el-col :span="8">
<el-button>后台充值</el-button>
<router-link v-if="postForm.id" :to="{name: 'order_info_list',params:{user_id:postForm.id}}">
<el-button type="primary">查看充值订单信息</el-button>
</router-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="注册时间" prop="timestamp">
<el-row :gutter="20">
<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-row>
</el-form-item>
<el-form-item label="应用版本历史记录">
<el-row :gutter="20">
<el-col :span="8">
<el-input v-model="postForm.history_release_limit" />
<el-input v-model="postForm.history_release_limit"/>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-col :span="8">
<el-button @click="add_amount_download_times(postForm.id)">下载次数充值</el-button>
</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>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
@ -237,9 +260,9 @@
<script>
import { validURL } from '@/utils/validate'
import { getUserInfos, updateUserInfo } from '@/api/user'
import { getStorageInfo,changeStorageInfo } from '@/api/storage'
import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
import { getUserInfos, resetPassword, updateUserInfo } from '@/api/user'
import { createOrderInfo } from '@/api/order'
import { changeStorageInfo, getStorageList } from '@/api/storage'
const defaultForm = {
email: undefined,
@ -260,6 +283,7 @@ const defaultForm = {
date_joined: undefined,
storage_active: undefined,
storage: undefined,
certification_id: undefined,
supersign_active: undefined,
role_choices: [],
gender_choices: [],
@ -268,7 +292,7 @@ const defaultForm = {
export default {
name: 'UserDetail',
components: { CommentDropdown, PlatformDropdown, SourceUrlDropdown }, filters: {
components: {}, filters: {
userStatusFilter(status) {
const statusMap = {
true: '激活,允许登录',
@ -332,8 +356,7 @@ export default {
storage_selection: []
}
},
computed: {
},
computed: {},
created() {
if (this.isEdit) {
const id = this.$route.params && this.$route.params.id
@ -341,17 +364,61 @@ export default {
}
},
methods: {
add_amount_download_times(user_id) {
this.$prompt('请输入要充值的下载次数', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(({ value }) => {
createOrderInfo({ id: user_id, amount: parseInt(value) }).then(res => {
if (res.code === 1000) {
this.$message.success('下载次数充值成功')
this.fetchData(user_id)
}
})
}).catch((e) => {
console.log(e)
this.$message({
type: 'info',
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) {
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('存储数据迁移并设置刷新成功')
}).catch(err => {
console.log(err)
})
},
fetchStorageData(user_id) {
getStorageInfo({ user_id: user_id }).then(response => {
if (response.storage_selection) {
this.storage_selection = response.storage_selection
getStorageList({ user_id: user_id }).then(response => {
if (response.data.storage_selection) {
this.storage_selection = response.data.storage_selection
}
}).catch(err => {
console.log(err)

@ -34,12 +34,12 @@
<el-image :src="scope.row.head_img" :preview-src-list="[scope.row.head_img]" fit="contain" style="width: 80px; height: 80px" />
</template>
</el-table-column>
<el-table-column label="登录名">
<el-table-column label="登录名" align="center">
<template slot-scope="scope">
{{ scope.row.username }}
</template>
</el-table-column>
<el-table-column label="昵称">
<el-table-column label="昵称" align="center">
<template slot-scope="scope">
{{ scope.row.first_name }}
</template>
@ -49,7 +49,7 @@
<span>{{ scope.row.mobile }}</span>
</template>
</el-table-column>
<el-table-column label="邮箱" width="210" align="center">
<el-table-column label="邮箱" width="200" align="center">
<template slot-scope="scope">
{{ scope.row.email }}
</template>
@ -60,21 +60,39 @@
{{ scope.row.download_times }}
</template>
</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">
<template slot-scope="scope">
<router-link :to="{name: 'app_info_list',params:{user_id:scope.row.id}}">
<el-link type="primary"> {{ scope.row.app_count }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="是否激活" width="80" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.is_active | statusFilter">{{ scope.row.is_active }}</el-tag>
</template>
</el-table-column>
<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">
<el-tag :type="scope.row.storage_active | statusFilter">{{ scope.row.storage_active }}</el-tag>
<router-link v-if="scope.row.storage_active" :to="{name: 'storage_info_list',params:{user_id:scope.row.id}}">
<el-link type="primary"> <el-tag :type="scope.row.storage_active | statusFilter">{{ scope.row.storage_active }}</el-tag></el-link>
</router-link>
<el-tag v-else :type="scope.row.storage_active | statusFilter">{{ scope.row.storage_active }}</el-tag>
</template>
</el-table-column>
<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">
<el-tag :type="scope.row.supersign_active | statusFilter">{{ scope.row.supersign_active }}</el-tag>
<router-link v-if="scope.row.supersign_active" :to="{name: 'devices_info_list',params:{user_id:scope.row.id}}">
<el-link type="primary"> <el-tag :type="scope.row.supersign_active | statusFilter">{{ scope.row.supersign_active }}</el-tag></el-link>
</router-link>
<el-tag v-else :type="scope.row.supersign_active | statusFilter">{{ scope.row.supersign_active }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="实名认证" width="95" align="center">
@ -91,7 +109,7 @@
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" width="110" class-name="small-padding fixed-width">
<template slot-scope="scope">
<router-link :to="{name: 'user_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
@ -144,7 +162,7 @@ export default {
return statusMap[status]
},
certLableFilter(row) {
return baseFilter(row.certification,row.certification_status_choices)
return baseFilter(row.certification, row.certification_status_choices)
}
},
data() {

@ -0,0 +1,157 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.user_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="微信openid">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.openid" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="微信昵称">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.nickname" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" disabled type="datetime" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="订阅时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.subscribe_time*1000" disabled type="datetime" />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="用户头像" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-image
:preview-src-list="[postForm.head_img_url]"
:src="postForm.head_img_url"
fit="contain"
style="width: 100px; height: 100px"
/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="扫码登录" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.enable_login" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="消息通知" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.enable_notify" disabled />
</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-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" :disabled="postForm.id === postForm.used_id" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getWxBindInfos, updateWxBindInfo } from '@/api/wxbind'
const wxbind_state_choices = [
{ id: false, name: '未订阅' },
{ id: true, name: '已订阅' }
]
const defaultForm = {
user_id: undefined,
address: undefined,
created_time: undefined,
head_img_url: undefined,
id: undefined,
nickname: undefined,
openid: undefined,
subscribe: undefined,
subscribe_time: undefined,
enable_login: undefined,
enable_notify: undefined
}
export default {
name: 'OrderDetail',
components: { }, filters: {},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false,
wxbind_state_choices,
id: ''
}
},
computed: {},
created() {
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id) {
getWxBindInfos(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateWxBindInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped>
</style>

@ -0,0 +1,192 @@
<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.openid" placeholder="微信openid" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.nickname" placeholder="微信昵称" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="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-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="头像" align="center">
<template slot-scope="scope">
<el-image :src="scope.row.head_img_url" :preview-src-list="[scope.row.head_img_url]" fit="contain" style="width: 80px; height: 80px" />
</template>
</el-table-column>
<el-table-column label="微信昵称" min-width="180px" align="center">
<template slot-scope="scope">
{{ scope.row.nickname }}
</template>
</el-table-column>
<el-table-column label="微信openid" min-width="280px" align="center">
<template slot-scope="scope">
{{ scope.row.openid }}
</template>
</el-table-column>
<el-table-column label="允许扫码登录" min-width="100px" align="center">
<template slot-scope="scope">
{{ scope.row.enable_login }}
</template>
</el-table-column>
<el-table-column label="允许消息通知" min-width="100px" align="center">
<template slot-scope="scope">
{{ scope.row.enable_notify }}
</template>
</el-table-column>
<el-table-column class-name="status-col" label="订阅状态" width="95" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.subscribe | statusFilter">{{ scope.row| statusLableFilter }}</el-tag>
</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">
<router-link :to="{name: 'wxbind_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="remove_wxbind(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 { deleteWxBind, getWxBindList } from '@/api/wxbind'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
const wxbind_state_choices = [
{ id: false, name: '未订阅' },
{ id: true, name: '已订阅' }
]
export default {
name: 'WxBindInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
if (time) {
return time.split('T')[0]
}
},
wxbindStatusFilter(status) {
const statusMap = {
'1': 'success',
'0': 'gray',
'2': 'info' }
return statusMap[status]
},
wxbindLableFilter(row) {
if (row.wxbind_type === 2) {
return '应用' + row.app_info.name + '域名'
}
return baseFilter(row.wxbind_type, row.wxbind_type_choices)
},
statusLableFilter(row) {
return baseFilter(row.subscribe, wxbind_state_choices)
},
statusFilter(status) {
const statusMap = {
true: 'success',
false: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: {
page: 1,
limit: 10,
user_id: undefined,
ordering: '-created_time',
openid: undefined,
nickname: undefined,
subscribe: undefined
},
sortOptions,
wxbind_state_choices
}
},
created() {
this.fetchData()
}, mounted() {
if (this.$route.params.user_id) {
this.listQuery.user_id = this.$route.params.user_id
}
console.log(this.$router)
},
methods: {
remove_wxbind(wxbind_id) {
deleteWxBind(wxbind_id).then(response => {
this.list = response.data
this.total = response.total
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getWxBindList(this.listQuery).then(response => {
this.list = response.data.results
this.total = response.data.count
this.listLoading = false
})
}
}
}
</script>

@ -39,5 +39,48 @@ yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
## deploy nginx
#### 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>
```

File diff suppressed because it is too large Load Diff

@ -5,12 +5,13 @@
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"analyz": "vue-cli-service serve analyz",
"lint": "vue-cli-service lint"
},
"dependencies": {
"ali-oss": "^6.5.1",
"app-info-parser": "^0.3.9",
"axios": "^0.19.0",
"axios": "^0.22.0",
"core-js": "^3.4.4",
"element-ui": "^2.13.0",
"js-base64": "^2.5.2",
@ -19,6 +20,7 @@
"vue": "^2.6.10",
"vue-clipboard2": "^0.3.1",
"vue-cookies": "^1.7.0",
"vue-cropper": "^0.5.7",
"vue-lazyload": "^1.3.3",
"vue-qr": "^2.3.0",
"vue-router": "^3.1.3",
@ -30,9 +32,14 @@
"@vue/cli-service": "^4.1.0",
"babel-eslint": "^10.0.3",
"babel-plugin-component": "^1.1.1",
"compression-webpack-plugin": "5.0.1",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"vue-template-compiler": "^2.6.10"
"html-webpack-plugin": "^5.3.1",
"script-ext-html-webpack-plugin": "^2.1.5",
"uglifyjs-webpack-plugin": "^2.2.0",
"vue-template-compiler": "^2.6.10",
"webpack-bundle-analyzer": "^4.4.1"
},
"eslintConfig": {
"root": true,

@ -1,16 +0,0 @@
window.g = {
baseUrl: 'https://app.hehelucky.cn',
PaymentQuestionMsg:'如对充值订单有疑问,请联系 nineven@qq.com',
footer: {
copyright: 'Copyright © 2017-2020 第九系艾文 版权所有.',
ipcBeiAn: {
url: 'https://beian.miit.gov.cn',
text: '豫ICP备15004336号',
},
gongAnBeiAn: {
url: 'http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=41142202000049',
text: '豫公网安备 41142202000049号',
},
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

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

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

@ -1,87 +1,87 @@
<template>
<div id="app" ref="app">
<router-view></router-view>
</div>
<div id="app" ref="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {}
},
}
export default {
name: 'app',
data() {
return {}
},
}
</script>
<style>
body {
background-color: #F6F6F6;
}
html, body {
height: 100%;
}
@font-face {
font-family: "iconfont";
src: url('./assets/icon/iconfont.eot?t=1583637102320'); /* IE9 */
src: url('./assets/icon/iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('./assets/icon/iconfont.woff?t=1583637102320') format('woff'),
url('./assets/icon/iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('./assets/icon/iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-android:before {
content: "\e61d";
}
.icon-android1:before {
content: "\e616";
}
.icon-android2:before {
content: "\e62a";
}
.icon-ios:before {
content: "\e60c";
}
.icon-IOS:before {
content: "\e68c";
}
.icon-ios1:before {
content: "\e601";
}
.icon-ios2:before {
content: "\e64b";
}
.icon-Android:before {
content: "\e6d4";
}
.icon-android3:before {
content: "\e625";
}
.canvas {
position: fixed;
left: -0px;
top: 58px;
height: 100%;
z-index: -2;
width: 100%;
}
body {
background-color: #F6F6F6;
}
html, body {
height: 100%;
}
@font-face {
font-family: "iconfont";
src: url('./assets/icon/iconfont.eot?t=1583637102320'); /* IE9 */
src: url('./assets/icon/iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('./assets/icon/iconfont.woff?t=1583637102320') format('woff'),
url('./assets/icon/iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('./assets/icon/iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-android:before {
content: "\e61d";
}
.icon-android1:before {
content: "\e616";
}
.icon-android2:before {
content: "\e62a";
}
.icon-ios:before {
content: "\e60c";
}
.icon-IOS:before {
content: "\e68c";
}
.icon-ios1:before {
content: "\e601";
}
.icon-ios2:before {
content: "\e64b";
}
.icon-Android:before {
content: "\e6d4";
}
.icon-android3:before {
content: "\e625";
}
.canvas {
position: fixed;
left: -0px;
top: 58px;
height: 100%;
z-index: -2;
width: 100%;
}
</style>

@ -1,75 +1,75 @@
<template>
<div id="download" ref="download">
<router-view></router-view>
</div>
<div id="download" ref="download">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'download',
data() {
return {}
},
}
export default {
name: 'download',
data() {
return {}
},
}
</script>
<style>
body {
background-color: rgba(0, 166, 224, 0.18)
}
@font-face {
font-family: "iconfont";
src: url('./assets/icon/iconfont.eot?t=1583637102320'); /* IE9 */
src: url('./assets/icon/iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('./assets/icon/iconfont.woff?t=1583637102320') format('woff'),
url('./assets/icon/iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('./assets/icon/iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-android:before {
content: "\e61d";
}
.icon-android1:before {
content: "\e616";
}
.icon-android2:before {
content: "\e62a";
}
.icon-ios:before {
content: "\e60c";
}
.icon-IOS:before {
content: "\e68c";
}
.icon-ios1:before {
content: "\e601";
}
.icon-ios2:before {
content: "\e64b";
}
.icon-Android:before {
content: "\e6d4";
}
.icon-android3:before {
content: "\e625";
}
body {
background-color: #F6F6F6;
}
@font-face {
font-family: "iconfont";
src: url('./assets/icon/iconfont.eot?t=1583637102320'); /* IE9 */
src: url('./assets/icon/iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('./assets/icon/iconfont.woff?t=1583637102320') format('woff'),
url('./assets/icon/iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('./assets/icon/iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-android:before {
content: "\e61d";
}
.icon-android1:before {
content: "\e616";
}
.icon-android2:before {
content: "\e62a";
}
.icon-ios:before {
content: "\e60c";
}
.icon-IOS:before {
content: "\e68c";
}
.icon-ios1:before {
content: "\e601";
}
.icon-ios2:before {
content: "\e64b";
}
.icon-Android:before {
content: "\e6d4";
}
.icon-android3:before {
content: "\e625";
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

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

Loading…
Cancel
Save