init fly app services

storage_local
nineven 5 years ago
commit e26ae1da18
  1. 5
      .gitignore
  2. 21
      fir_client/.gitignore
  3. 24
      fir_client/README.md
  4. 5
      fir_client/babel.config.js
  5. 11281
      fir_client/package-lock.json
  6. 48
      fir_client/package.json
  7. 17
      fir_client/public/index.html
  8. 80
      fir_client/src/App.vue
  9. BIN
      fir_client/src/assets/download_pattern_left.png
  10. BIN
      fir_client/src/assets/download_pattern_right.png
  11. 356
      fir_client/src/assets/gt.js
  12. 53
      fir_client/src/assets/icon/iconfont.css
  13. BIN
      fir_client/src/assets/icon/iconfont.eot
  14. 1
      fir_client/src/assets/icon/iconfont.js
  15. 72
      fir_client/src/assets/icon/iconfont.json
  16. 53
      fir_client/src/assets/icon/iconfont.svg
  17. BIN
      fir_client/src/assets/icon/iconfont.ttf
  18. BIN
      fir_client/src/assets/icon/iconfont.woff
  19. BIN
      fir_client/src/assets/icon/iconfont.woff2
  20. 424
      fir_client/src/assets/plane.svg
  21. 57
      fir_client/src/assets/propeller.svg
  22. 31
      fir_client/src/components/FirAppBase.vue
  23. 327
      fir_client/src/components/FirAppInfosBase.vue
  24. 217
      fir_client/src/components/FirAppInfosbaseinfo.vue
  25. 411
      fir_client/src/components/FirAppInfoscombo.vue
  26. 555
      fir_client/src/components/FirAppInfostimeline.vue
  27. 762
      fir_client/src/components/FirApps.vue
  28. 1519
      fir_client/src/components/FirDownload.vue
  29. 121
      fir_client/src/components/FirHeader.vue
  30. 39
      fir_client/src/components/FirIndex.vue
  31. 174
      fir_client/src/components/FirLogin.vue
  32. 189
      fir_client/src/components/FirRegist.vue
  33. 213
      fir_client/src/components/FirUserProfileBase.vue
  34. 96
      fir_client/src/components/FirUserProfileChangePwd.vue
  35. 208
      fir_client/src/components/FirUserProfileInfo.vue
  36. 49
      fir_client/src/main.js
  37. 582
      fir_client/src/restful/index.js
  38. 117
      fir_client/src/router/index.js
  39. 50
      fir_client/src/store/index.js
  40. 40
      fir_client/src/utils/index.js
  41. 8171
      fir_client/yarn.lock
  42. 0
      fir_ser/api/__init__.py
  43. 12
      fir_ser/api/admin.py
  44. 5
      fir_ser/api/apps.py
  45. 119
      fir_ser/api/migrations/0001_initial.py
  46. 19
      fir_ser/api/migrations/0002_auto_20200303_1940.py
  47. 27
      fir_ser/api/migrations/0003_auto_20200306_1734.py
  48. 27
      fir_ser/api/migrations/0004_auto_20200306_2331.py
  49. 22
      fir_ser/api/migrations/0005_auto_20200307_0800.py
  50. 22
      fir_ser/api/migrations/0006_auto_20200307_0810.py
  51. 18
      fir_ser/api/migrations/0007_appreleaseinfo_changelog.py
  52. 23
      fir_ser/api/migrations/0008_auto_20200307_1249.py
  53. 26
      fir_ser/api/migrations/0009_auto_20200309_1424.py
  54. 27
      fir_ser/api/migrations/0010_auto_20200309_1918.py
  55. 23
      fir_ser/api/migrations/0011_auto_20200310_1019.py
  56. 18
      fir_ser/api/migrations/0012_userinfo_head_img.py
  57. 0
      fir_ser/api/migrations/__init__.py
  58. 147
      fir_ser/api/models.py
  59. 192
      fir_ser/api/tests.py
  60. 45
      fir_ser/api/urls.py
  61. 0
      fir_ser/api/utils/__init__.py
  62. 47
      fir_ser/api/utils/ali/__init__.py
  63. 59
      fir_ser/api/utils/ali/api.py
  64. 4
      fir_ser/api/utils/ali/client/__init__.py
  65. 4
      fir_ser/api/utils/ali/client/api/__init__.py
  66. 13
      fir_ser/api/utils/ali/client/api/certify.py
  67. 4
      fir_ser/api/utils/ali/client/basic.py
  68. 4
      fir_ser/api/utils/ali/pay/__init__.py
  69. 10
      fir_ser/api/utils/ali/pay/api/__init__.py
  70. 82
      fir_ser/api/utils/ali/pay/api/app.py
  71. 32
      fir_ser/api/utils/ali/pay/api/base.py
  72. 44
      fir_ser/api/utils/ali/pay/api/order.py
  73. 81
      fir_ser/api/utils/ali/pay/api/page.py
  74. 100
      fir_ser/api/utils/ali/pay/api/transfer.py
  75. 81
      fir_ser/api/utils/ali/pay/api/wap.py
  76. 198
      fir_ser/api/utils/ali/pay/basic.py
  77. 69
      fir_ser/api/utils/ali/tools.py
  78. 12
      fir_ser/api/utils/ali/yun/__init__.py
  79. 7
      fir_ser/api/utils/ali/yun/api/__init__.py
  80. 42
      fir_ser/api/utils/ali/yun/api/base.py
  81. 145
      fir_ser/api/utils/ali/yun/api/sms.py
  82. 213
      fir_ser/api/utils/ali/yun/api/vod.py
  83. 226
      fir_ser/api/utils/ali/yun/basic.py
  84. 76
      fir_ser/api/utils/app/TokenManager.py
  85. 245
      fir_ser/api/utils/app/analyze.py
  86. 71
      fir_ser/api/utils/app/dadf.py
  87. 385
      fir_ser/api/utils/app/firApi.py
  88. 91
      fir_ser/api/utils/app/raqc.py
  89. 66
      fir_ser/api/utils/auth.py
  90. 47
      fir_ser/api/utils/captcha_verify.py
  91. 10
      fir_ser/api/utils/exceptions.py
  92. 23
      fir_ser/api/utils/filter.py
  93. 199
      fir_ser/api/utils/geetest.py
  94. 18
      fir_ser/api/utils/middlewares.py
  95. 21
      fir_ser/api/utils/permission.py
  96. 28
      fir_ser/api/utils/randomstrings.py
  97. 13
      fir_ser/api/utils/response.py
  98. 294
      fir_ser/api/utils/serializer.py
  99. 3
      fir_ser/api/views.py
  100. 0
      fir_ser/api/views/__init__.py
  101. Some files were not shown because too many files have changed in this diff Show More

5
.gitignore vendored

@ -0,0 +1,5 @@
fir_ser/.idea/
fir_ser/db.sqlite3
*.pyc
fir_ser/files/
fir_ser/venv/

@ -0,0 +1,21 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

@ -0,0 +1,24 @@
# fir_test
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,48 @@
{
"name": "fir_test",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.19.0",
"core-js": "^3.4.4",
"element-ui": "^2.13.0",
"js-base64": "^2.5.2",
"qrcodejs2": "^0.0.2",
"vue": "^2.6.10",
"vue-cookies": "^1.7.0",
"vue-router": "^3.1.3",
"vuex": "^3.1.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.1.0",
"@vue/cli-plugin-eslint": "^4.1.0",
"@vue/cli-service": "^4.1.0",
"babel-eslint": "^10.0.3",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"vue-template-compiler": "^2.6.10"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<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">
<title>fly分发平台</title>
</head>
<body>
<noscript>
<strong>We're sorry but fly doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

@ -0,0 +1,80 @@
<template>
<div id="app" ref="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app',
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,d09GMgABAAAAAAnUAAsAAAAAEXwAAAmIAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCEIAqUYJBKATYCJAMoCxYABCAFhG0HdhuGDlGUL1aT7CuMW3giIDU2lBZVYzMmA6BYbBoB4AIAvsWyNCR1ZYRAPDyt/e/cmdn1b4KYdG+eSCaJRMWjWPVE6ZTyQkZL4P/W2j938EFdZvf2bG4F1UgXqyQeKfBIqpGQb4i2eQ/W/4P6Ek9oYwayNWYT2liwtS7KBWzNL+pBXQarNpcN/6uINB8EdIMjCcASCSSx4cAb7oGJPvjzf6maMe0OLEmbbAtQ1TLH+t2bjwpcoLGH1RZY9/OCWElqEp69Ctt/lUoZgKMJ9DZKJTpb39oLSoVQBWoeMPQUKGtmZQQOra7u2bKID6DSppeMC3hP7x//HA0lSZWJNS/fr5uFyp+QiaT/uZpYwYv1OWFuR8Z+oBDfe93f5Fy5n9R9H1g8BBYZNWVZ128wWubBX4gsc00C/+npXSCHRbkuqBVJ6GSNo/zjETVVPQL/ONPxMyIeNT9HHoWfyzwSPzeo8DQ7IF5OsxdGGZpzMGqgeQOjCpoJxHejmz7ZYhE4AvIjgLIWhPY8ORu1xKf7mSXfEqJWz1lFqu4IsUVclojNFtvZeZZJWCxfTwc225MLw+IOJVu9jIhlZydks0WD2YzVCGJDzZv3bRWtVSPgFM6ZTJDKjCPGrSIdax2dlWgymUleOEnitJKXpPyEKz9ri1IZTMalepIwQ/od1OvB5hq3QpDBcnCLkASgX+Ot37xvI9/Ep9QGC7EF1DMQNJgws3VoBmqIDgCbNXYuVd01lKFvR5CqnITxPpi1Z23gbKCzVCYzT3meaFLvhr5kmfWJDrV+iz8KDo/ogiAUbGoo8SKbLBDTGVnfLmB0BpiGzuEVMxCPpJitnaqi7QPc/I2nEGN6Yb7eUj2voN2qcKHuJxEIVUb7HLspkHcwNKgtsk9XrLzLL2qnl/syr6WbOyffau0FeKUntEq8bGeo+bLsHFVv2IggXAm3BxcRKD4qQJAKpPFED97cegA2kUtJkufhEa5hguf0l6KbAjfRsqUoyisV/sajng+9Tg8sp/2sVPRImnYco2IPWCUbqfgT1lSrrW/7VfTgedn+s9KD1yN30pCRKrXQnuutQZutHvvpsYD3WcPOT9ADl+8K2mVNPnAp9cSF3FNUNnVr4DitOXmXd5BOI2x9VwM0UXRJJ6U6MWENSmG19tlu9tF0g83WkJw35hw1brVGEXTtxk4RtKzrdEcwaG7N+s0ANrPfcth3vHWoM7SOzosxLcPNKMmLbqN7xpoDCR7Zwo26aAOV2mziKs4ZG1WmpTzlGUXJPpWdjSaDmSD1S9EsUryx0/OqplwYy9vjrrPyxLZUyHbAlg4UBYchLJdPRqKr5WY1GnenBYElwhrjZsE0UWXitVtXau4B6TbsEETKTYgqW7ca11skHotAqCLSqu8TzTldome82Wsp5a2lPn03qYZV0HrRfaYtuR2xJ6W3pQhPi8Xyezqa3c8TyNnMNv1zOy+QUBFrb8+Yo3hUiDsBt4OTRPQOeXLy11deWJbq1+OgVrJp8JITpcISj+p5aa9SJ5TKi/o2VKD37G/xu1ERehnQt/bKk+LfZZ2Li48XXAy/KLjDXTbqabKD0+DFjk5YidPrAw8b+ADh3wm7wHd0BAD7RC6YL3I63Np/w+1uHZyLbhdOOgWY4JsQi8aE33JgNDJJFCsKnToP+Ain5hLZJ+oMIeJY0aRZzy41NiRiiRkNlwIzZVR8UKZ6gjrOx1Oy55plBcn64P+UtFEhJJwsTRaRKlKkLQ6TIVFajgW4FybDoZ1loaLkI+ACamn7vt5/X1BiF1y1/8m85UFdNdyaAG6A7phsAKgEUNljkEgZGhJqDY7/TQBeHCUBQ7bM3HwgbX2hUHhqbn3VDsIVYcAoomY1TISKHcKariNO2PbaZgyX5N6oC/OHfNjxXzabV+91hR2d90zbk6bWotfUxzuvZ/mWE2AIgaUsOMde5snf/NxBElNVPnB045As1kLdjhRM0wCJH8X384+4FiQpzJ3tlufDTZydm1/k+xI80DKzs7INNcWemo9FflMbU70a5MXSzpiaiJeyfPP74Jjso8dKNQbLzDuiJKZjVebQuSV4UtxHuc94mSgx1kfuDXSn54cWlI/IC901zzN/UFheSAz85UPeQ4W6cPeJ5Z7Qz3V1Yf4DxUwcjVMOHNy/Z7VCcnVNu+RhrCZm2Pgc/PekHiDmSxhpaVX/Zpb3ybTr7gmq/SQPHuhYh85ZyzAgS9bhdbCcmeKbiAdODfLT8OYfiitWlDYqpvArls3WTlgMAt2/dHlh2PKgTf9C3xZ6SfMPf4H/e/DeeBU9YTHXud+QEcczVOuLHD+8pR7Q4ItaA4rCDaqSE1PLB/c/6xOQwt8fF5DmMMYhIFW6P8A/ZWWSDlR+AvKkAZJcuA2ThltOqtHilt0hhX74CCiuz12fJrwJOm/kar23PmgzaH21LlvtP7SQMKjJ4X3Ge4emTB16l/FuniAh5KR716vKe+sOOlsSWpJUK/6+4R1UkQDelaH4e4fnCqZB8Dwv3HAbkMo7II0h+zH2kPQAT7diZzA60R6o3IB2E8hUwxCdENuicNkw3pQ1YR3AhGA9UOqmhVWC7/HngiboCBZbg4KDzga9uu/nfkayyrXNtb5N0LO3U5vStQ1TJl+Jq2M/00nrlp1baILFTn7eSQjGzoIaSRi7V5FYhGUuwcaARMOYt59wXsE8J7+YWJonyFWLkc/AlG4gigJdJjGTQJ1iCbNXk56FOQWmJZOoT+UzxdQWBqaOatItGSBI3E3MxD40KfmrXQpS5vyRCZc+X53Qn98Zz0aZ+yj0f0oB601pJcIT/LFKmMNKINMsffEGFomXauqjNqtKllBO4+/UP+993doltuQfXLtU0pPGBnrW2o4u+P30yuA4vdY6Qe/t07D9YAFHIkqHvfYUeph1RU+m/aBns24EEAD/Rq8s+xcelTUkEMB7JTjY42BnMOokDk8JeBZNLyFmhvio0Caos9puTM95priqjCj9mOOZGMpKz2zmGrAPc+tYh5+nswWBQhRHvKheOR72eAjyc8SFZ4R0pyD4izMyqL5102eIF9TO4GBThIR9FjJtiZ8xg/ChkAXq1tt3w2hzPKZwI736/P0wDo+ZPZIlXSYD3aD2sXrdF5A3j5ZNUJ0UFHXkEF5IverEPDJNIP7+xVywGYJ0ziWSfsUy1NEorjp9+1rvrS37se5DXLePFDlKVFFHE2100cegueWb5QgzqyRdljFPtoQhvCSjo0cKVVaMkK2o4rlyAAA=') 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: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

@ -0,0 +1,356 @@
"v0.4.8 Geetest Inc.";
(function (window) {
"use strict";
if (typeof window === 'undefined') {
throw new Error('Geetest requires browser environment');
}
var document = window.document;
var Math = window.Math;
var head = document.getElementsByTagName("head")[0];
function _Object(obj) {
this._obj = obj;
}
_Object.prototype = {
_each: function (process) {
var _obj = this._obj;
for (var k in _obj) {
if (_obj.hasOwnProperty(k)) {
process(k, _obj[k]);
}
}
return this;
}
};
function Config(config) {
var self = this;
new _Object(config)._each(function (key, value) {
self[key] = value;
});
}
Config.prototype = {
api_server: 'api.geetest.com',
protocol: 'http://',
typePath: '/gettype.php',
fallback_config: {
slide: {
static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
type: 'slide',
slide: '/static/js/geetest.0.0.0.js'
},
fullpage: {
static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
type: 'fullpage',
fullpage: '/static/js/fullpage.0.0.0.js'
}
},
_get_fallback_config: function () {
var self = this;
if (isString(self.type)) {
return self.fallback_config[self.type];
} else if (self.new_captcha) {
return self.fallback_config.fullpage;
} else {
return self.fallback_config.slide;
}
},
_extend: function (obj) {
var self = this;
new _Object(obj)._each(function (key, value) {
self[key] = value;
})
}
};
var isNumber = function (value) {
return (typeof value === 'number');
};
var isString = function (value) {
return (typeof value === 'string');
};
var isBoolean = function (value) {
return (typeof value === 'boolean');
};
var isObject = function (value) {
return (typeof value === 'object' && value !== null);
};
var isFunction = function (value) {
return (typeof value === 'function');
};
var MOBILE = /Mobi/i.test(navigator.userAgent);
var pt = MOBILE ? 3 : 0;
var callbacks = {};
var status = {};
var nowDate = function () {
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
if (month >= 1 && month <= 9) {
month = '0' + month;
}
if (day >= 0 && day <= 9) {
day = '0' + day;
}
if (hours >= 0 && hours <= 9) {
hours = '0' + hours;
}
if (minutes >= 0 && minutes <= 9) {
minutes = '0' + minutes;
}
if (seconds >= 0 && seconds <= 9) {
seconds = '0' + seconds;
}
var currentdate = year + '-' + month + '-' + day + " " + hours + ":" + minutes + ":" + seconds;
return currentdate;
}
var random = function () {
return parseInt(Math.random() * 10000) + (new Date()).valueOf();
};
var loadScript = function (url, cb) {
var script = document.createElement("script");
script.charset = "UTF-8";
script.async = true;
// 对geetest的静态资源添加 crossOrigin
if (/static\.geetest\.com/g.test(url)) {
script.crossOrigin = "anonymous";
}
script.onerror = function () {
cb(true);
};
var loaded = false;
script.onload = script.onreadystatechange = function () {
if (!loaded &&
(!script.readyState ||
"loaded" === script.readyState ||
"complete" === script.readyState)) {
loaded = true;
setTimeout(function () {
cb(false);
}, 0);
}
};
script.src = url;
head.appendChild(script);
};
var normalizeDomain = function (domain) {
// special domain: uems.sysu.edu.cn/jwxt/geetest/
// return domain.replace(/^https?:\/\/|\/.*$/g, ''); uems.sysu.edu.cn
return domain.replace(/^https?:\/\/|\/$/g, ''); // uems.sysu.edu.cn/jwxt/geetest
};
var normalizePath = function (path) {
path = path.replace(/\/+/g, '/');
if (path.indexOf('/') !== 0) {
path = '/' + path;
}
return path;
};
var normalizeQuery = function (query) {
if (!query) {
return '';
}
var q = '?';
new _Object(query)._each(function (key, value) {
if (isString(value) || isNumber(value) || isBoolean(value)) {
q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
}
});
if (q === '?') {
q = '';
}
return q.replace(/&$/, '');
};
var makeURL = function (protocol, domain, path, query) {
domain = normalizeDomain(domain);
var url = normalizePath(path) + normalizeQuery(query);
if (domain) {
url = protocol + domain + url;
}
return url;
};
var load = function (config, send, protocol, domains, path, query, cb) {
var tryRequest = function (at) {
var url = makeURL(protocol, domains[at], path, query);
loadScript(url, function (err) {
if (err) {
if (at >= domains.length - 1) {
cb(true);
// report gettype error
if (send) {
config.error_code = 508;
var url = protocol + domains[at] + path;
reportError(config, url);
}
} else {
tryRequest(at + 1);
}
} else {
cb(false);
}
});
};
tryRequest(0);
};
var jsonp = function (domains, path, config, callback) {
if (isObject(config.getLib)) {
config._extend(config.getLib);
callback(config);
return;
}
if (config.offline) {
callback(config._get_fallback_config());
return;
}
var cb = "geetest_" + random();
window[cb] = function (data) {
if (data.status == 'success') {
callback(data.data);
} else if (!data.status) {
callback(data);
} else {
callback(config._get_fallback_config());
}
window[cb] = undefined;
try {
delete window[cb];
// eslint-disable-next-line no-empty
} catch (e) {
}
};
load(config, true, config.protocol, domains, path, {
gt: config.gt,
callback: cb
}, function (err) {
if (err) {
callback(config._get_fallback_config());
}
});
};
var reportError = function (config, url) {
load(config, false, config.protocol, ['monitor.geetest.com'], '/monitor/send', {
time: nowDate(),
captcha_id: config.gt,
challenge: config.challenge,
pt: pt,
exception_url: url,
error_code: config.error_code
// eslint-disable-next-line no-unused-vars
}, function (err) {
})
}
var throwError = function (errorType, config) {
var errors = {
networkError: '网络错误',
gtTypeError: 'gt字段不是字符串类型'
};
if (typeof config.onError === 'function') {
config.onError(errors[errorType]);
} else {
throw new Error(errors[errorType]);
}
};
var detect = function () {
return window.Geetest || document.getElementById("gt_lib");
};
if (detect()) {
status.slide = "loaded";
}
window.initGeetest = function (userConfig, callback) {
var config = new Config(userConfig);
if (userConfig.https) {
config.protocol = 'https://';
} else if (!userConfig.protocol) {
config.protocol = window.location.protocol + '//';
}
// for KFC
if (userConfig.gt === '050cffef4ae57b5d5e529fea9540b0d1' ||
userConfig.gt === '3bd38408ae4af923ed36e13819b14d42') {
config.apiserver = 'yumchina.geetest.com/'; // for old js
config.api_server = 'yumchina.geetest.com';
}
if (userConfig.gt) {
window.GeeGT = userConfig.gt
}
if (userConfig.challenge) {
window.GeeChallenge = userConfig.challenge
}
if (isObject(userConfig.getType)) {
config._extend(userConfig.getType);
}
jsonp([config.api_server || config.apiserver], config.typePath, config, function (newConfig) {
var type = newConfig.type;
var init = function () {
config._extend(newConfig);
callback(new window.Geetest(config));
};
callbacks[type] = callbacks[type] || [];
var s = status[type] || 'init';
if (s === 'init') {
status[type] = 'loading';
callbacks[type].push(init);
load(config, true, config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {
if (err) {
status[type] = 'fail';
throwError('networkError', config);
} else {
status[type] = 'loaded';
var cbs = callbacks[type];
for (var i = 0, len = cbs.length; i < len; i = i + 1) {
var cb = cbs[i];
if (isFunction(cb)) {
cb();
}
}
callbacks[type] = [];
}
});
} else if (s === "loaded") {
init();
} else if (s === "fail") {
throwError('networkError', config);
} else if (s === "loading") {
callbacks[type].push(init);
}
});
};
})(window);

@ -0,0 +1,53 @@
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1583637102320'); /* IE9 */
src: url('iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAnUAAsAAAAAEXwAAAmIAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCEIAqUYJBKATYCJAMoCxYABCAFhG0HdhuGDlGUL1aT7CuMW3giIDU2lBZVYzMmA6BYbBoB4AIAvsWyNCR1ZYRAPDyt/e/cmdn1b4KYdG+eSCaJRMWjWPVE6ZTyQkZL4P/W2j938EFdZvf2bG4F1UgXqyQeKfBIqpGQb4i2eQ/W/4P6Ek9oYwayNWYT2liwtS7KBWzNL+pBXQarNpcN/6uINB8EdIMjCcASCSSx4cAb7oGJPvjzf6maMe0OLEmbbAtQ1TLH+t2bjwpcoLGH1RZY9/OCWElqEp69Ctt/lUoZgKMJ9DZKJTpb39oLSoVQBWoeMPQUKGtmZQQOra7u2bKID6DSppeMC3hP7x//HA0lSZWJNS/fr5uFyp+QiaT/uZpYwYv1OWFuR8Z+oBDfe93f5Fy5n9R9H1g8BBYZNWVZ128wWubBX4gsc00C/+npXSCHRbkuqBVJ6GSNo/zjETVVPQL/ONPxMyIeNT9HHoWfyzwSPzeo8DQ7IF5OsxdGGZpzMGqgeQOjCpoJxHejmz7ZYhE4AvIjgLIWhPY8ORu1xKf7mSXfEqJWz1lFqu4IsUVclojNFtvZeZZJWCxfTwc225MLw+IOJVu9jIhlZydks0WD2YzVCGJDzZv3bRWtVSPgFM6ZTJDKjCPGrSIdax2dlWgymUleOEnitJKXpPyEKz9ri1IZTMalepIwQ/od1OvB5hq3QpDBcnCLkASgX+Ot37xvI9/Ep9QGC7EF1DMQNJgws3VoBmqIDgCbNXYuVd01lKFvR5CqnITxPpi1Z23gbKCzVCYzT3meaFLvhr5kmfWJDrV+iz8KDo/ogiAUbGoo8SKbLBDTGVnfLmB0BpiGzuEVMxCPpJitnaqi7QPc/I2nEGN6Yb7eUj2voN2qcKHuJxEIVUb7HLspkHcwNKgtsk9XrLzLL2qnl/syr6WbOyffau0FeKUntEq8bGeo+bLsHFVv2IggXAm3BxcRKD4qQJAKpPFED97cegA2kUtJkufhEa5hguf0l6KbAjfRsqUoyisV/sajng+9Tg8sp/2sVPRImnYco2IPWCUbqfgT1lSrrW/7VfTgedn+s9KD1yN30pCRKrXQnuutQZutHvvpsYD3WcPOT9ADl+8K2mVNPnAp9cSF3FNUNnVr4DitOXmXd5BOI2x9VwM0UXRJJ6U6MWENSmG19tlu9tF0g83WkJw35hw1brVGEXTtxk4RtKzrdEcwaG7N+s0ANrPfcth3vHWoM7SOzosxLcPNKMmLbqN7xpoDCR7Zwo26aAOV2mziKs4ZG1WmpTzlGUXJPpWdjSaDmSD1S9EsUryx0/OqplwYy9vjrrPyxLZUyHbAlg4UBYchLJdPRqKr5WY1GnenBYElwhrjZsE0UWXitVtXau4B6TbsEETKTYgqW7ca11skHotAqCLSqu8TzTldome82Wsp5a2lPn03qYZV0HrRfaYtuR2xJ6W3pQhPi8Xyezqa3c8TyNnMNv1zOy+QUBFrb8+Yo3hUiDsBt4OTRPQOeXLy11deWJbq1+OgVrJp8JITpcISj+p5aa9SJ5TKi/o2VKD37G/xu1ERehnQt/bKk+LfZZ2Li48XXAy/KLjDXTbqabKD0+DFjk5YidPrAw8b+ADh3wm7wHd0BAD7RC6YL3I63Np/w+1uHZyLbhdOOgWY4JsQi8aE33JgNDJJFCsKnToP+Ain5hLZJ+oMIeJY0aRZzy41NiRiiRkNlwIzZVR8UKZ6gjrOx1Oy55plBcn64P+UtFEhJJwsTRaRKlKkLQ6TIVFajgW4FybDoZ1loaLkI+ACamn7vt5/X1BiF1y1/8m85UFdNdyaAG6A7phsAKgEUNljkEgZGhJqDY7/TQBeHCUBQ7bM3HwgbX2hUHhqbn3VDsIVYcAoomY1TISKHcKariNO2PbaZgyX5N6oC/OHfNjxXzabV+91hR2d90zbk6bWotfUxzuvZ/mWE2AIgaUsOMde5snf/NxBElNVPnB045As1kLdjhRM0wCJH8X384+4FiQpzJ3tlufDTZydm1/k+xI80DKzs7INNcWemo9FflMbU70a5MXSzpiaiJeyfPP74Jjso8dKNQbLzDuiJKZjVebQuSV4UtxHuc94mSgx1kfuDXSn54cWlI/IC901zzN/UFheSAz85UPeQ4W6cPeJ5Z7Qz3V1Yf4DxUwcjVMOHNy/Z7VCcnVNu+RhrCZm2Pgc/PekHiDmSxhpaVX/Zpb3ybTr7gmq/SQPHuhYh85ZyzAgS9bhdbCcmeKbiAdODfLT8OYfiitWlDYqpvArls3WTlgMAt2/dHlh2PKgTf9C3xZ6SfMPf4H/e/DeeBU9YTHXud+QEcczVOuLHD+8pR7Q4ItaA4rCDaqSE1PLB/c/6xOQwt8fF5DmMMYhIFW6P8A/ZWWSDlR+AvKkAZJcuA2ThltOqtHilt0hhX74CCiuz12fJrwJOm/kar23PmgzaH21LlvtP7SQMKjJ4X3Ge4emTB16l/FuniAh5KR716vKe+sOOlsSWpJUK/6+4R1UkQDelaH4e4fnCqZB8Dwv3HAbkMo7II0h+zH2kPQAT7diZzA60R6o3IB2E8hUwxCdENuicNkw3pQ1YR3AhGA9UOqmhVWC7/HngiboCBZbg4KDzga9uu/nfkayyrXNtb5N0LO3U5vStQ1TJl+Jq2M/00nrlp1baILFTn7eSQjGzoIaSRi7V5FYhGUuwcaARMOYt59wXsE8J7+YWJonyFWLkc/AlG4gigJdJjGTQJ1iCbNXk56FOQWmJZOoT+UzxdQWBqaOatItGSBI3E3MxD40KfmrXQpS5vyRCZc+X53Qn98Zz0aZ+yj0f0oB601pJcIT/LFKmMNKINMsffEGFomXauqjNqtKllBO4+/UP+993doltuQfXLtU0pPGBnrW2o4u+P30yuA4vdY6Qe/t07D9YAFHIkqHvfYUeph1RU+m/aBns24EEAD/Rq8s+xcelTUkEMB7JTjY42BnMOokDk8JeBZNLyFmhvio0Caos9puTM95priqjCj9mOOZGMpKz2zmGrAPc+tYh5+nswWBQhRHvKheOR72eAjyc8SFZ4R0pyD4izMyqL5102eIF9TO4GBThIR9FjJtiZ8xg/ChkAXq1tt3w2hzPKZwI736/P0wDo+ZPZIlXSYD3aD2sXrdF5A3j5ZNUJ0UFHXkEF5IverEPDJNIP7+xVywGYJ0ziWSfsUy1NEorjp9+1rvrS37se5DXLePFDlKVFFHE2100cegueWb5QgzqyRdljFPtoQhvCSjo0cKVVaMkK2o4rlyAAA=') format('woff2'),
url('iconfont.woff?t=1583637102320') format('woff'),
url('iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('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";
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,72 @@
{
"id": "",
"name": "",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "217325",
"name": "android",
"font_class": "android",
"unicode": "e61d",
"unicode_decimal": 58909
},
{
"icon_id": "1261088",
"name": "android",
"font_class": "android1",
"unicode": "e616",
"unicode_decimal": 58902
},
{
"icon_id": "1410210",
"name": "android",
"font_class": "android2",
"unicode": "e62a",
"unicode_decimal": 58922
},
{
"icon_id": "1420800",
"name": "IOS",
"font_class": "ios",
"unicode": "e60c",
"unicode_decimal": 58892
},
{
"icon_id": "1546553",
"name": "IOS",
"font_class": "IOS",
"unicode": "e68c",
"unicode_decimal": 59020
},
{
"icon_id": "2075064",
"name": "ios",
"font_class": "ios1",
"unicode": "e601",
"unicode_decimal": 58881
},
{
"icon_id": "2325952",
"name": "ios",
"font_class": "ios2",
"unicode": "e64b",
"unicode_decimal": 58955
},
{
"icon_id": "3965518",
"name": "Android",
"font_class": "Android",
"unicode": "e6d4",
"unicode_decimal": 59092
},
{
"icon_id": "13302922",
"name": "android",
"font_class": "android3",
"unicode": "e625",
"unicode_decimal": 58917
}
]
}

@ -0,0 +1,53 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
ascent="896"
descent="-128"
/>
<missing-glyph />
<glyph glyph-name="android" unicode="&#58909;" d="M269.401218 547.905l484.587674 0 0-11.106965c0-120.589472 0.023536-241.183038-0.011256-361.76944-0.011256-40.771589-21.756513-62.327534-62.705134-62.342884-10.184967 0-20.37198 0-29.858028 0 0-43.198871 0.467651-85.178985-0.171915-127.141702-0.397043-26.024723-22.889313-47.623647-48.671513-48.568159-27.686573-1.013073-52.970422 18.175965-53.951773 44.760436-1.579985 42.982954-0.425695 86.064145-0.425695 130.026402l-92.891643 0c0-17.075911 0.033769-34.169217-0.005117-51.256384-0.055259-25.179472 0.483001-50.374294-0.428765-75.523067-0.896416-24.611537-20.249183-44.734854-44.115754-47.749514-25.325805-3.202948-49.012274 11.464099-56.531528 35.426861-1.660826 5.300726-2.638083 11.033287-2.675946 16.571421-0.234337 35.778878-0.174985 71.555709-0.064468 107.33561 0.013303 4.910846 0.836041 9.815553 1.417279 16.121166-12.051477 0-23.86657-0.073678-35.68678 0.011256-34.978652 0.254803-57.743122 22.928199-57.767682 58.021461-0.100284 122.571616-0.039909 245.152443-0.042979 367.729176L269.400195 547.905 269.401218 547.905zM754.197646 583.671599 268.922311 583.671599c4.508687 83.888596 52.172243 136.038326 123.015731 173.068707-7.217379 10.940167-14.230096 21.587668-21.263279 32.217772-5.119601 7.730055-10.30674 15.407921-15.398712 23.150256-3.591805 5.460362-9.344832 11.614525-1.36509 17.062608 8.616238 5.885034 12.135388-2.347464 15.941064-7.959276 9.094122-13.428847 18.064424-26.946722 27.064402-40.440038 11.086499-16.617469 11.078313-16.613376 30.132274-11.05887 60.601216 17.662265 120.804367 16.659425 180.590008-3.89061 2.139733-0.738827 4.331655-1.331321 7.135514-2.183735 12.395308 18.558681 24.680099 36.96182 36.96489 55.358819 1.471514 2.206248 2.910283 4.427846 4.428869 6.598278 3.459798 4.948709 7.856945 7.618515 13.384845 3.53757 5.008061-3.693112 4.154623-8.404414 0.994654-13.125949-10.67513-15.95232-21.285792-31.954783-31.911804-47.940872-2.371-3.571339-4.705162-7.167237-7.42204-11.304464 70.458725-36.700877 118.544907-88.950891 122.990149-173.090197L754.197646 583.671599zM641.381208 678.031687c0.150426 14.033621-11.603269 25.974581-25.713638 26.131147-14.242376 0.159636-25.844621-11.30651-25.957185-25.65224-0.116657-14.520715 11.002588-25.810852 25.501814-25.904996C629.645932 652.518616 641.236921 663.776008 641.381208 678.031687L641.381208 678.031687zM433.369663 678.65488c-0.187265 14.328333-11.883655 25.780153-26.066679 25.512047-14.080693-0.260943-25.824155-12.32777-25.576515-26.275433 0.251733-14.231119 11.936867-25.453718 26.327622-25.300222C422.521594 652.744767 433.556928 664.105512 433.369663 678.65488L433.369663 678.65488zM892.258025 385.416256c-0.004093-36.07359 0.095167-72.149227-0.028653-108.219747-0.11154-31.752168-21.937638-54.680367-51.753711-54.658877-29.79049 0.017396-51.8059 23.100114-51.836599 54.654784-0.067538 73.140811-0.149403 146.276505 0.061398 219.415268 0.075725 26.923186 19.805068 48.538483 45.833885 51.248198 27.095102 2.819209 50.834782-13.987572 56.574506-40.425711 1.105171-5.102205 1.090844-10.510378 1.105171-15.782451 0.089028-35.413558 0.045025-70.821999 0.040932-106.234533L892.258025 385.416256zM234.306932 385.412163c0-37.7293 0.35918-75.465762-0.132006-113.184829-0.302899-23.12672-16.584724-42.608423-38.748513-48.26526-21.668509-5.526877-45.432749 3.454682-56.523341 23.153326-4.52199 8.035-7.7935 17.987676-7.869225 27.101241-0.624217 74.130348-0.546446 148.266835-0.252757 222.399229 0.116657 29.218462 23.174815 51.633984 51.857065 51.633984 28.801976-0.004093 51.323922-22.078855 51.64831-51.639101 0.414439-37.065174 0.102331-74.132394 0.102331-111.199615-0.028653 0-0.055259 0-0.084934 0L234.306932 385.411139zM234.306932 385.412163" horiz-adv-x="1024" />
<glyph glyph-name="android1" unicode="&#58902;" d="M391.428571 620q9.142857 0 15.714286 6.571429T413.714286 642.285714t-6.571429 15.714286T391.428571 664.571429t-15.428571-6.571429-6.285714-15.714286 6.285714-15.714285 15.428571-6.571429z m241.142858 0q9.142857 0 15.428571 6.571429t6.285714 15.714285-6.285714 15.714286-15.428571 6.571429-15.714286-6.571429T610.285714 642.285714t6.571429-15.714285T632.571429 620zM168.571429 514.857143q24 0 41.142857-17.142857t17.142857-41.142857v-245.714286q0-24.571429-16.857143-41.714286t-41.428571-17.142857-41.714286 17.142857-17.142857 41.714286V456.571429q0 24 17.142857 41.142857t41.714286 17.142857z m605.714285-10.857143v-380.571429q0-26.285714-18.285714-44.571428t-44-18.285714h-42.857143v-129.714286q0-24.571429-17.142857-41.714286t-41.714286-17.142857-41.714285 17.142857-17.142858 41.714286v129.714286H472.571429v-129.714286q0-24.571429-17.142858-41.714286t-41.714285-17.142857q-24 0-41.142857 17.142857t-17.142858 41.714286l-0.571428 129.714286h-42.285714q-26.285714 0-44.571429 18.285714t-18.285714 44.571428V504h524.571428zM641.714286 735.428571q61.142857-31.428571 97.714285-87.714285t36.571429-123.142857H247.428571q0 66.857143 36.571429 123.142857T382.285714 735.428571l-40.571428 74.857143q-4 7.428571 2.857143 11.428572 7.428571 3.428571 11.428571-3.428572l41.142857-75.428571q54.285714 24 114.857143 24t114.857143-24l41.142857 75.428571q4 6.857143 11.428571 3.428572 6.857143-4 2.857143-11.428572z m272.571428-278.857142v-245.714286q0-24.571429-17.142857-41.714286t-41.714286-17.142857q-24 0-41.142857 17.142857t-17.142857 41.714286V456.571429q0 24.571429 17.142857 41.428571t41.142857 16.857143q24.571429 0 41.714286-16.857143t17.142857-41.428571z" horiz-adv-x="1024" />
<glyph glyph-name="android2" unicode="&#58922;" d="M256 128c0-25.6 17.066667-42.666667 42.666667-42.666667h42.666666v-149.333333c0-34.133333 29.866667-64 64-64s64 29.866667 64 64V85.333333h85.333334v-149.333333c0-34.133333 29.866667-64 64-64s64 29.866667 64 64V85.333333h42.666666c25.6 0 42.666667 17.066667 42.666667 42.666667V554.666667H256v-426.666667zM149.333333 554.666667C115.2 554.666667 85.333333 524.8 85.333333 490.666667v-298.666667c0-34.133333 29.866667-64 64-64S213.333333 157.866667 213.333333 192v298.666667C213.333333 524.8 183.466667 554.666667 149.333333 554.666667z m725.333334 0c-34.133333 0-64-29.866667-64-64v-298.666667c0-34.133333 29.866667-64 64-64s64 29.866667 64 64v298.666667c0 34.133333-29.866667 64-64 64z m-213.333334 247.466666L716.8 857.6c8.533333 8.533333 8.533333 21.333333 0 29.866667-8.533333 8.533333-21.333333 8.533333-29.866667 0l-64-64C593.066667 844.8 554.666667 853.333333 512 853.333333s-81.066667-8.533333-115.2-25.6L337.066667 891.733333c-8.533333 4.266667-25.6 4.266667-34.133334 0-4.266667-8.533333-4.266667-25.6 0-34.133333l55.466667-55.466667C298.666667 755.2 256 682.666667 256 597.333333h512c0 85.333333-42.666667 162.133333-106.666667 204.8zM426.666667 682.666667H384V725.333333h42.666667v-42.666666z m213.333333 0h-42.666667V725.333333h42.666667v-42.666666z" horiz-adv-x="1024" />
<glyph glyph-name="ios" unicode="&#58892;" d="M521.996 659.402c13.596 0.316 26.587 2.344 38.944 5.874l1.477-1.023c25.485 8.525 47.394 21.707 65.751 38.675l-25.33-22.506c28.822 15.94 52.712 37.447 70.922 63.249l-36.429-39.535c28.139 24 50.335 54.116 64.645 88.349l-34.464-58.168c22.406 27.513 39.057 60.636 47.571 96.894l-25.474-60.785c14.141 25.287 23.872 54.935 27.305 86.475l-11.137-47.132c6.314 18.235 10.235 39.273 10.832 61.141-0.92-14.951-0.492-7.646-0.492-0.24 0 7.406-0.428 14.711-1.261 21.894-114.436-13.762-202.632-110.063-202.632-226.963 0-1.99 0.026-3.974 0.076-5.952zM813.027 279.983c-9.291 23.371-14.678 50.452-14.678 78.791 0 2.616 0.046 5.222 0.137 7.816 0.877 22.699 5.309 44.464 12.755 64.776 8.765 26.429 27.096 55.391 50.856 78.871 1.828 9.48 42.301 41.542 89.752 59.323-84.33 32.038-144.712 69.372-214.043 76.759 0.404 0.843-15.633 2.529-32.046 2.637l-53.878-0.333-15.629-4.312-11.857-3.773-15.091-5.389-10.779-8.623-17.785-7.006-14.552-5.928-10.24-3.773-12.396-3.773h-7.006c-2.507-0.304-5.41-0.478-8.354-0.478s-5.847 0.174-8.699 0.511c-2.079-0.302-4.891-0.455-7.739-0.455-2.848 0-5.659 0.153-8.427 0.452l-5.585-0.030-11.857 3.234-8.623 3.234-12.396 3.234-20.48 8.084-8.084 2.695-14.552 5.389-9.701 3.234-13.474 4.312h-10.24l-14.013 2.695h-10.779l-12.935 3.773c-35.215-0.199-68.68-7.463-99.123-20.442-100.455-45.409-170.936-147.27-170.936-265.593 0-6.498 0.213-12.947 0.631-19.339l-0.046-53.025c0-4.312 0-19.402 3.773-29.103s0-10.779 3.234-16.168 3.234-17.246 5.389-25.869 3.234-11.318 4.851-16.707 4.312-16.168 7.006-24.253 4.312-11.857 6.467-17.785l8.084-22.636 7.545-17.246c3.234-7.006 5.928-14.552 9.162-21.558l8.084-16.168 10.779-21.019 9.162-15.629 11.318-19.402 21.558-32.337 10.24-13.474 12.396-15.629 10.779-12.396 12.396-13.474 11.318-10.779 12.396-11.318 11.318-8.623 12.396-9.162 11.318-6.467 11.857-6.467 10.779-3.773 11.857-3.773 14.013-4.312h12.396c17.291 0.666 33.606 3.944 48.855 9.45 35.686 19.327 84.485 32.203 136.187 33.26-11.243-0.381-6.541-0.215-1.8-0.215 4.741 0 9.443-0.166 14.101-0.492l7.996 0.035 14.552-3.234 9.162-2.695 11.318-3.773 9.701-3.773 14.013-5.928 12.396-5.389 8.084-2.695 10.779-3.773h45.811c39.742 3.516 74.688 21.631 99.914 48.874 71.609 68.094 126.848 162.124 151.674 267.874-5.067-48.749 17.64 18.78 26.966 90.511-106.339-115.262-156.75-58.206-177.238 11.642z" horiz-adv-x="1024" />
<glyph glyph-name="IOS" unicode="&#59020;" d="M737.007 358.636c-1.092 104.007 84.833 153.888 88.595 156.316-48.181 70.512-123.305 80.221-150.126 81.435-63.958 6.432-124.761-37.622-157.165-37.622-32.404 0-82.405 36.652-135.441 35.681-69.663-1.092-133.863-40.535-169.787-102.915-72.453-125.732-18.569-311.903 52.065-413.727 34.467-49.881 75.609-105.828 129.616-103.887 51.943 2.063 71.604 33.618 134.47 33.618s80.464-33.618 135.563-32.646c55.948 1.092 91.387 50.851 125.611 100.852 39.564 57.89 55.948 113.959 56.798 116.63-1.214 0.729-109.105 41.991-110.197 166.267zM633.605 663.742c28.641 34.71 47.938 83.012 42.72 131.072-41.264-1.699-91.265-27.428-120.878-62.138-26.457-30.826-49.881-79.978-43.569-127.067 45.997-3.641 93.085 23.423 121.727 58.132z" horiz-adv-x="1024" />
<glyph glyph-name="ios1" unicode="&#58881;" d="M615.913 712.238c31.803 40.968 55.898 98.81 47.218 157.832-52.08-3.61-112.838-36.802-148.25-79.991-32.36-39.164-58.954-97.353-48.607-153.876 56.94-1.805 115.545 32.15 149.639 76.035zM896.859 156.8c-22.776-50.481-33.677-73.117-62.98-117.835-40.97-62.496-98.603-140.266-170.055-140.822-63.467-0.694-79.854 41.386-165.957 40.9s-104.156-41.733-167.693-41.107c-71.451 0.695-126.03 70.827-166.929 133.32-114.364 174.568-126.376 379.48-55.897 488.498 50.203 77.354 129.294 122.558 203.661 122.558 75.688 0 123.253-41.593 185.956-41.593 60.758 0 97.768 41.663 185.26 41.663 66.245 0 136.447-36.109 186.441-98.603-163.805-89.922-137.278-324.275 28.192-387.047v0.068h0.001z" horiz-adv-x="1024" />
<glyph glyph-name="ios2" unicode="&#58955;" d="M812.2 345.2c-7.2 96.1 56.4 156.8 116.4 199-56.4 50.6-105.4 99.5-209.1 104.5-83.6 3.4-121.8-43.8-200-45.5-72.7-1.7-67.3 52.3-218.2 37.1C170.5 626.8 55.9 505.4 65 335.1c9.1-183.8 147.3-438.3 276.3-438.3 90.9 1.7 112.7 42.1 187.3 42.1 92.7 0 114.5-50.6 187.3-40.5C841.4-84.7 959.5 99 959.5 158c-69.1 33.8-138.2 91.1-147.3 187.2z m-105.4 526C534.1 849.3 485 702.6 494.1 635.2c132.7-6.7 232.7 123.1 212.7 236z" horiz-adv-x="1024" />
<glyph glyph-name="Android" unicode="&#59092;" d="M512 192.7c-41.8 0-75.6 34-75.6 75.6V499.7c0 41.6 33.8 75.6 75.6 75.6 20.2 0 39.1-7.8 53.5-22.1 14.3-14.3 22.1-33.3 22.1-53.5v-231.5c0-20.2-7.8-39.1-22.1-53.5-14.4-14-33.3-22-53.5-22M243.1 400c-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.4 30.6 0 55.2 24.8 55.2 55.4V344.9c0 30.3-24.8 55.1-55.2 55.1m593-11.4H297.3V409c0 83.4 45.5 160.9 119.9 206.9l-28.9 52.5c-3.6 6.1-4.4 14.1-2.4 21.2 2.2 7.1 7.1 13.4 13.6 16.8 4.1 2.4 8.8 3.6 13.4 3.6 10.2 0 19.7-5.6 24.6-14.6l30.4-55.2c31.4 11.4 64.4 17.3 98.7 17.3 34.8 0 68.1-5.8 99.4-17.5l30.1 55.4c4.9 9 14.3 14.6 24.6 14.6 4.6 0 9.2-1.2 13.4-3.4 6.8-3.6 11.7-9.7 13.6-17 2.2-7.3 1.2-14.8-2.4-21.4l-28.9-52.8c74.4-46 119.9-123.5 119.9-206.9v-19.9h-0.2zM633.8 442.7l38.7 70.5c1.9 3.6 0.7 8.5-2.9 10.5-3.9 1.9-8.5 0.7-10.5-3.2l-38.9-71.2c-32.8 14.6-69.5 22.9-108.2 22.6-38.7 0-75.4-8-107.9-22.6l-38.9 71c-1.9 3.6-6.8 5.1-10.5 3.2s-5.1-6.8-3.2-10.5l38.7-70.5C314.3 403.4 263 329 263 243.4h498c-0.3 85.8-51.3 160.2-127.2 199.3M398.5 333.8c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9s-9.3-20.9-20.9-20.9m226.8 0c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9s-9.5-20.9-20.9-20.9M419.3 66c-41.6 0-75.6 33.8-75.6 75.6V243.7h-19.9c-21.2 0-41.1 8.3-56.2 23.1-15.1 15.1-23.3 34.8-23.1 56.2V702h535.1v-379c0-43.8-35.5-79.3-79.3-79.3h-19.9v-102.1c0-41.6-33.8-75.6-75.6-75.6-20.2 0-39.1 7.8-53.5 22.1-14.3 14.3-22.1 33.3-22.1 53.5V243.7h-34v-102.1c-0.3-41.6-34.4-75.6-75.9-75.6M189 307.7c0-32.6 26.3-59.1 59.1-59.1h40.1V126c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.4V248.8h74.6v-122.5c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V248.8H624c32.6 0 59.1 26.5 59.1 59.1V666.8H188.8l0.2-359.1z m570.6 6.8c-41.6 0-75.6 33.8-75.6 75.6V621.5c0 41.8 33.8 75.6 75.6 75.6s75.6-33.8 75.6-75.6V390c0-41.7-33.8-75.5-75.6-75.5M512 554.9c-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V499.7c0 30.4-24.6 55.2-55.2 55.2M188.2 542.2c-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V487c-0.1 30.4-24.9 55.2-55.2 55.2m445.6 207.9l38.7 70.5c1.9 3.6 0.7 8.3-2.9 10.5-3.6 1.9-8.5 0.7-10.5-3.2l-38.9-71.2c-32.8 14.6-69.5 22.9-108.4 22.9-38.7 0-75.4-8-107.9-22.6L365 828c-1.9 3.6-6.8 5.1-10.5 3.2-3.6-1.9-5.1-6.8-3.2-10.5l38.7-70.5c-75.9-39.1-127.2-113.5-127.2-199.1h497.9c0 85.4-51 159.8-126.9 199m-235.3-109c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9 0-11.4-9.3-20.9-20.9-20.9m226.8 0c-11.4 0-20.9 9.2-20.9 20.9 0 11.4 9.2 20.9 20.9 20.9 11.4 0 20.9-9.5 20.9-20.9 0-11.4-9.5-20.9-20.9-20.9M264.7 531.7v-358.9c0-32.6 26.3-59.1 59.1-59.1h40.1v-122.5c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.4V114H549v-122.5c0-30.4 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V114h40.4c32.6 0 59.1 26.3 59.1 59.1V532l-494.2-0.3zM891 487c0 30.6-24.8 55.2-55.2 55.2-30.6 0-55.2-24.8-55.2-55.2v-231.5c0-30.6 24.8-55.2 55.2-55.2 30.6 0 55.2 24.8 55.2 55.2V487z" horiz-adv-x="1024" />
<glyph glyph-name="android3" unicode="&#58917;" d="M633.472 667.434667h-40.533333v40.533333h40.533333m-202.496-40.533333h-40.490667v40.533333h40.533334m223.914666 74.453333l53.034667 53.077334a20.778667 20.778667 0 0 1 0 28.757333 20.778667 20.778667 0 0 1-28.757333 0L619.306667 804.266667a244.650667 244.650667 0 0 1-107.349334 25.130666c-38.826667 0-75.306667-9.344-107.690666-25.514666L343.893333 864.256a20.309333 20.309333 0 0 1-28.373333 0 20.053333 20.053333 0 0 1 0-28.757333L368.64 782.421333C308.266667 737.877333 269.013333 667.434667 269.013333 586.453333h485.973334c0 80.981333-40.533333 151.850667-100.053334 195.968m201.301334-236.501333a60.757333 60.757333 0 0 1-60.757334-60.714667v-283.477333a60.757333 60.757333 0 0 1 60.757334-60.757333 60.757333 60.757333 0 0 1 60.714666 60.757333V485.205333a60.757333 60.757333 0 0 1-60.714666 60.714667m-688.469334 0a60.757333 60.757333 0 0 1-60.757333-60.714667v-283.477333a60.757333 60.757333 0 0 1 60.757333-60.757333 60.757333 60.757333 0 0 1 60.714667 60.757333V485.205333a60.757333 60.757333 0 0 1-60.714667 60.714667m101.205334-404.949333a40.533333 40.533333 0 0 1 40.533333-40.533334h40.490667v-141.696a60.757333 60.757333 0 0 1 60.714666-60.757333 60.757333 60.757333 0 0 1 60.757334 60.757333v141.738667h80.981333v-141.738667a60.757333 60.757333 0 0 1 60.757333-60.757333 60.757333 60.757333 0 0 1 60.757334 60.757333v141.738667h40.490666a40.533333 40.533333 0 0 1 40.533334 40.490667V545.92H268.970667z" horiz-adv-x="1024" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 16 KiB

@ -0,0 +1,424 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px"
width="800px" height="398.17px" viewBox="0 0 800 398.17" enable-background="new 0 0 800 398.17"
xml:space="preserve">
<g>
<path d="M361.973,356.763c2.58,2.149,5.508,3.111,8.413,2.784c0.548-0.067,1.06-0.186,1.579-0.328
c7.256-2.038,12.098-11.919,11.271-23.021c-0.405-5.709-2.334-10.857-5.214-14.585l-0.838,1.632
c2.425,3.384,3.98,8.007,4.361,13.08c0.836,10.958-4.122,20.7-11.342,21.536c-2.456,0.287-4.932-0.567-7.142-2.419
c-3.96-3.274-6.614-9.314-7.142-16.052c0.005-0.414,0.003-0.809-0.019-1.21l-1.672,0.091c0.015,0.394-0.027,0.801,0.01,1.222
C354.785,346.729,357.667,353.167,361.973,356.763z"/>
<path d="M360.603,359.128c2.879,2.514,6.147,3.721,9.482,3.429c0.713-0.035,1.425-0.202,2.133-0.402
c8.068-2.242,13.556-13.286,12.84-25.626c-0.435-6.649-2.638-12.685-5.951-16.988l-0.84,1.623c2.863,3.969,4.712,9.469,5.08,15.473
c0.761,12.524-5.246,23.59-13.422,24.21c-2.826,0.258-5.684-0.794-8.23-3.025c-4.475-3.895-7.454-10.895-7.904-18.701
c-0.041-0.288-0.013-0.547-0.021-0.827l-1.689,0.069c0.018,0.276-0.029,0.559,0.015,0.836
C352.565,347.489,355.776,354.896,360.603,359.128z"/>
<path d="M363.352,354.406c2.287,1.779,4.855,2.504,7.402,2.102c0.303-0.073,0.653-0.136,0.985-0.219
c6.463-1.837,10.637-10.616,9.707-20.423c-0.436-4.779-2.076-9.078-4.529-12.165l-0.853,1.68c1.958,2.776,3.328,6.517,3.707,10.627
c0.871,9.284-3.246,17.87-9.307,18.801c-2.062,0.355-4.155-0.263-6.094-1.742c-3.392-2.664-5.725-7.679-6.293-13.444
c-0.041-0.52-0.024-1.007-0.037-1.52l-1.68,0.07c0.023,0.544-0.006,1.042,0.042,1.609
C357.005,345.977,359.583,351.463,363.352,354.406z"/>
<path d="M364.701,352.049c2.008,1.415,4.212,1.915,6.399,1.431c0.153-0.022,0.258-0.057,0.406-0.091
c5.614-1.602,9.17-9.449,8.144-17.861c-0.448-3.859-1.85-7.234-3.802-9.707l-0.891,1.682c1.544,2.168,2.645,4.997,3.01,8.211
c0.094,0.632,0.103,1.285,0.145,1.948c-0.061-0.82-0.128-1.657-0.252-2.494c-0.473-2.88-1.585-5.39-3.103-7.259l-0.868,1.699
c0.901,1.339,1.621,2.948,2.057,4.817c-0.459-1.778-1.251-3.326-2.221-4.472l-0.866,1.621c0.472,0.705,0.899,1.527,1.186,2.444
c-0.33-0.835-0.815-1.547-1.368-2.136l-0.818,1.56c-1.516-1.452-3.743-1.181-4.941,0.794c-0.663,1.544-0.508,3.511-0.508,3.511
l-1.915,0.066c0.033,1.104,0.182,2.175,0.572,3.214c1.069,3.029,3.462,4.649,5.748,4.015c0.521-0.14,1.031-0.422,1.493-0.766
c1.118-0.891,1.848-2.336,2.229-3.945c-0.447,2.563-1.685,4.698-3.41,5.517c-1.103,0.528-2.225,0.449-3.283-0.213
c-1.544-0.925-2.794-2.878-3.341-5.356c-0.157-0.806-0.241-1.637-0.272-2.458l-1.685,0.066c0.022,0.925,0.093,1.841,0.315,2.791
c0.683,2.905,2.179,5.264,4.12,6.425c1.306,0.765,2.674,0.984,4.014,0.601c0.301-0.076,0.579-0.199,0.844-0.316
c2.531-1.239,4.195-4.409,4.518-8.023c-0.321,4.585-2.372,8.486-5.417,9.447l-0.173,0.06c-1.281,0.363-2.645,0.114-3.918-0.693
c-2.207-1.425-3.894-4.455-4.473-8.104c-0.138-0.725-0.19-1.446-0.199-2.147l-1.688,0.044c0.021,0.797,0.078,1.607,0.22,2.354
c0.661,4.13,2.608,7.574,5.26,9.292c1.637,1.066,3.491,1.374,5.278,0.895l0.206-0.066c4.048-1.301,6.767-6.688,6.656-12.672
c0.153,6.721-2.794,12.774-7.093,13.98l-0.302,0.073c-1.698,0.378-3.457-0.001-5.046-1.167c-2.822-2.037-4.871-6.057-5.455-10.79
c-0.053-0.63-0.066-1.249-0.094-1.859l-1.677,0.074c0.021,0.647,0.025,1.313,0.101,1.969
C359.185,345.251,361.503,349.744,364.701,352.049z M371.276,342.924c-1.633,1.3-3.683,0.144-4.624-2.463
c-0.038-0.13-0.043-0.24-0.09-0.383c0.201,0.504,0.45,1.033,0.773,1.433c0.909,1.152,2.166,1.66,3.346,1.329
c0.429-0.126,0.781-0.331,1.121-0.609C371.638,342.451,371.481,342.739,371.276,342.924z"/>
<path d="M347.813,338.653c0.316,8.156,2.699,15.746,6.726,21.376c4.103,5.724,9.391,8.784,14.915,8.57
c1.083-0.035,2.203-0.196,3.28-0.524c9.837-2.74,16.5-15.753,15.887-30.89c-0.338-8.134-2.715-15.707-6.749-21.341
c-4.091-5.745-9.378-8.792-14.901-8.578c-1.106,0.042-2.18,0.2-3.28,0.513C353.864,310.524,347.166,323.546,347.813,338.653z
M364.15,309.403c0.97-0.27,1.934-0.408,2.888-0.457c4.381-0.161,8.584,2.14,12.119,6.297c0.872,0.925,1.348,1.594,1.348,1.594
c3.838,5.382,6.127,12.623,6.45,20.411c0.573,14.147-5.75,26.679-14.689,29.2c-0.93,0.253-1.896,0.422-2.848,0.446
c-4.927,0.193-9.736-2.605-13.495-7.87c-3.848-5.364-6.112-12.617-6.447-20.381C348.895,324.462,355.217,311.904,364.15,309.403z"
/>
<path d="M359.23,361.48c3.193,2.89,6.838,4.295,10.526,4.105c0.932-0.065,1.825-0.201,2.722-0.471
c9.001-2.543,15.037-14.406,14.341-28.259c-0.357-7.567-2.787-14.537-6.57-19.51l-0.849,1.668
c3.306,4.648,5.399,11.017,5.742,17.922c0.619,12.865-5.005,24.253-13.13,26.541c-0.785,0.22-1.551,0.355-2.371,0.404
c-3.228,0.184-6.436-1.101-9.279-3.656c-5.056-4.559-8.32-12.53-8.731-21.354c-0.024-0.149,0.001-0.316,0.018-0.482l-1.711,0.085
c-0.007,0.17-0.001,0.308,0.018,0.468C350.383,348.2,353.866,356.635,359.23,361.48z"/>
<path d="M784.963,25.941C759.762,14.89,728.636,4.944,716.374,1.818c-15.066-3.817-38.277-1.438-58.987,5.41
c-2.218,0.573-4.4,1.124-6.621,1.723L589.491,29.87l-0.006-0.216L297.214,59.633c-0.783-0.652-1.711-0.995-2.817-0.832
c-2.222,0.364-4.46,2.25-5.469,4.479L64.79,209.054L1.817,230.553c-0.68,0.234-1.226,0.714-1.549,1.371
c-0.307,0.64-0.35,1.346-0.129,1.996c0.466,1.353,1.862,2.122,3.261,1.732l6.125-2.066c12.542-2.859,16.711-4.192,34.069,2.538
c18.29,7.057,30.995,10.653,38.673,12.698c7.65,2.05,14.429,2.278,29.606-1.574c2.485-0.626,10.035-2.861,20.795-6.146
l27.979-7.967c10.183-3.137,21.293-6.587,32.869-10.162c-0.496,3.256-0.741,6.587-0.765,9.885c-0.107,2.786,0.347,8.206,0.358,8.3
c0.764,6.788,2.395,13.221,4.74,18.675c2.074,4.818,4.958,9.022,8.249,12.579c6.252,7.771,13.832,12,21.709,13.813
c0.665,0.22,1.32,0.412,1.962,0.554l1.666,43.199c-2.745-1.083-5.594-1.575-8.472-1.325c-1.151,0.103-2.283,0.301-3.407,0.607
c-12.031,3.375-19.927,18.963-18.4,36.238c0.815,9.228,4.143,17.691,9.352,23.84c5.285,6.229,11.894,9.335,18.602,8.765
c1.136-0.084,2.28-0.297,3.378-0.597c10.927-3.068,18.362-16.197,18.525-31.514l91.221-19.138
c1.34,6.657,3.908,12.736,7.625,17.593c5.269,6.847,12.04,10.516,19.108,10.223c1.516-0.047,3-0.271,4.47-0.694
c12.497-3.507,21.017-19.151,20.269-37.175c-0.792-20.385-13.257-36.539-27.741-35.999c-1.509,0.061-3,0.279-4.468,0.691
c-4.101,1.158-7.782,3.654-10.822,7.085l-2.85-24.833c2.662-0.088,4.906-0.172,6.44-0.164c16.822-0.171,31.929-1.904,57.733-6.72
c22.583-4.208,117.348-24.725,154.363-32.992l-0.371,0.099c2.707-0.45,2.589,1.957,3.363,4.097c0.751,2.151,1.318,3.103,2.26,2.994
c0.877-0.093,1.733-1.432,0.655-8.551c4.731-1.047,7.838-1.776,8.726-2.015c7.347-2.083,10.267-13.274,9.367-23.235
c-0.056-0.583-0.124-1.278-0.198-1.996c31.979-5.857,75.042-13.764,81.744-15.129c2.922-0.585,5.166-1.122,6.907-1.622
c4.206-1.167,5.562-2.131,5.562-3.998c0.002-0.474-0.208-0.91-0.591-1.221c-1.457-1.264-6.696-1.731-35.099,3.201
c-6.222,1.038-12.971,2.23-20.001,3.457c-0.89,0.159-1.778,0.296-2.668,0.463l0.065-0.201l-39.525-10.802
c-1.331-7.564-2.913-14.629-4.576-18.691c-0.275-0.649-0.555-1.338-0.815-1.961c-1.749-4.419-3.392-8.557-7.332-9.865
c-4.066-1.354-10.015,0.586-18.723,6.007c-5.047,3.153-10.131,7.378-14.889,11.912l-0.096-0.024l-0.03,0.134
c-12.813,12.243-23.324,26.709-25.213,29.395c-0.733-0.12-2.112-0.326-3.944-0.618c-0.25-0.029-0.529-0.092-0.725-0.144
l-0.01,0.064c-10.572-1.662-34.937-5.363-57.426-8.733l105.524-82.946l0.063-0.014l5.649,3.892c0,0,207.753-65.977,221.627-70.207
C801.786,42.669,807.831,35.92,784.963,25.941z M583.639,31.835L434.53,82.745l-135.428-21.24c-0.081-0.17-0.185-0.323-0.268-0.468
L583.639,31.835z M300.284,63.594l130.801,20.51l-99.03,33.829l0.379-0.225C331.295,115.739,307.907,75.93,300.284,63.594z
M323.701,113.402l-11.187-19.44l-0.982-3.3l16.318,28.811l-2.936,1l-10.365-19.566l-0.58-1.998l8.686,15.114
c0.162,0.235,0.439,0.314,0.703,0.244c0.055-0.013,0.085-0.026,0.12-0.05C323.764,114.054,323.888,113.697,323.701,113.402z
M310.069,85.563l-1.33-4.502c7.23,12.131,16.113,27.186,22.085,37.389l-1.795,0.629L310.069,85.563z M323.316,121.109
l-42.607,14.519l15.901-64.98L323.316,121.109z M289.821,67.248c1.499-3.493,2.964-4.688,5.721-0.298
c-3.169,3.998-3.938,8.308-5.249,13.755c-4.497,18.845-9.304,37.607-13.94,56.448l-4.191,1.436
C277.487,116.659,286.889,78.101,289.821,67.248z M270.337,139.029l0.489,0.133l-91.839,31.366L287.632,68.902
C283.339,85.356,270.946,136.525,270.337,139.029z M287.268,67.069L175.083,171.986L71.631,207.321L287.268,67.069z
M225.93,281.286c-12.676-2.143-22.203-11.426-27.252-23.633c-1.972-5.085-3.343-10.861-3.975-16.944
c-0.006-0.061-0.349-5.113-0.279-8.237c0.049-4.473,0.494-9.028,1.458-13.449c0.028-0.085,0.029-0.199,0.07-0.3
c0.206-0.902,0.457-1.808,0.714-2.724c1.306-4.437,3.049-8.569,5.162-12.317c1.264-2.124,2.652-4.174,4.286-6.049
c4.984-5.783,9.576-9.469,13.84-11.861c4.96-2.413,10.281-3.512,15.735-2.981c7.182,0.783,13.795,4.35,19.182,10.452
c0.24,0.266,0.615,0.289,0.841,0.074c0.266-0.227,0.303-0.615,0.042-0.871c-5.581-6.322-12.483-10.067-19.943-10.85
c-0.706-0.068-1.367-0.046-2.049-0.078c1.843-0.197,3.698-0.288,5.443-0.401l1.665-0.061l7.329,0.158
c8.04,0.475,19.865,1.721,30.712,2.828c9.34,1.008,18.126,1.938,23.156,2.229c9.839,0.565,19.596,2.553,28.993,5.929
c0.811,0.275,1.608,0.593,2.492,0.931c1.227,0.438,2.538,0.941,3.903,1.436l-4.642,8.441l-71.177-2.459
c-0.484-0.026-0.87,0.353-0.886,0.825c0.002,0.473,0.374,0.846,0.841,0.862l70.274,2.449l-1.771,3.221l-66.307-0.453
c-0.452-0.016-0.826,0.357-0.851,0.846c0.012,0.453,0.397,0.86,0.845,0.841l65.391,0.432l-1.832,3.306l-61.719-0.455
c-0.452-0.016-0.826,0.357-0.851,0.846c0.012,0.453,0.397,0.859,0.845,0.841l60.802,0.433l-1.855,3.346l-57.964-0.455
c-0.452-0.016-0.826,0.357-0.851,0.846c0.012,0.453,0.397,0.859,0.845,0.841l57.047,0.434l-1.867,3.368l-54.358-0.441
c-0.452-0.016-0.826,0.357-0.851,0.846c0.011,0.454,0.397,0.86,0.845,0.841l53.441,0.42l-1.873,3.379l-51.155-0.454
c-0.452-0.016-0.826,0.357-0.851,0.846c0.011,0.454,0.397,0.86,0.845,0.841l50.238,0.432l-0.091,0.163l-1.86,3.33l-48.341-0.929
c-0.455,0.005-0.855,0.367-0.871,0.828c0.002,0.481,0.371,0.866,0.884,0.896l47.399,0.885l-1.858,3.386l-46.327-0.785
c-0.465-0.004-0.847,0.377-0.843,0.851c-0.027,0.452,0.355,0.858,0.82,0.87l45.427,0.752l-1.864,3.345l-44.289-0.617
c-0.474,0.02-0.852,0.395-0.881,0.863c0.029,0.447,0.404,0.834,0.871,0.841l43.357,0.624l-1.868,3.407l-42.696-0.525
c-0.452-0.017-0.826,0.357-0.851,0.846c0.011,0.453,0.397,0.86,0.845,0.84l41.76,0.5l-1.866,3.401l-41.64-0.372
c-0.476-0.004-0.849,0.346-0.847,0.813c0.007,0.459,0.369,0.875,0.857,0.863l40.681,0.379l-1.876,3.405l-40.123-0.28
c-0.467-0.014-0.862,0.377-0.85,0.844c-0.006,0.483,0.354,0.862,0.877,0.851l39.156,0.28l-1.413,2.638l-39.903-0.225
C252.94,272.658,241.364,283.873,225.93,281.286z M259.646,164.861c0.167,0.147,0.358,0.176,0.579,0.11
c0.091-0.032,0.216-0.081,0.291-0.151l8.866-10.152c0.032,0.601,0.047,1.23,0.102,1.957l-21.419,22.867
c-1.548-0.096-2.978-0.174-4.191-0.186c4.815-5.158,20.472-22.023,25.65-27.835c-0.014,0.178-0.1,0.342-0.101,0.53
c-0.022,0.232-0.005,0.552-0.05,0.835l-9.763,11.15C259.376,164.238,259.423,164.625,259.646,164.861z M269.521,158.34
l-0.021,1.554l-18.572,19.674l-1.259-0.03L269.521,158.34z M337.34,166.602c0.126,0.036,0.243,0.03,0.351,0.021
c0.193-0.073,0.34-0.199,0.405-0.407l2.897-9.006l1.196-0.388l-9.88,29.483c-0.319,0.087-0.652,0.209-0.994,0.334l3.134-9.72
c0.101-0.336-0.084-0.65-0.406-0.777c-0.317-0.086-0.647,0.09-0.75,0.411l-3.385,10.541c-0.388,0.148-0.759,0.286-1.133,0.411
l9.677-29.431l1.105-0.399l-2.626,8.153C336.85,166.158,337.016,166.476,337.34,166.602z M385.022,148.034l38.726-0.447l0.004-0.01
l1.146-0.016l0.021,0.011l18.262-0.211l116.024-95.476l4.06-1.117l-117.143,96.337l0.256,0.006l38.734-11.611l-13.115,13.336
l-25.365,17.485c-10.93-1.181-18.821-1.858-21.604-2.07l20.565-16.936l-2.415,0.047l-27.239,22.408l9.549-21.912l-0.572-0.287
l-1.169,0.015l-10.691,24.561l-10.374,8.529c-5.895-0.946-12.952-1.864-19.993-2.569l3.617-30.06L385.022,148.034z
M360.852,172.174l-1.196,12.703c-0.418-0.156-0.843-0.275-1.203-0.401l-0.082-0.192l1.25-12.965
C360.012,171.491,360.424,171.788,360.852,172.174z M359.798,169.587l0.299-2.966c0.321,0.857,0.639,1.91,0.789,3.187
c-0.296-0.094-0.591-0.108-0.918-0.17C359.914,169.619,359.858,169.606,359.798,169.587z M358.114,169.548
c-1.743-0.036-3.59,0.195-5.342,0.555l6.429-11.67L358.114,169.548z M357.911,171.229l-0.999,10.577l-5.469-9.322l0.164-0.325
C353.593,171.572,355.9,171.224,357.911,171.229z M352.815,182.826c-1.787-0.356-4.274-0.275-7.172,0.155l3.503-6.357
L352.815,182.826z M343.482,183.371c-0.142,0.024-0.302,0.067-0.433,0.092c1.181-2.557,3.104-6.623,4.87-8.935l0.238,0.409
L343.482,183.371z M344.547,184.919c3.78-0.647,6.891-0.832,8.583-0.338c1.906,0.531,3.384,2.381,3.417,4.319
c-0.002,0.124-0.061,0.248-0.059,0.36c-2.975-1.643-8.495-1.276-14.31-0.011L344.547,184.919z M339.97,189.785
c-1.914,0.487-3.758,1.064-5.576,1.663c-0.103-0.01-0.203-0.059-0.304-0.098c-0.862-0.337-1.659-0.651-2.509-0.931
c-1.005-0.366-1.994-0.704-3.006-1.038c4.673-1.771,9.571-3.159,13.864-4.022L339.97,189.785z M352.52,195.287
c5.213-1.694,5.706-5.051,5.689-6.42c-0.007-1.049-0.442-2.057-0.964-2.981c0.042,0.034,0.126,0.036,0.164,0.057l-0.058,0.084
c5.118,3.481,9.217,10.49,10.562,18.025l-33.282-1.163l4.433-7.99C343.185,196.132,347.783,196.86,352.52,195.287z
M361.439,187.304c7.412,2.591,16.231,6.002,20.183,7.75c0.813,0.349,1.697,0.706,2.579,1.077l-10.054,8.224l-4.47-0.327
C368.556,197.565,365.478,191.452,361.439,187.304z M364.323,186.51c0.249-0.833,0.943-3.401,0.989-6.546
c0.057,0.016,0.148,0.018,0.198,0.03c0.332,2.083,0.467,4.473,0.361,7.077C365.347,186.893,364.842,186.689,364.323,186.51z
M369.376,245.501l-0.002-1.97l9.741,0.326l-1.783,1.821L369.376,245.501z M375.913,247.118l-1.808,1.906l-4.763-0.148l0.012-1.965
L375.913,247.118z M369.408,242.12l0.011-2.012l12.887,0.491l-1.783,1.838L369.408,242.12z M369.414,238.738l0.05-2.016
l16.012,0.644l-1.758,1.812L369.414,238.738z M369.456,235.331l0.03-2.052l19.153,0.878l-1.725,1.801L369.456,235.331z
M369.513,231.93l0.011-2.071l22.234,1.098l-1.703,1.756L369.513,231.93z M369.529,228.482l0.018-2.059l25.344,1.368l-1.707,1.729
L369.529,228.482z M369.581,225.057l0.015-2.091l28.417,1.625l-1.677,1.742L369.581,225.057z M370.863,209.246l12.712,0.9
l-2.671,1.853l-11.186-0.692l0.01-1.152L370.863,209.246z M369.743,207.84l0.042-2.046l2.452,0.154l-2.307,1.9L369.743,207.84z
M398.082,198.755c0.673-0.186,1.26-0.456,1.789-0.838c1.302-0.951,2.055-2.093,2.252-3.387c0.159-0.88-0.006-1.647-0.225-2.3
c0.433-0.005,0.838-0.023,1.23-0.014c0.904,0.512,1.743,1.481,2.452,2.734l-15.294,10.546l-13.75-0.975l9.392-7.729
C390.37,198.467,394.896,199.667,398.082,198.755z M390.316,193.18l1.275-1.063c2.789,0.06,5.833,0.08,8.504,0.105
c0.239,0.456,0.515,1.211,0.403,2.047c-0.132,0.821-0.705,1.581-1.599,2.252c-0.016,0.012-0.032,0.018-0.032,0.018
c-0.521-0.629-1.484-1.547-3.409-2.525C394.085,193.316,392.202,193.101,390.316,193.18z M406.351,196.509
c0.771,1.873,1.339,4.185,1.451,6.496c0.108,1.841,0.118,3.027,0.202,3.742l-14.98-1.062L406.351,196.509z M405.695,192.168
c0.453,0,0.829-0.002,1.121-0.02c0.336,0.019,1.176,0.074,2.445,0.248l-2.274,1.58
C406.569,193.272,406.149,192.711,405.695,192.168z M379.108,215.271l2.668-1.844l25.476,1.742l-1.639,1.667L379.108,215.271z
M404.174,218.315l-1.643,1.666l-28.025-1.546l2.748-1.886L404.174,218.315z M383.69,212.106l2.609-1.786l23.977,1.748
l-1.583,1.605L383.69,212.106z M388.176,209.018l2.572-1.791l22.695,1.645l-1.687,1.723L388.176,209.018z M388.045,207.077
l-2.596,1.806l-13.096-0.903l2.249-1.88L388.045,207.077z M379.064,213.276l-2.749,1.869l-6.633-0.377l0.027-2.123L379.064,213.276
z M374.496,216.409l-2.763,1.917l-2.078-0.114l0.022-2.1L374.496,216.409z M369.916,221.649l2.773-1.944l28.429,1.733l-1.675,1.711
L369.916,221.649z M400.093,224.668l13.611,0.797l-1.973,1.528l-13.303-0.63L400.093,224.668z M401.525,223.202l1.665-1.666
l14.371,0.876l-1.925,1.523L401.525,223.202z M404.64,220.032l1.615-1.646l15.166,0.989l-1.885,1.484L404.64,220.032z
M407.732,216.89l1.589-1.628l15.918,1.104l-1.858,1.466L407.732,216.89z M410.819,213.776l1.557-1.601l16.644,1.183l-1.797,1.434
L410.819,213.776z M413.872,210.659l1.647-1.701l17.512,1.275l-2.024,1.567l-1.087-0.082L413.872,210.659z M414.997,207.221
l-5.311-0.384c-0.023-0.7-0.096-1.936-0.201-3.964c-0.126-2.41-0.737-5.092-1.731-7.388l4.045-2.813
c3.592,0.488,8.92,1.227,15.353,2.165L414.997,207.221z M411.394,190.884c-2.364-0.296-3.957-0.458-4.482-0.468l7.429-17.056
l8.993-7.416c1.805,0.476,8.89,2.178,18.647,3.919L411.394,190.884z M408.759,181.909c-1.182-0.196-2.511-0.447-3.919-0.678
l6.616-5.47L408.759,181.909z M408.178,183.305l-3.144,7.192c-2.843-0.008-7.263-0.008-11.417-0.055l8.798-7.23
C405.226,183.304,407.253,183.302,408.178,183.305z M391.05,190.417c-3.418-0.053-6.297-0.18-7.377-0.318l-2.45-0.346l0.949-7.777
c0.545,0.056,1.016,0.117,1.61,0.163c6.257,0.553,11.779,0.86,16.081,1.023L391.05,190.417z M383.411,191.81
c0.92,0.151,3.03,0.233,5.613,0.283l-1.866,1.539c-0.953,0.228-1.794,0.508-2.464,0.862c-0.952-0.387-1.79-0.748-2.408-1.024
c-0.691-0.317-1.559-0.651-2.496-1.037l1.082-1.014L383.411,191.81z M379.454,190.41l0.019,0.013l-1.426,1.307
c-3.002-1.218-6.742-2.662-10.516-4.07c0.142-2.753,0.01-5.206-0.281-7.38c2.268,0.35,6.471,0.931,13.229,1.574L379.454,190.41z
M369.327,250.323l3.403,0.055l-1.885,1.9l-1.535-0.016L369.327,250.323z M371.591,253.725l6.075,0.131l-2.322,1.839l-5.595-0.079
L371.591,253.725z M372.991,252.312l1.841-1.889l6.949,0.182l-2.296,1.801L372.991,252.312z M376.222,249.003l1.833-1.833
l7.814,0.225l-2.246,1.771L376.222,249.003z M379.446,245.727l1.805-1.823l8.672,0.288l-2.209,1.742L379.446,245.727z
M382.641,242.477l1.796-1.835l9.5,0.38l-2.168,1.711L382.641,242.477z M385.831,239.214l1.736-1.797l10.398,0.458l-2.137,1.647
L385.831,239.214z M388.989,235.993l1.754-1.783l11.165,0.521l-2.048,1.655L388.989,235.993z M392.169,232.778l1.696-1.731
l12.021,0.592l-2.032,1.583L392.169,232.778z M395.313,229.563l1.679-1.735l12.816,0.704l-2.004,1.591L395.313,229.563z
M408.36,231.557l14.833,0.747l91.829,3.998c16.128,0.713,32.281,1.304,48.417,1.959c0.279,0.342,0.594,0.64,0.854,0.964
c0.044,0.09,0.088,0.171,0.143,0.275c0.21,0.332,0.38,0.619,0.549,0.973c-16.974-0.853-33.975-1.745-50.988-2.495l-91.81-4.202
l-15.834-0.638L408.36,231.557z M563.338,235.635c-0.098-0.081-0.16-0.132-0.294-0.24l-0.581-15.413
c0.437-0.077,0.902-0.172,1.4-0.261l0.466,16.8C564.012,236.225,563.695,235.923,563.338,235.635z M562.805,237.577
c0.028,0.033,0.088,0.084,0.116,0.11c-15.935-0.853-31.895-1.764-47.848-2.502l-91.795-4.586l-12.973-0.553l1.967-1.542
l12.002,0.591l91.841,4.37c14.641,0.729,29.316,1.312,43.929,1.962c0.342,0.31,0.697,0.602,1.048,0.816
C561.761,236.625,562.305,237.107,562.805,237.577z M559.323,231.457l-0.417-10.808c0.437-0.099,0.894-0.186,1.388-0.289
l0.367,13.098C560.214,232.908,559.748,232.225,559.323,231.457z M559.485,234.78c-14.424-0.828-28.885-1.696-43.34-2.443
l-91.761-4.92l-10.153-0.474l1.935-1.518l9.23,0.516l91.783,4.733c13.607,0.701,27.234,1.288,40.803,1.917
C558.461,233.454,558.958,234.16,559.485,234.78z M517.232,229.512l-91.759-5.245l-7.339-0.383l1.892-1.493l6.452,0.405
l91.795,5.054c12.803,0.715,25.61,1.301,38.392,1.965c0.309,0.754,0.634,1.472,0.988,2.144
C544.198,231.145,530.721,230.296,517.232,229.512z M555.808,222.164c-0.094-0.326-0.17-0.602-0.264-0.909
c0.323-0.059,0.718-0.13,1.216-0.214l0.11,4.473C556.506,224.35,556.139,223.188,555.808,222.164z M555.618,227.036
c0.241,0.745,0.508,1.425,0.777,2.107c-12.677-0.817-25.377-1.708-38.062-2.443l-91.751-5.617l-4.58-0.235l1.843-1.482l3.717,0.228
l91.765,5.429C531.447,225.765,543.503,226.363,555.618,227.036z M519.407,223.893l-91.723-5.962l-1.847-0.124l1.831-1.424
l0.976,0.063l91.765,5.748c11.43,0.765,22.872,1.365,34.289,2.028c0.222,0.713,0.445,1.418,0.678,2.118
C543.383,225.512,531.393,224.64,519.407,223.893z M520.477,221.063l-90.807-6.271l1.809-1.4l90.018,5.984
c10.758,0.765,21.568,1.36,32.325,2.045c0.149,0.458,0.292,0.899,0.445,1.385c0.064,0.221,0.132,0.446,0.216,0.691
C543.163,222.705,531.802,221.835,520.477,221.063z M521.562,218.239l-88.126-6.421l2.009-1.58l116.811,8.413l0.365,0.021
c0.319,0.491,0.621,1.063,0.846,1.762c0.026,0.06,0.07,0.143,0.112,0.217C542.876,219.868,532.219,218.999,521.562,218.239z
M435.04,208.63l-17.948-1.287l12.036-12.241c5.815,0.825,12.455,1.807,19.495,2.857L435.04,208.63z M428.621,193.275
c-5.867-0.847-10.926-1.588-14.732-2.065l30.448-20.964c1.941,0.322,3.914,0.643,5.95,0.922L428.621,193.275z M449.152,166.841
l16.468-11.317l-11.667,11.886c-0.747-0.094-1.541-0.201-2.292-0.273C450.786,167.045,450.001,166.949,449.152,166.841z
M380.982,178.241c-5.017-0.442-9.883-0.772-14.232-0.866c-0.798-3.216-2.048-5.475-3.373-6.576
c-0.17-0.147-0.396-0.224-0.61-0.342l2.042-21.364l1.629-0.54l18.125-0.225L380.982,178.241z M364.274,149.3l0.08-0.018
l-0.102,0.051L364.274,149.3l-2.386,0.793l18.996-34.398l2.294-0.65L364.274,149.3z M362.386,174.156
c0.622,1.184,1.118,2.632,1.203,4.26c0.218,3.453-0.565,6.594-0.82,7.55c-0.507-0.184-1.009-0.354-1.468-0.529L362.386,174.156z
M350.532,170.703c-0.025,0.006-0.039,0.01-0.057,0.016l-7.079-12.123l0.865-2.555l17.625-5.947L350.532,170.703z M347.019,173.009
c-1.857,2.143-3.707,5.91-5.574,9.91l-0.455,0.969c-2.178,0.469-4.412,1.058-6.687,1.774l7.315-21.817L347.019,173.009z
M330.007,178.42c-0.377-3.146-1.163-10.864-1.309-17.085l7.741-2.587L330.007,178.42z M329.403,180.121l-2.667,8.18
c-0.243,0.12-0.52,0.2-0.758,0.279c-3.627-1.041-7.294-1.903-11.004-2.586l-0.935-1.049
C322.134,182.413,327.607,180.715,329.403,180.121z M309.054,177.717l0.543,1.935l-9.992-11.029
c3.655-0.818,6.846-0.491,10.064,2.764c2.437,2.432,4.114,4.351,5.07,5.511L309.054,177.717z M312.262,185.492l-1.739-0.208
l-0.02-0.058l-11.415-13.196c-0.212-0.241-0.595-0.275-0.858-0.056c-0.255,0.216-0.286,0.59-0.065,0.865l10.612,12.229
l-1.713-0.241c-1.703-2.039-12.985-15.612-19.277-22.08l-1.067-1.118c-5.946-6.112-8.086-8.209-9.772-7.731
c-1.555,0.432-1.399,2.697-1.309,3.688c0.318,5.005,1.224,20.221,1.482,24.357l-4.837-0.712c-0.278-8.303-0.62-14.688-0.905-19.34
c-0.052-1.472-0.153-2.736-0.195-3.891l0.006-6.616c0.29-1.48,1.123-2.559,2.303-2.897c1.323-0.383,2.982,0.056,4.829,1.186
c4.19,2.546,9.621,8.704,15.37,15.2c1.365,1.566,2.791,3.124,4.2,4.685L312.262,185.492z M341.011,339.004l-44.829,9.864
c0.048-0.12,0.088-0.225,0.063-0.365l-8.393-61.969c1.873-0.085,3.735-0.159,5.648-0.246c1.457-0.06,2.911-0.138,4.378-0.195
l43.194,47.547C340.963,335.395,340.942,337.184,341.011,339.004z M238.031,337.249l-0.011-0.232
c0.694,0.633,1.402,1.285,2.032,2.044c1.69,1.965,3.081,4.252,4.309,6.722l-0.102,0.156
C242.57,342.557,240.473,339.613,238.031,337.249z M243.484,347.36l-0.058,0.109c-1.111-2.413-2.357-4.71-3.909-6.653
c-0.475-0.594-0.965-1.091-1.483-1.609l0.011-0.131c0.488,0.572,0.975,1.134,1.473,1.74
C241.063,342.738,242.359,344.989,243.484,347.36z M241.405,337.92c-1.092-1.265-2.212-2.353-3.366-3.328l-0.916-44.74
l22.871,27.713l-14.579,26.423C244.212,341.79,242.955,339.728,241.405,337.92z M234.599,294.697l2.767,67.357l-1.152,0.213
L234.599,294.697z M235.037,362.573l-1.318,0.248l-2.223-75.03c0.031,0.019,0.029,0.033,0.064,0.042L235.037,362.573z
M255.249,306.25c1.797,1.137,3.675,2.608,5.548,4.503l22.117,26.75c0.29,0.969,0.677,2.232,1.08,3.52L255.249,306.25z
M282.425,341.012c-1.332-1.17-3.224-3.123-5.161-5.18l-26.397-31.997c0.451,0.213,0.904,0.457,1.374,0.703L282.425,341.012z
M244.604,288.384c7.278-0.244,18.963-0.698,32.245-1.283l-13.361,24.179L244.604,288.384z M245.923,353.806
c0.531,1.937,0.998,3.944,1.278,6.037l-0.23,0.036c-0.288-2-0.678-3.942-1.177-5.866L245.923,353.806z M246.755,352.24l0.123-0.18
c0.75,2.401,1.344,4.901,1.71,7.474l-0.19,0.029C248.044,357.035,247.5,354.585,246.755,352.24z M247.982,350.02l15.597-28.068
l23.926,28.973l-37.251,8.222C249.786,355.938,249.015,352.9,247.982,350.02z M267.075,315.644l14.54-26.211l7.103,52.428
L267.075,315.644z M282.956,286.924l0.095-0.109c0.064-0.014,0.164-0.016,0.218-0.021l7.883,57.988l-0.408-0.493L282.956,286.924z
M284.482,286.736c0.087,0.012,0.196-0.007,0.285-0.023l7.464,55.012c0.054,0.328,0.373,0.579,0.693,0.548
c0.017-0.006,0.028-0.01,0.083-0.023c0.264-0.09,0.47-0.357,0.435-0.682l-7.469-54.886c0.051-0.021,0.103-0.027,0.195-0.031
l8.432,62.108c0.042,0.204,0.135,0.336,0.262,0.454l-0.059,0.02l-2.183-2.692L284.482,286.736z M340.42,325.7
c-0.854-0.663-11.533-9.511-18.385-15.612l-40.942-45.049l1.376-0.054l19.792,22.312c0.143,0.18,0.384,0.256,0.61,0.193
c0.077-0.023,0.156-0.051,0.212-0.135c0.288-0.214,0.296-0.61,0.067-0.853l-19.152-21.582l2.965-0.108l53.948,60.071
C341.025,325.561,340.923,326.152,340.42,325.7z M342.986,230.154l-3.928,3.235l-21.042-0.385l1.824-3.352L342.986,230.154z
M348.934,225.217l-3.931,3.241l-24.207-0.52l1.834-3.33L348.934,225.217z M337.014,235.044l-3.996,3.315l-17.794-0.308
l1.834-3.353L337.014,235.044z M331.013,240.053l-4.041,3.261l-14.562-0.188l1.872-3.385L331.013,240.053z M324.954,245.002
l-4.041,3.342l-11.313-0.146l1.874-3.365L324.954,245.002z M318.89,250.031l-4.106,3.331l-7.989-0.045l1.859-3.393L318.89,250.031z
M312.775,255.068l-4.121,3.377l-4.66-0.039l1.866-3.398L312.775,255.068z M306.603,260.151l-3.162,2.585l-1.842,0.011l1.441-2.645
L306.603,260.151z M305.97,262.746l3.165-2.601l2.09,0.025l-3.776,2.589L305.97,262.746z M311.177,258.436l4.08-3.356l3.263,0.023
l-4.88,3.355L311.177,258.436z M317.322,253.383l4.051-3.327l4.407,0.042l-4.827,3.318L317.322,253.383z M323.417,248.359
l4.034-3.332l5.553,0.099l-4.777,3.288L323.417,248.359z M329.506,243.34l4.002-3.308l6.63,0.124l-4.711,3.26L329.506,243.34z
M335.526,238.368l3.971-3.272l7.819,0.143l-4.752,3.247L335.526,238.368z M341.532,233.379l3.961-3.228l8.865,0.203l-4.629,3.206
L341.532,233.379z M347.514,228.504l3.926-3.261l9.975,0.248l-4.632,3.173L347.514,228.504z M350.939,223.583l-27.364-0.665
l1.844-3.327l29.441,0.764L350.939,223.583z M326.368,217.885l1.785-3.298l32.582,0.925l-3.89,3.18L326.368,217.885z
M329.098,212.881l1.804-3.282l35.67,1.08l-3.849,3.183L329.098,212.881z M231.776,396.422c-1.008,0.297-2.04,0.481-3.086,0.592
c-6.142,0.494-12.221-2.403-17.16-8.204c-4.998-5.871-8.182-14.019-8.956-22.884c-1.444-16.462,5.952-31.276,17.188-34.428
c1.016-0.285,2.058-0.475,3.102-0.558c2.853-0.28,5.663,0.265,8.402,1.437l0.067,1.892c-2.37-1.047-4.827-1.575-7.329-1.385
c-0.854,0.069-1.736,0.264-2.576,0.501c-10.026,2.795-16.588,16.678-15.217,32.235c1.508,17.08,11.956,30.208,23.307,29.245
c0.87-0.083,1.718-0.24,2.594-0.484c8.904-2.503,14.946-13.717,15.244-27.084l0.215-0.049c-0.137,6.52-1.554,12.681-4.364,17.69
c-3.497,6.217-8.524,9.893-14.173,10.372c-11.699,1.016-22.505-12.277-24.023-29.602c-0.117-1.316-0.181-2.663-0.199-3.967
c-0.002-0.333-0.262-0.602-0.6-0.604c-0.361,0.007-0.61,0.271-0.604,0.602c0,1.326,0.054,2.714,0.173,4.086
c1.612,17.988,12.98,31.763,25.364,30.708c1.039-0.085,1.998-0.259,2.981-0.526c4.821-1.356,9.079-5,12.133-10.464
c2.938-5.224,4.412-11.719,4.527-18.545l0.319-0.07C248.826,381.326,241.901,393.611,231.776,396.422z M226.235,368.307
c-0.06-0.1-0.121-0.171-0.148-0.316c-0.209-0.967,0.526-1.915,1.389-2.152c0.014,0.001,0.014,0.001,0.024-0.003l113.45-25.042
c0.058,0.787,0.163,1.716,0.307,2.566L226.235,368.307z M307.926,285.594c12.139-0.525,23.634-1.016,32.413-1.318l4.166,34.218
c-0.741,1.813-1.407,3.723-1.911,5.729L307.926,285.594z M340.615,272.385l0.466-0.489l5.177,42.895
c-0.148,0.267-0.247,0.529-0.377,0.777L340.615,272.385z M350.609,311.083c3.044-3.791,6.809-6.527,11.051-7.713
c1.327-0.389,2.697-0.6,4.087-0.648c13.526-0.473,25.199,14.896,25.998,34.351c0.685,17.291-7.336,32.217-19.07,35.496
c-1.34,0.392-2.711,0.594-4.085,0.652c-6.531,0.241-12.787-3.177-17.691-9.6c-3.703-4.846-6.295-10.968-7.522-17.719
c-0.021-0.071-0.04-0.167-0.057-0.247c-0.403-2.203-0.638-4.465-0.725-6.795c-0.077-1.46-0.022-2.926,0.024-4.37l0.101-1.168
c0.141-2.477,0.501-4.915,1.02-7.205l0.133-0.415l-0.047-0.014c0.502-2.179,1.143-4.239,1.958-6.195l0.268-0.603
c1.169-2.736,2.65-5.276,4.366-7.466l0.197-0.277L350.609,311.083z M348.988,310.609c-0.599,0.79-1.19,1.62-1.707,2.52
l-5.076-42.342l2.002-2.024L348.988,310.609z M310.443,262.703l3.753-2.597l48.747,0.352l-2.45,2.521L310.443,262.703z
M366.15,262.929l-3.564-0.028l2.456-2.478l2.498,0.013l0.001,1.419L366.15,262.929z M367.54,258.719l-0.846,0.017l0.857-0.862
L367.54,258.719z M367.587,255.709l-2.99,3.049l-47.968-0.328l4.849-3.34l46.132,0.421L367.587,255.709z M367.627,253.799
l-43.688-0.408l4.834-3.319l38.868,0.472L367.627,253.799z M331.184,248.382l4.776-3.295l31.71,0.451l0.001,3.28L331.184,248.382z
M367.72,243.843l-29.315-0.425l4.719-3.242l24.602,0.389L367.72,243.843z M367.78,238.843l-22.251-0.338l4.743-3.273l17.509,0.329
L367.78,238.843z M367.823,233.881l-15.181-0.286l4.658-3.226l10.536,0.241L367.823,233.881z M367.848,228.871l-8.138-0.154
l4.622-3.191l3.552,0.101L367.848,228.871z M366.721,223.847l1.184-0.792l0.012,0.855L366.721,223.847z M363.804,223.815
l-10.35-0.248l3.898-3.172l10.567,0.258l0.006,0.329L363.804,223.815z M367.952,218.921l-8.59-0.204l3.867-3.182l4.76,0.129
L367.952,218.921z M368.012,213.95l-2.78-0.063l2.79-2.309L368.012,213.95z M368.081,209.017l-36.097-1.095c0,0,0,0,0-0.001
l-0.027,0l-0.122-0.004l1.874-3.372l34.385,1.209L368.081,209.017z M369.218,257.035l4.312,0.067l-2.386,1.883l-1.929-0.017
L369.218,257.035z M468.906,263.301l-97.341-0.474l-2.344-0.38l2.727-2.129l41.503,0.626l63.792,0.566
C474.426,262.127,471.665,262.7,468.906,263.301z M482.912,260.293l-69.462-1.085l-39.68-0.353l2.337-1.85l38.419,0.754
l75.587,0.98C487.701,259.258,485.324,259.777,482.912,260.293z M495.393,257.583l-80.837-1.536l-36.628-0.499l2.313-1.761
l35.39,0.81l86.96,1.429C500.192,256.536,497.793,257.068,495.393,257.583z M507.416,254.958l-91.778-2.109l-33.55-0.545
l2.25-1.779l32.352,0.854l91.893,1.896c2.048,0.049,4.047,0.071,6.083,0.102C512.27,253.89,509.853,254.452,507.416,254.958z
M518.943,252.434c-3.428-0.111-6.903-0.255-10.33-0.31l-91.877-2.479l-30.541-0.614l2.204-1.724l29.388,0.896l91.892,2.255
c5.58,0.132,11.18,0.212,16.718,0.364C523.958,251.35,521.487,251.876,518.943,252.434z M530.123,249.974
c-6.827-0.246-13.63-0.487-20.432-0.688l-91.853-2.786l-27.559-0.674l2.171-1.728l26.417,0.918l91.855,2.581
c9.049,0.269,18.089,0.467,27.106,0.685C535.303,248.848,532.754,249.407,530.123,249.974z M540.921,247.576
c-10.045-0.378-20.097-0.797-30.159-1.113l-91.863-3.156l-24.572-0.711l2.153-1.664l23.465,0.879l91.875,2.967
c12.347,0.411,24.67,0.726,37.061,1.077C546.331,246.408,543.678,246.969,540.921,247.576z M551.469,245.264
c-13.189-0.575-26.412-1.18-39.647-1.64l-91.818-3.5l-21.644-0.687l2.093-1.662l20.59,0.846l91.847,3.316
c15.523,0.589,31.129,1.037,46.667,1.54C557.028,244.012,554.33,244.611,551.469,245.264z M561.635,242.984
c-16.224-0.769-32.484-1.55-48.703-2.203l-91.835-3.859l-18.741-0.668l2.044-1.616l17.723,0.827l91.827,3.665
c17.092,0.698,34.187,1.258,51.291,1.858c0.17,0.405,0.315,0.786,0.409,1.135C564.376,242.378,563.052,242.679,561.635,242.984z
M567.259,241.519c-0.116-0.372-0.275-0.818-0.447-1.28l-0.831-20.904c0.481-0.095,0.944-0.19,1.421-0.246l0.645,22.268
C567.753,241.393,567.506,241.448,567.259,241.519z M570.406,240.816l-0.878-22.136c0.481-0.095,0.958-0.196,1.411-0.273
l0.63,22.142C571.185,240.649,570.792,240.727,570.406,240.816z M573.937,240.012l-0.88-21.996
c0.497-0.099,0.961-0.175,1.463-0.263l0.607,21.975C574.762,239.822,574.342,239.91,573.937,240.012z M577.47,239.186l-0.86-21.844
c0.487-0.085,0.998-0.149,1.457-0.246l0.596,21.813C578.31,238.986,577.931,239.077,577.47,239.186z M581.015,238.163
l-0.838-21.458c0.433-0.105,0.974-0.162,1.438-0.267l0.595,21.147C581.813,237.83,581.4,237.981,581.015,238.163z M584.465,235.555
l-0.787-19.513c0.527-0.086,1.016-0.169,1.509-0.286l0.473,17.921C585.313,234.356,584.922,234.974,584.465,235.555z
M587.763,228.04l-0.497-12.649c0.323-0.064,0.687-0.123,1.032-0.188c0.04,0.612,0.13,1.289,0.176,1.831
C588.816,220.677,588.551,224.575,587.763,228.04z M622.419,200.59c-11.056,1.934-22.482,3.906-33.383,5.787
c-0.632-4.743-1.415-10.17-2.316-15.511L622.419,200.59z M587.336,206.702c-0.114,0.018-0.231,0.04-0.372,0.066l-0.149-4.154
C586.992,204.025,587.185,205.357,587.336,206.702z M585.01,207.094c-0.529,0.099-1.062,0.161-1.591,0.285l-0.553-14.813
c-0.046-0.915-0.102-1.828-0.16-2.745l1.851,0.5c0.004,0.833,0.018,1.711,0.058,2.567L585.01,207.094z M584.578,188.132
c-0.008,0.142-0.012,0.269-0.024,0.393l-1.895-0.503c-0.172-3.844-0.382-7.646-0.585-11.452
C582.94,179.869,583.794,183.87,584.578,188.132z M628.815,201.17c7.028-1.214,13.781-2.397,19.959-3.469
c15.498-2.658,23.964-3.704,28.581-3.977l-124.249,22.478c-0.481-0.571-0.955-1.069-1.557-1.376
c-0.375-0.177-0.739-0.351-1.078-0.525C570.329,211.393,601.314,205.951,628.815,201.17z M581.422,207.695
c-0.503,0.101-0.999,0.195-1.523,0.256l-0.619-15.622c-0.038-1.167-0.115-2.314-0.162-3.495l1.835,0.48
c0.032,1.124,0.048,2.229,0.064,3.322L581.422,207.695z M579.518,168.988c0.252,0.657,0.513,1.319,0.806,1.999
c0.145,0.351,0.307,0.77,0.422,1.19c0.067,5.122,0.147,10.249,0.25,15.402l-1.933-0.552c-0.34-7.214-0.716-14.432-1.108-21.66
C578.488,166.505,579.007,167.699,579.518,168.988z M577.885,208.299c-0.501,0.106-1.018,0.196-1.535,0.245l-0.643-16.483
c-0.058-1.412-0.136-2.803-0.198-4.198l1.885,0.492c0.022,1.345,0.03,2.709,0.058,4.039L577.885,208.299z M577.054,163.606
c0.118,7.676,0.21,15.33,0.355,22.964l-1.919-0.518c-0.419-8.447-0.88-16.915-1.333-25.362
C575.314,161.343,576.256,162.318,577.054,163.606z M573.861,192.115l0.471,16.794c-0.515,0.077-1.028,0.153-1.518,0.266
l-0.689-17.388c-0.074-1.633-0.174-3.261-0.248-4.896l1.915,0.513C573.832,188.992,573.815,190.561,573.861,192.115z
M573.227,160.138c0.06,0.023,0.126,0.097,0.216,0.118c0.096,8.456,0.237,16.9,0.38,25.362l-1.976-0.546
c-0.407-8.4-0.848-16.805-1.293-25.221C571.533,159.803,572.427,159.892,573.227,160.138z M570.774,209.512
c-0.504,0.094-1.031,0.156-1.499,0.237l-0.722-18.208c-0.096-1.885-0.206-3.768-0.294-5.643l1.931,0.511
c0.064,1.809,0.036,3.627,0.105,5.466L570.774,209.512z M569.836,159.917c0.122,8.244,0.243,16.491,0.395,24.731l-2.02-0.571
c-0.375-7.832-0.773-15.705-1.176-23.536C568.063,160.201,568.987,160.003,569.836,159.917z M567.237,210.083
c-0.513,0.091-1.04,0.177-1.541,0.242l-0.718-19.045c-0.072-2.128-0.226-4.242-0.336-6.376l1.936,0.546
c0.076,2.042,0.069,4.096,0.147,6.153L567.237,210.083z M566.294,160.76c0.086,7.632,0.22,15.264,0.357,22.904l-2.02-0.544
c-0.384-7.1-0.751-14.156-1.1-21.254C564.488,161.417,565.456,161.054,566.294,160.76z M563.148,191.346l0.521,19.308
c-0.509,0.09-0.988,0.189-1.507,0.283l-0.781-19.915c-0.102-2.355-0.238-4.738-0.334-7.113l2.002,0.569
C563.078,186.766,563.09,189.046,563.148,191.346z M562.747,162.248c0.084,6.819,0.197,13.63,0.298,20.449l-2.06-0.545
c-0.303-6.17-0.603-12.335-0.96-18.538C560.982,163.108,561.864,162.653,562.747,162.248z M560.116,211.247
c-0.513,0.077-0.986,0.163-1.499,0.224l-0.802-20.691c-0.112-2.608-0.285-5.227-0.401-7.812l2.026,0.537
c0.038,2.544,0.042,5.061,0.102,7.586L560.116,211.247z M559.188,164.09c0.072,5.887,0.17,11.756,0.256,17.645l-2.07-0.59
c-0.258-5.155-0.537-10.352-0.794-15.51C557.471,165.073,558.379,164.578,559.188,164.09z M556.56,211.817
c-0.503,0.068-0.996,0.156-1.489,0.231l-0.837-21.564c-0.112-2.813-0.271-5.653-0.437-8.522l2.055,0.579
c0.046,2.754,0.066,5.54,0.118,8.285L556.56,211.817z M555.65,166.205c0.065,4.877,0.138,9.698,0.231,14.543l-2.112-0.571
c-0.225-4.074-0.42-8.165-0.647-12.271C553.963,167.305,554.818,166.739,555.65,166.205z M553.017,212.369
c-0.495,0.058-1.002,0.123-1.495,0.224l-0.848-22.346c-0.142-3.086-0.325-6.157-0.47-9.23l2.048,0.552
c0.036,2.992,0.074,5.991,0.13,8.98L553.017,212.369z M552.097,168.608c0.086,3.743,0.142,7.459,0.18,11.157l-2.124-0.551
c-0.162-2.911-0.29-5.832-0.471-8.761C550.499,169.832,551.308,169.182,552.097,168.608z M549.459,212.901
c-0.501,0.059-0.994,0.142-1.473,0.191l-0.901-23.114c-0.153-3.312-0.313-6.632-0.517-9.95l2.097,0.565
c0.044,3.256,0.082,6.472,0.16,9.726L549.459,212.901z M548.589,171.315c0.03,2.5,0.07,4.996,0.124,7.513l-2.192-0.61
c-0.084-1.62-0.147-3.285-0.237-4.96C547.038,172.597,547.8,171.915,548.589,171.315z M545.886,212.903
c-0.449-0.141-0.874-0.279-1.349-0.416c-0.034-0.031-0.098-0.053-0.124-0.06l-0.928-22.7c-0.132-3.579-0.331-7.117-0.525-10.687
l2.124,0.582c0.042,3.474,0.088,6.954,0.182,10.404L545.886,212.903z M545.065,174.307c0.01,1.178,0.02,2.377,0.05,3.545
l-2.21-0.605c-0.022-0.327-0.028-0.623-0.042-0.952C543.591,175.639,544.291,174.952,545.065,174.307z M540.574,178.42l0.92,0.231
c0.052,3.719,0.088,7.431,0.184,11.122l0.607,22.05c-0.467-0.106-0.966-0.232-1.517-0.358l-0.842-21.984
c-0.145-3.314-0.315-6.623-0.504-9.934C539.819,179.176,540.201,178.798,540.574,178.42z M537.959,181.015
c0.024,2.831,0.05,5.648,0.124,8.507l0.597,21.458c-0.519-0.099-0.966-0.22-1.507-0.302l-0.827-21.486
c-0.08-2.087-0.229-4.116-0.325-6.201C536.665,182.329,537.277,181.672,537.959,181.015z M534.424,184.667
c0.056,1.549,0.03,3.073,0.08,4.595l0.589,20.939c-0.499-0.099-0.991-0.197-1.495-0.3l-0.823-20.95
c-0.046-0.793-0.086-1.547-0.124-2.332C533.234,185.98,533.842,185.322,534.424,184.667z M530.91,188.552
c0.016,0.161-0.01,0.307,0.006,0.45l0.565,20.513c-0.487-0.125-1-0.216-1.499-0.345l-0.735-18.711
C529.798,189.823,530.35,189.189,530.91,188.552z M527.452,192.594l0.443,16.19c-0.499-0.091-0.972-0.188-1.524-0.305
l-0.527-13.998C526.348,193.869,526.909,193.22,527.452,192.594z M524,196.773l0.321,11.331c-0.541-0.1-1.018-0.189-1.533-0.298
l-0.369-9.073C522.916,198.117,523.445,197.434,524,196.773z M520.542,201.182l0.181,6.249c-0.128-0.024-0.24-0.091-0.351-0.087
c-0.411-0.081-0.779-0.151-1.201-0.201l-0.144-3.993C519.489,202.529,519.99,201.854,520.542,201.182z M517.081,205.766
l0.022,0.935c-0.204-0.008-0.382-0.056-0.631-0.102C516.676,206.354,516.852,206.121,517.081,205.766z M510.426,207.188
c2.785,0.584,6.069,1.21,9.631,1.885c9.419,1.813,20.113,3.879,23.998,5.164c0.623,0.2,1.22,0.393,1.774,0.572
c2.174,0.689,3.815,1.201,5.075,2.016l-113.423-8.157l13.372-10.495C470.68,201.115,493.389,204.59,510.426,207.188z
M450.478,196.439c-7.025-1.035-13.796-2.037-19.839-2.917l21.712-22.097c8.42,1.215,17.725,2.163,26.981,2.351L450.478,196.439z
M558.359,113.543l-4.423,1.342l-73.119,57.497c-2.643-0.724-12.519-3.296-24.689-4.96l16.961-17.275l28.362-19.56l-5.317,1.615
l-16.726,11.525l9.083-9.25l-3.375,1.022l70.683-71.921l3.306-0.934l-70.608,71.851l7.638-2.294L667.979,13.805l5.084-1.454
L501.45,130.586l52.486-15.702L681.898,14.295l4.282-1.193L558.359,113.543z M678.727,6.476c-1.635,0.505-3.365,1.026-5.137,1.601
L3.175,234.142l0.216,0.806l-0.233-0.801c-0.504,0.134-1.041-0.151-1.221-0.623c-0.075-0.26-0.016-0.518,0.041-0.744
c0.141-0.233,0.345-0.4,0.618-0.509l63.101-21.564l0.002,0.008l0.051-0.02l110.084-37.58l0.019,0.018l0.038-0.036L651.496,10.7
c21.077-5.642,31.227-6.742,34.92-6.774C684.637,4.642,681.55,5.589,678.727,6.476z"/>
<path d="M241.779,231.515l0.204,0.01c-0.083-9.434-2.978-16.052-3.186-16.524c-3.164-7.205-8.294-12.225-14.208-12.832
c-1.61-0.147-3.171-0.011-4.746,0.418c-8.531,2.413-15.406,13.177-16.785,26.311c-0.011,0.102-0.032,0.503-0.042,0.603
c0,0.005,0,0.004,0,0.008c-0.227,1.568-0.108,6.931-0.1,7.035c0.912,12.542,7.274,22.532,15.94,23.565
c2.173,0.252,4.339,0.087,6.459-0.51c9.176-2.588,15.87-12.667,16.643-25.13c0.059-0.964,0.048-1.957,0.024-2.943L241.779,231.515z
M240.611,234.47c-0.73,11.951-7.061,21.573-15.759,24.027c-1.978,0.543-4.003,0.674-6.017,0.465
c-5.081-0.614-9.281-4.607-11.959-10.425c2.62,5.072,6.481,8.538,11.108,9.254c2.137,0.279,4.238,0.152,6.314-0.44
c8.279-2.321,14.411-11.389,15.2-22.588c0.071-0.778,0.084-1.53,0.086-2.291c-0.035-1.467-0.334-9.903-2.657-14.999l0.013-0.007
c-0.004,0.002-0.008,0.004-0.013,0.005c-1.31-3.23-2.967-6.057-4.977-8.227l-0.789,0.885c1.788,1.884,3.245,4.369,4.425,7.17
c0.007,0.01,0.011,0.015,0.011,0.015s0.068,0.156,0.174,0.424c0.021,0.051,0.043,0.101,0.064,0.153
c-0.001,0-0.002,0.001-0.003,0.001c0.659,1.697,2.488,7.035,2.549,13.915c0.004,0,0.008-0.001,0.013-0.001
c-0.016,0.97,0.006,1.9-0.076,2.861c-0.788,10.696-6.563,19.345-14.339,21.53c-1.902,0.55-3.846,0.662-5.808,0.362
c-4.585-0.648-8.347-4.37-10.744-9.709c2.282,4.574,5.72,7.758,9.852,8.521c2.065,0.355,4.15,0.275,6.168-0.287
c7.466-2.085,12.865-9.979,13.762-20.105c0.084-0.871,0.083-1.719,0.085-2.58c-0.001-0.09-0.113-8.723-2.783-14.693
c-0.053-0.12-0.105-0.243-0.159-0.362c-0.011-0.024-0.022-0.049-0.032-0.072c-1.105-2.399-2.447-4.528-4.025-6.177l-0.788,0.861
c1.573,1.671,2.906,3.864,3.984,6.315h0.013c0,0,0.072,0.156,0.184,0.427c0.007,0.017,0.016,0.032,0.023,0.049
c-0.001,0-0.001,0.001-0.002,0.001c0.601,1.471,2.341,6.331,2.399,13.197c0.003,0,0.005,0,0.008-0.001
c0,0.095-0.004,0.19-0.004,0.285c0,0.06,0.003,0.117,0.003,0.178l-0.005,0.001c-0.006,0.814-0.026,1.629-0.095,2.423
c-0.82,9.642-5.892,17.108-12.893,19.055c-1.854,0.541-3.732,0.633-5.624,0.296c-4.119-0.726-7.461-4.159-9.582-9.029
c2.034,4.112,5.037,7.035,8.663,7.826c1.984,0.449,4.037,0.393,5.986-0.152c6.541-1.844,11.376-8.785,12.334-17.675
c0.105-0.98,0.124-1.926,0.122-2.87c-0.017-1.437-0.231-8.124-2.582-13.68c-1.004-2.228-2.26-4.232-3.766-5.743l-0.812,0.905
c1.302,1.323,2.386,3.078,3.317,5.051l0.014-0.005c0,0,2.519,4.469,2.635,13.288c0.003,0,0.007-0.001,0.01-0.001
c-0.002,0.198-0.003,0.396-0.005,0.595c0,0.169,0.006,0.327,0.004,0.499l-0.013,0.001c-0.012,0.604-0.04,1.22-0.132,1.869
c-1.178,8.318-5.376,14.892-11.437,16.609c-1.765,0.455-3.617,0.531-5.415,0.142c-3.649-0.8-6.597-3.956-8.368-8.346
c1.704,3.64,4.225,6.282,7.381,7.13c1.947,0.514,3.919,0.507,5.849-0.028c5.605-1.586,9.901-7.529,10.891-15.173
c0.148-1.072,0.181-2.102,0.188-3.124c0.007-2.258-0.192-8.021-2.179-12.238c-0.007,0.003-0.014,0.006-0.02,0.009
c-0.95-2.101-2.125-3.965-3.545-5.369l-0.836,0.923c1.095,1.116,2.03,2.533,2.829,4.14l0.014-0.004c0,0,2.269,3.766,2.506,12.533
c0.001,0,0.002,0,0.003,0c0,0.021-0.001,0.041-0.001,0.062c0.004,0.157,0.012,0.302,0.015,0.462l-0.026,0.005
c-0.014,0.8-0.036,1.605-0.132,2.424c-0.929,7.162-4.874,12.745-10.053,14.169c-1.705,0.496-3.436,0.505-5.181,0.039
c-2.677-0.729-4.844-2.916-6.412-5.905c1.387,2.268,3.206,3.959,5.355,4.688c1.92,0.632,3.799,0.661,5.671,0.138
c4.814-1.336,8.472-6.213,9.488-12.728c0.144-0.924,0.193-1.818,0.235-2.725c-0.008-0.853-0.138-7.654-2.151-11.977
c-0.003,0.001-0.005,0.002-0.008,0.003c-0.815-1.714-1.805-3.263-3.035-4.378l-0.81,0.904c0.808,0.777,1.528,1.749,2.154,2.846
l0.002-0.001c0,0,2.778,4.298,2.635,12.117c0.004,0,0.008-0.001,0.013-0.001c-0.03,0.982-0.038,2.011-0.203,3.002
c-0.961,6.022-4.281,10.543-8.635,11.792c-1.628,0.455-3.286,0.395-4.946-0.169c-2.235-0.714-4.026-2.648-5.339-5.217
c1.117,1.85,2.49,3.272,4.206,3.963c1.789,0.772,3.703,0.917,5.517,0.392c3.93-1.094,7.018-5.044,8.061-10.337
c0.184-0.892,0.265-1.795,0.305-2.686c0.014-0.474,0.157-7.236-2.511-12.18c-0.593-1.011-1.269-1.916-2.069-2.631l-0.815,0.867
c0.746,0.665,1.367,1.551,1.928,2.521l0.005-0.002c0,0,0.048,0.082,0.126,0.23c0.057,0.102,0.117,0.2,0.173,0.304
c-0.006,0.003-0.011,0.005-0.017,0.008c0.639,1.317,2.135,5.008,1.985,10.314c0.006,0,0.011-0.001,0.017-0.002
c-0.009,0.135-0.017,0.271-0.025,0.406c0,0.002,0,0.003,0,0.005l0,0c-0.053,0.862-0.101,1.723-0.28,2.589
c-0.972,4.851-3.731,8.464-7.208,9.42c-1.56,0.454-3.184,0.332-4.714-0.334c-1.557-0.665-2.809-1.978-3.812-3.726
c0.725,1.036,1.602,1.894,2.602,2.435c1.7,0.932,3.559,1.182,5.336,0.673c3.116-0.861,5.62-3.857,6.656-7.993
c0.249-0.956,0.352-1.924,0.435-2.897c0.046-0.722,0.279-6.026-2.302-11.045c-0.488-0.79-1.042-1.497-1.666-2.019l-0.848,0.887
c0.556,0.45,1.046,1.047,1.498,1.693l0.003-0.001c0,0,0.067,0.091,0.171,0.253c0.038,0.057,0.079,0.112,0.117,0.17
c-0.003,0.001-0.007,0.003-0.01,0.004c0.637,1.074,2.13,4.232,1.845,9.698c0.007,0,0.012-0.001,0.019,0
c-0.013,0.148-0.026,0.298-0.039,0.447c0,0.001,0,0.002,0,0.003h0c-0.072,0.83-0.152,1.675-0.376,2.51
c-0.938,3.711-3.116,6.391-5.836,7.123c-1.474,0.434-2.998,0.222-4.423-0.562c-0.969-0.548-1.813-1.416-2.492-2.489
c0.35,0.423,0.726,0.799,1.151,1.109c1.595,1.183,3.44,1.571,5.184,1.096c2.329-0.663,4.248-2.748,5.253-5.719
c0.342-1.009,0.535-1.996,0.668-2.994c0.049-0.651,0.42-6.678-2.207-10.251c-0.375-0.502-0.787-0.959-1.225-1.319l-0.87,0.916
c0.055,0.024,0.069,0.026,0.096,0.027c0.378,0.3,0.657,0.689,0.929,1.073l0.003-0.002c0,0,2.661,3.212,2.033,9.227
c0.004-0.001,0.007-0.001,0.011-0.002c-0.016,0.138-0.035,0.278-0.052,0.417c-0.002,0.017-0.003,0.033-0.005,0.05l-0.001,0
c-0.105,0.829-0.241,1.662-0.502,2.446c-0.891,2.609-2.518,4.412-4.463,4.961c-1.402,0.386-2.842,0.091-4.128-0.885
c-0.238-0.171-0.358-0.476-0.552-0.69c1.257,0.918,2.699,1.245,4.089,0.85c1.596-0.433,2.946-1.709,3.846-3.552
c0.539-1.088,0.78-2.235,0.966-3.423c0.205-1.492,0.513-5.756-1.97-8.995c-0.282-0.306-0.602-0.562-0.901-0.784l-0.824,0.898
c0.128,0.09,0.257,0.193,0.384,0.303c0.018,0.014,0.091,0.077,0.205,0.191c0.03,0.029,0.062,0.055,0.09,0.084
c-0.002,0.001-0.004,0.002-0.006,0.003c0.737,0.787,2.51,3.287,1.767,8.386c0.005-0.001,0.01-0.001,0.015-0.002
c-0.018,0.115-0.041,0.226-0.061,0.34c-0.005,0.034-0.008,0.063-0.013,0.097l-0.004,0c-0.152,0.831-0.358,1.619-0.732,2.351
c-0.749,1.549-1.838,2.584-3.08,2.935c-1.044,0.294-2.128,0.062-3.051-0.652c0.778,0.352,1.621,0.479,2.493,0.231
c0.863-0.245,1.711-0.827,2.349-1.662c0.725-0.913,1.18-2.081,1.471-3.294c0.128-0.766,0.801-5.596-1.993-8.578
c-0.067-0.073-0.135-0.15-0.182-0.2c-0.056-0.059-1.246,1.501-3.261,5.209l-0.005,0.04c0.787-0.096,1.301,0.619,1.301,0.619
c0.746,1.026,0.675,1.946,0.676,2.721c0.002,0.777-0.402,1.542-0.402,1.542c-0.789,1.541-2.013,1.397-2.013,1.397
c-1.142,0.004-1.555-1.315-1.555-1.315c-0.686-2.169,0.56-3.978,0.56-3.977c0.707-0.978,1.365-0.984,1.365-0.984
c0.017-0.003,0.033,0,0.05-0.003c0.008-0.015,0.015-0.027,0.023-0.041l0.679-5.866c-0.102,0.014-0.206-0.009-0.311,0.012
c0.098-0.075,0.239-0.154,0.36-0.223l0.155-1.32c-0.095,0.023-0.201,0.019-0.307,0.032c-0.391,0.11-0.797,0.266-1.174,0.526
c0.49-0.43,0.995-0.748,1.545-1.004l0.179-1.381c-1.015,0.273-1.93,0.914-2.769,1.679c0.823-1.033,1.827-1.73,2.829-2.215
l0.214-1.414c-1.326,0.431-2.558,1.296-3.59,2.458c1.04-1.456,2.298-2.561,3.691-3.201l0.144-1.318
c-1.605,0.613-3.129,1.655-4.385,3.202c1.272-1.843,2.796-3.21,4.519-4.031l0.171-1.395c-1.897,0.733-3.68,2.103-5.17,4.05
c1.502-2.27,3.319-3.92,5.269-4.852l0.18-1.388c-2.293,0.918-4.414,2.651-6.143,5.118c1.759-2.779,3.885-4.821,6.263-5.961
l0.194-1.41c-2.56,1.055-4.891,2.994-6.802,5.695c1.939-3.022,4.314-5.288,6.918-6.571l0.191-1.376
c-2.888,1.17-5.512,3.394-7.617,6.472c2.123-3.411,4.78-5.953,7.723-7.33l0.177-1.419c-3.286,1.361-6.267,4.006-8.615,7.659
c2.818-4.798,6.612-8.203,10.834-9.4c1.425-0.381,2.846-0.527,4.279-0.401c5.868,0.602,10.853,5.901,13.77,13.357l0.001-0.001
c0,0,0.011,0.029,0.02,0.053c0.016,0.041,0.034,0.08,0.05,0.122c-0.002,0.001-0.003,0.001-0.005,0.002
c0.37,1.025,2.366,6.927,2.508,14.694c0.005,0,0.01-0.001,0.015-0.001C240.658,232.616,240.675,233.507,240.611,234.47z
M215.751,237.386c0.14-0.039,0.259-0.081,0.372-0.167c1.344-0.68,2.254-2.07,2.749-3.696c0.002,0,0,0,0.014,0
c-0.246,0.958-0.616,1.874-1.196,2.604c-0.508,0.633-1.113,1.062-1.741,1.255C215.882,237.403,215.816,237.392,215.751,237.386z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 46 KiB

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="218px"
height="55.42px" viewBox="0 0 218 55.42" enable-background="new 0 0 218 55.42" xml:space="preserve">
<g id="图层_1">
<path d="M0.004,39.378c0,0.446-0.026,0.797,0.089,1.212c2.358,9.905,13.594,16.157,26.114,14.588
c14.284-1.79,27.18-5.003,45.067-9.97c9.536-2.648,28.081-9.924,30.793-11.462c0,0.007,0.106-0.056,0.106-0.048
c0.261-0.188,0.409-0.297,0.748-0.457c0.357-0.169,0.512-0.335,0.882-0.526c0.319-0.199,0.601-0.316,0.875-0.4
c0.303-0.093,0.788,0.162,1.483,0.213c6.163,0.487,13.141-4.136,18.909-4.159c2.469-0.305,22.718-1.619,22.718-1.619
c13.208-0.746,29.064-1.454,38.962-1.56c1.964-0.006,3.845-0.04,5.639-0.045c7.445-0.052,13.578-0.111,17.98-0.723
c0.138-0.019,3.573-0.261,5.988-1.988c0.808-0.582,1.491-1.359,1.629-2.562c0.042-0.369,0.015-2.078-0.439-3.113
c-0.526-1.334-1.37-3.269-3.351-5.662c-1.353-2.023-5.865-5.111-7.46-5.977c-2.203-1.274-4.845-2.336-7.348-3.18
c-1.421-0.479-8.745-2.14-12.822-1.899c-1.662-0.196-8.437,0.341-9.561,0.474c-3.146,0.371-9.199,1.696-12.647,2.671
c-5.236,1.447-10.908,3.424-17.317,5.982c-0.734,0.267-1.435,0.579-2.129,0.835L120.335,21c-0.789,0.391-3.916,1.89-4.403,2.182
l-0.645,0.356c-0.001,0.001-2.164,0.915-2.663,0.746c-0.085-0.029-0.853-0.539-1.137-0.656c-2.036-1.176-4.377-0.989-5.055-0.802
c-1.199,0.332-2.275,0.916-2.68,1.254c-0.831,0.696-1.324,1.711-1.338,1.774c-0.044,0.186-0.181,0.531-0.189,0.654
c-4.413,2.428-25.302,3.867-33.815,4.304c-11.743,0.603-27.824,0.282-37.338-0.282c0.007-0.002,0.016-0.005,0.024-0.007
c-0.56-0.013-1.303-0.026-1.948-0.131c-0.778-0.035-0.778-0.035-1.306-0.085c-0.633-0.059-1.805-0.145-2.545-0.254
c-4.722-0.443-7.939-0.882-12.458-0.543C9.852,29.735-0.234,30.579,0.004,39.378 M24.312,52.881
c-5.824,0.517-10.87-0.323-14.738-2.31c5.978,0.428,12.313,0.364,18.589-0.311c0.026,0.01,0.059,0.004,0.078-0.002
c0.318-0.1,0.532-0.42,0.489-0.735c-0.033-0.372-0.357-0.645-0.711-0.592c-6.994,0.725-14.153,0.826-20.659,0.228
c-0.729-0.521-1.34-1.148-1.915-1.769c7.884,0.449,21.055,0.239,35.077-1.862c0.022,0.003,0.075,0.009,0.1,0.007
c0.313-0.107,0.532-0.388,0.476-0.75c-0.05-0.39-0.373-0.625-0.763-0.576c-14.659,2.194-28.411,2.289-36.033,1.733
c-0.411-0.581-0.827-1.208-1.136-1.88c8.745,0.852,35.167,0.56,58.306-5.022l0.005-0.001c0.37-0.102,0.573-0.445,0.501-0.821
c-0.085-0.362-0.437-0.576-0.842-0.503c-23.562,5.712-50.602,5.858-58.481,4.954c-0.233-0.6-0.49-1.173-0.653-1.841
C1.976,40.66,2,40.512,1.972,40.346c5.944,0.286,18.407,0.426,29.488-0.591c0.022-0.007,0.065-0.008,0.082-0.024
c0.331-0.081,0.547-0.368,0.498-0.682c-0.032-0.372-0.352-0.69-0.736-0.631c-11.083,1.007-23.591,0.891-29.417,0.596
c-0.123-3.271,1.759-5.484,5.927-6.653c3.308-0.934,8.711-0.788,13.936-0.195c11,1.25,31.014,1.543,43.171,0.814
c12.299-0.737,30.204-2.606,37.454-4.794c0.007,0.078,0.782,2.123,1.211,2.671c0.023,0.028,0.027,0.043,0.045,0.064
c-5.744,3.553-18.25,8.608-33.422,12.936C50.237,49.556,29.319,52.437,24.312,52.881z M126.565,26.469
c-1.307,0.139-2.482,0.311-3.549,0.451c0.882-0.334,1.747-0.699,2.622-1.111l1.112,0.631
C126.727,26.444,126.617,26.464,126.565,26.469z M131.479,26.047l-2.687-1.559c0.721-0.322,1.548-0.668,2.373-0.984l3.955,2.252
C133.797,25.835,132.582,25.929,131.479,26.047z M139.692,25.414l-5.569-3.164c1.071-0.433,2.175-0.913,3.346-1.35l6.844,4.219
C142.703,25.23,141.2,25.313,139.692,25.414z M148.457,24.864l-8.246-5.09c0.946-0.356,1.887-0.773,2.93-1.171
c0.064,0.168,0.156,0.317,0.305,0.439l9.32,5.587C151.305,24.711,149.858,24.776,148.457,24.864z M216.177,19.834
c-0.047,0.338-0.149,1.374-2.969,2.161c-2.875,0.806-7.532,1.055-13.33,1.163L185.5,14.507c-0.37-0.18-0.801-0.09-1.01,0.289
c-0.221,0.338-0.131,0.788,0.265,0.999l12.322,7.398c-1.496,0.034-3.022,0.061-4.632,0.081c-0.417-0.024-0.812-0.017-1.231-0.014
l-17.796-10.827c-0.383-0.215-0.887-0.063-1.093,0.28c-0.259,0.398-0.148,0.903,0.263,1.145l15.554,9.426
c-0.479,0.016-0.903,0.01-1.366,0.016c-1.291,0.01-2.725,0.054-4.172,0.094l-17.301-10.358c-0.43-0.254-1.001-0.102-1.218,0.324
c-0.271,0.415-0.145,0.961,0.319,1.22l14.869,8.886c-1.775,0.035-3.601,0.09-5.475,0.159l-15.69-9.109
c-0.435-0.276-1.05-0.107-1.294,0.357c-0.297,0.457-0.117,1.05,0.358,1.317l12.997,7.584c-1.625,0.078-3.276,0.124-4.913,0.201
l-13.099-7.826c-0.524-0.279-1.16-0.124-1.448,0.369c-0.297,0.491-0.118,1.131,0.387,1.435l10.346,6.208
c-1.576,0.051-3.139,0.15-4.698,0.208l-11.222-6.732c0.081-0.054,0.17-0.079,0.257-0.123c21.108-8.252,42.737-14.648,57.974-5.288
c0.142,0.092,0.35,0.162,0.558,0.103c0.131-0.042,0.271-0.147,0.356-0.303c0.214-0.316,0.112-0.763-0.197-0.929
c-15.741-9.702-37.782-3.25-59.175,5.166c-11.583,4.543-17.978,7.265-24.736,10.153l-5.745,3.312
c-0.044,0.014-0.142,0.049-0.173,0.064c-0.738,0.183-1.397,0.371-2.015,0.523c-0.859,0.255-1.669,0.465-2.53,0.619
c-1.61,0.37-3.342-0.035-4.504-0.918c-0.473-0.417-0.876-0.88-1.083-1.516c-0.236-0.445-0.052-1.482-0.018-1.631
c0.466-1.314,1.466-1.939,2.299-2.19c1.635-0.437,3.65,0.005,4.941,1.057l0.244,0.153c0.231,0.08,0.931,0.243,2.212-0.118
c0.746-0.204,3.869-2.324,4.752-2.781c2.613-1.39,8.317-3.862,28.76-12.029c6.353-2.525,11.931-4.451,17.108-5.911
c13.508-3.788,23.687-4.122,34.09-1.14C210.282,7.253,216.641,15.415,216.177,19.834z M25.297,30.053 M25.297,30.053
M25.297,30.053 M25.297,30.053 M25.297,30.053 M25.297,30.053 M25.297,30.053"/>
</g>
<g id="图层_2">
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

@ -0,0 +1,31 @@
<template>
<div ref="appbase">
<el-container>
<el-header>
<FirHeader></FirHeader>
</el-header>
<div class="pbody">
<router-view></router-view>
</div>
</el-container>
</div>
</template>
<script>
import FirHeader from "@/components/FirHeader";
export default {
name: "FirAppBase",
components: {FirHeader},
mounted() {
}, watch: {}
}
</script>
<style scoped>
.pbody {
padding-top: 20px;
}
</style>

@ -0,0 +1,327 @@
<template>
<el-main>
<div class="page-app app-info">
<div class="banner">
<div class="middle-wrapper" ref="mwp">
<div @click="defaulttimeline">
<img :src="icon_url" class="appicon" style="width:100px; height:100px">
</div>
<div class="badges">
<span class="bundleid">SHORT<b class="short">&nbsp;&nbsp;{{ appinfos.short }}</b></span>
<span>{{ master_release.release_type |getapptype }}</span>
<span><i class="el-icon-cloudy"></i><b class="ng-binding">{{ appinfos.count_hits }}</b></span>
<span class="bundleid ng-binding">BundleID<b class="ng-binding">&nbsp;&nbsp;{{ appinfos.bundle_id }}</b></span>
<span class="version ng-scope">{{ master_release.minimum_os_version }}&nbsp; 或者高版本</span>
</div>
<div class="actions">
<!-- <el-button class="upload" icon="el-icon-cloudy">-->
<!-- 上传新版本-->
<!-- </el-button>-->
<el-button @click="appDownload" class="download" icon="el-icon-view">
预览
</el-button>
</div>
<div class="tabs-container">
<el-row :gutter="1">
<el-col :span="3">
<a class="" ref="baseinfo" @click="baseinfo"><i class="el-icon-document"></i>基本信息</a>
</el-col>
<el-col :span="3">
<a class="" ref="combo" @click="combo"><i class="el-icon-copy-document"
style="transform:rotateX(180deg);"></i>应用合并</a>
</el-col>
</el-row>
</div>
</div>
</div>
<div>
<div class="block" style="margin-top: -46px;color: #d5f9f9">
<el-slider
v-model="$store.state.appInfoIndex[0]"
range
:show-tooltip="false"
:change="slidevents()"
:max="100">
</el-slider>
</div>
<el-container style="padding-top: 20px;max-width: 96%">
<router-view></router-view>
</el-container>
</div>
</div>
</el-main>
</template>
<script>
import {getappinfos} from "../restful";
export default {
name: "FirAppInfosBase",
data() {
return {
icon_url:"",
appinfos: {},
master_release: {},
allapp: [],
activity: {
editing: false
},
slidvalue: [[5, 5], [5, 5]]
}
},
methods: {
appDownload() {
this.$router.push({name: 'FirDownload', params: { short: this.appinfos.short }})
},
combo() {
this.$router.push({name: 'FirAppInfoscombo'});
this.$store.dispatch('doappInfoIndex', [[31, 31], [31, 31]]);
this.$refs.combo.classList.add('active');
this.$refs.baseinfo.classList.remove('active');
},
baseinfo() {
this.$refs.baseinfo.classList.add('active');
this.$refs.combo.classList.remove('active');
this.$router.push({name: 'FirAppInfosbaseinfo'});
this.$store.dispatch('doappInfoIndex', [[18, 18], [18, 18]]);
},
slidevents() {
// let index = this.$store.state.appInfoIndex[1];
// this.$store.dispatch('doappInfoIndex',[index,index]);
// eslint-disable-next-line no-console
// console.log(index);
},
defaulttimeline() {
this.$refs.baseinfo.classList.remove('active');
this.$refs.combo.classList.remove('active');
this.$router.push({name: 'FirAppInfostimeline'});
this.$store.dispatch('doappInfoIndex', [[5, 5], [5, 5]]);
},
}, created() {
}, filters: {
getapptype: function (type) {
let ftype = '';
if (type === 0) {
ftype = 'Android'
} else {
ftype = 'iOS'
}
return ftype
},
},
computed: {
getBH: function () {
let sch = this.$refs.appinfomain.scrollHeight;
if (sch === 0 || sch < window.innerHeight) {
sch = window.innerHeight;
} else {
sch += 130;
}
return sch;
}
}, mounted() {
getappinfos(data => {
if (data.code === 1000) {
this.appinfos = data.data;
this.master_release = data.data.master_release;
this.$store.dispatch("getUser",data.userinfo);
this.appinfos["icon_url"] = this.master_release.icon_url;
this.$store.dispatch('doucurrentapp', this.appinfos);
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
} else {
// eslint-disable-next-line no-console
console.log("失败了");
}
}, {
"app_id": this.$route.params.id
});
this.icon_url = this.$store.state.currentapp.master_release.icon_url
// this.$store.dispatch('dosetAh',this.getBH);
},watch:{
'$store.state.currentapp.master_release.icon_url':function () {
this.icon_url = this.$store.state.currentapp.master_release.icon_url
}
}
}
</script>
<style scoped>
.el-main {
margin: 10px auto 100px;
width: 1166px;
position: relative;
padding-bottom: 1px;
background-color: #bfe7f9;
color: #9b9b9b;
-webkit-font-smoothing: antialiased;
border-radius: 1%;
}
.appicon {
overflow: hidden;
margin-left: 10px;
border-radius: 17.544%;
}
.page-app {
padding-bottom: 0
}
.page-app .banner {
padding-top: 60px;
padding-bottom: 40px;
border-bottom: 1px solid rgba(208, 208, 208, .5);
background-color: #d5f9f9;
border-radius: 10px;
}
.page-app .banner .actions {
position: absolute;
right: 0;
top: 0;
}
.page-app .banner .actions .download, .page-app .banner .actions .upload {
display: block;
min-width: 150px;
padding: 10px 20px;
border-radius: 5px;
font-size: 14px;
margin: 0 12px 12px 12px
}
.page-app .banner .actions .upload {
background-color: #3ab2a7;
border: 1px solid transparent;
color: #fff;
cursor: pointer
}
.page-app .banner .actions .download {
text-align: center;
text-decoration: none;
color: #3ab2a7;
border: 1px solid;
background-color: transparent
}
.page-app .banner .middle-wrapper {
position: relative
}
.page-app .badges {
margin-left: 160px;
font-size: 12px;
line-height: initial;
position: relative;
margin-top: -100px;
}
.page-app .badges > span {
position: relative;
display: inline-block;
margin-right: 8px;
padding: 4px 8px;
border: 1px solid;
border-radius: 5px
}
.page-app .badges .short {
color: #6f6ef8
}
.page-app .badges b {
display: inline-block;
padding-left: 12px;
height: 100%;
font-weight: 400
}
.page-app .badges b:before {
position: absolute;
top: 0;
width: 0;
height: 100%;
border-left: 1px solid;
content: ' ';
margin-left: -6px
}
.page-app .tabs-container {
margin-top: 40px;
margin-left: 160px
}
.page-app .tabs-container .el-row {
margin: 10px auto;
}
.page-app .tabs-container .el-row .el-col {
margin-right: 30px;
border-left: 1px solid;
}
.page-app .tabs-container .el-row .el-col a {
display: block;
padding-left: 15px;
color: #599b1a;
text-decoration: none;
-webkit-transition: all .5s;
transition: all .5s
}
.page-app .tabs-container .el-row .el-col a > i {
display: block;
margin-bottom: 14px;
height: 22px;
font-size: 22px
}
.page-app .tabs-container .el-row .el-col a.active {
color: #4a4a4a
}
.page-app .tabs-container .el-row .el-col:nth-child(5) {
display: none
}
.page-app .has-devices .tabs-container .el-row .el-col:nth-child(5) {
display: inline-block
}
</style>

@ -0,0 +1,217 @@
<template>
<div style="margin-top: 20px;width: 56%;margin-left: 8%">
<el-form ref="form" label-width="80px">
<el-form-item label="应用ID">
<el-row>
<el-col :span="19">
<el-input v-model="currentapp.app_id" :disabled="true"></el-input>
</el-col>
<el-col :span="4">
<el-button type="danger" plain @click="delApp"
style="margin:0 8px;border-radius:4px;cursor:pointer;height: 40px">删除应用
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="应用名称">
<el-input v-model="currentapp.name"></el-input>
</el-form-item>
<el-form-item label="短链接">
<el-input v-model="currentapp.short" maxlength="16" show-word-limit type="text">
<template slot="prepend">Http://</template>
</el-input>
</el-form-item>
<el-form-item label="应用图标">
<el-upload
class="avatar-uploader"
:action="getuppicurl"
:show-file-list="false"
accept=".png , .jpg , .jpeg"
:headers="uploadconf.AuthHeaders"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
<img v-if="currentapp.icon_url" :src="currentapp.icon_url"
class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="应用描述">
<el-input type="textarea" v-model="currentapp.description"
:autosize="{ minRows: 6, maxRows: 18}"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveappinfo">保存</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {deleteapp, getappinfos, getapppicurl, updateapp} from "../restful"
export default {
name: "FirAppInfosbaseinfo",
data() {
return {
currentapp: {},
imageUrl: "",
uploadconf:{},
}
},
methods: {
delApp() {
//APP
this.$confirm('确认删除 ' + this.currentapp.name + ' ?')
// eslint-disable-next-line no-unused-vars
.then(res => {
this.willDeleteApp = false;
deleteapp(data => {
if (data.code === 1000) {
this.$message({
message: '删除成功',
type: 'success'
});
this.$router.push({name: 'FirApps'});
} else {
this.$message({
message: '删除失败,请联系管理员',
type: 'error'
});
}
}, {
"app_id": this.currentapp.app_id
});
})
// eslint-disable-next-line no-unused-vars
.catch(err => {
this.willDeleteApp = false;
});
// alert('APP',this.delapp.name)
},
updateinfo(){
getappinfos(data => {
if (data.code === 1000) {
this.appinfos = data.data;
this.master_release = data.data.master_release;
this.$store.dispatch("getUser",data.userinfo);
this.appinfos["icon_url"] = this.master_release.icon_url;
this.$store.dispatch('doucurrentapp', this.appinfos);
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
} else {
// eslint-disable-next-line no-console
console.log("失败了");
}
}, {
"app_id": this.currentapp.app_id
});
},
saveappinfo() {
updateapp(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
}else {
this.$message.error('操作失败,'+data.msg);
}
}, {
"app_id": this.currentapp.app_id,
"data": {
"description": this.currentapp.description,
"short": this.currentapp.short,
"name": this.currentapp.name,
}
});
},
handleAvatarSuccess(res, file) {
this.imageUrl = URL.createObjectURL(file.raw);
this.$message({
message: '应用图标上传成功',
type: 'success'
});
this.updateinfo();
},
beforeAvatarUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 2;
if(file.type === 'image/jpeg' || file.type === 'image/png'|| file.type === 'image/jpg'){
if (isLt2M) {
return true;
}
else{
this.$message.error('上传头像图片大小不能超过 2MB!');
}
}else {
this.$message.error('上传头像图片只能是 JPG/PNG/JPEG 格式!');
}
return false;
}
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[18, 18], [18, 18]]);
if(!this.currentapp.app_id){
this.currentapp = this.$store.state.currentapp;
}
this.uploadconf = {
"AuthHeaders": {"Authorization": this.$cookies.get("auth_token")}
};
},
watch: {
'$store.state.currentapp': function () {
this.currentapp = this.$store.state.currentapp;
}
},computed:{
getuppicurl(){
return getapppicurl(this.currentapp.app_id)
}
}
}
</script>
<style scoped>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
height: 100px;
width: 100px;
border-radius: 10px;
display: block;
}
</style>

@ -0,0 +1,411 @@
<template>
<div class="apps-app-combo page-tabcontent ">
<div class="middle-wrapper">
<div v-if="has_combo " class="request-wrapper">
<p class="lead text-center ng-scope">已经与 <b>{{ has_combo.name }}</b> 合并</p>
<table>
<tr>
<td><span class="type">{{ currentapp.type|getapptype}}</span></td>
<td></td>
<td><span class="type">{{ has_combo.type|getapptype}}</span></td>
</tr>
<tr>
<td>
<div class="icon"><img class="ng-isolate-scope" :src="cmaster_release.icon_url">
</div>
</td>
<td><i class="icon-combo"></i></td>
<td>
<div class="icon"><img class="ng-isolate-scope" :src="hmaster_release.icon_url">
</div>
</td>
</tr>
<tr>
<td colspan="3" class="actions">
<!-- <a class="btn btn-link " @click="each_confirm"><b>解除合并</b></a>-->
<el-button round @click="each_confirm">解除合并</el-button>
</td>
</tr>
</table>
</div>
<div v-else>
<div class="icon-container text-center">
<img width="128" height="128" :src="cmaster_release.icon_url">
</div>
<div class="apps-list">
<div class="known-apps">
<p class="lead ng-binding">
<b>选择已有的应用进行合并</b>
</p>
<div class="apps">
<b v-if="comboapplists.length ===0 ">暂无可以合并的应用</b>
<div class="app ng-scope" v-for="comboapp in comboapplists" :key="comboapp.app_id"
@click="each_add(comboapp.app_id)">
<div class="icon">
<img class="ng-isolate-scope" :src="comboapp.master_release|geticon_url">
</div>
<p class="ng-binding">{{ comboapp.name }}</p></div>
</div>
</div>
<span class="hr-vertical"></span>
<div class="find-apps">
<p class="lead ">
<b>输入需要合并的应用的短链接或者名字</b>
</p>
<div class="form-group">
<el-input
clearables
prefix-icon="el-icon-search"
placeholder="输入短链接或者名字"
v-model="searchKey"
@click="searchapps"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {getappinfos, getapps, updateapp} from "../restful";
export default {
name: "FirAppInfoscombo"
, mounted() {
this.$store.dispatch('doappInfoIndex', [[31, 31], [31, 31]]);
// this.getappiconFun();
this.setData();
}, data() {
return {
searchKey: '',
currentapp: {},
has_combo: {},
cmaster_release: {},
hmaster_release: {},
comboapplists: [],
orgapplists: [],
comboapp:{"master_release":""}
}
}, methods: {
searchapps() {
let keysearch = this.searchKey.replace(/^\s+|\s+$/g, "");
let newapplists = [];
for (let i = 0; i < this.orgapplists.length; i++) {
if (this.orgapplists[i].short.search(keysearch) === 0 || this.orgapplists[i].name.search(keysearch) === 0) {
newapplists.push(this.orgapplists[i]);
}
}
if (keysearch === "") {
this.comboapplists = this.orgapplists.slice();
} else {
this.comboapplists = newapplists.slice();
}
},
each_add(hcombo_id) {
updateapp(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
this.comboapplists = [];
getappinfos(data => {
let appinfos = {};
appinfos = data.data;
appinfos["icon_url"] = appinfos.master_release.icon_url;
this.$store.dispatch('doucurrentapp', appinfos);
if (data.code === 1000) {
this.$store.dispatch('doucurrentapp', data.data);
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
} else {
// eslint-disable-next-line no-console
console.log("失败了");
}
}, {
"app_id": this.currentapp.app_id
});
this.setData();
}
}, {
"app_id": this.currentapp.app_id,
"data": {
"has_combo": {
"hcombo_id": hcombo_id,
"action": "COMBO",
}
}
});
},
each_confirm() {
updateapp(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
let currentapp = this.$store.state.currentapp;
this.has_combo = this.$store.state.currentapp.has_combo;
this.has_combo = null;
currentapp.has_combo = null;
this.hmaster_release = null;
this.$store.dispatch('doucurrentapp', currentapp);
this.getappiconFun();
}
}, {
"app_id": this.currentapp.app_id,
"data": {
"has_combo": {
"hcombo_id": this.has_combo.app_id,
"action": "UNCOMBO",
}
}
});
},
setData() {
this.currentapp = this.$store.state.currentapp;
this.cmaster_release = this.$store.state.currentapp.master_release;
if (this.$store.state.currentapp.has_combo) {
this.has_combo = this.$store.state.currentapp.has_combo;
this.hmaster_release = this.$store.state.currentapp.has_combo.master_release;
} else {
this.has_combo = null;
this.hmaster_release = null;
this.getappiconFun();
}
},
findByShort(e) {
alert(111, e)
},
getappiconFun() {
let type = "android";
if (this.currentapp.type === 0) {
type = "ios"
}
getapps(data => {
if (data.code === 1000) {
this.comboapplists = data.data;
this.orgapplists = this.comboapplists.slice(); //
} else {
this.$router.push({name: 'FirLogin'});
}
}, {"type": type,"page":1,"size":999,"act":'combo'});
}
}, filters: {
geticon_url(app){
return app.icon_url
},
getapptype: function (type) {
let ftype = '';
if (type === 0) {
ftype = 'Android'
} else {
ftype = 'iOS'
}
return ftype
},
},
watch: {
'$store.state.currentapp': function () {
this.setData();
},
// eslint-disable-next-line no-unused-vars
searchKey: function (val, oldVal) {
this.searchapps()
},
}
}
</script>
<style scoped>
.text-center {
text-align: center
}
.middle-wrapper {
margin: 0 auto;
width: 1100px
}
.apps-app-combo {
position: relative;
padding-top: 45px
}
.apps-app-combo .icon-container {
margin-bottom: 60px
}
.apps-app-combo .icon-container img {
border-radius: 17.544%
}
.apps-app-combo .apps-list {
position: relative;
overflow: hidden
}
.apps-app-combo .lead {
margin-bottom: 30px;
font-size: 16px
}
.apps-app-combo .lead b {
max-width: 300px;
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: top
}
.known-apps {
text-align: center;
float: left;
width: 45%;
margin: 10px 30px;
}
.apps-app-combo .known-apps .icon img {
width: 80px;
height: 80px;
border-radius: 22.7%
}
.apps-app-combo .known-apps .apps .app {
display: inline-block;
margin: 0 0 20px 28px;
text-align: center;
cursor: pointer;
vertical-align: top;
}
.apps-app-combo .known-apps .apps .app > p {
margin-top: 8px;
font-size: 12px;
text-align: center;
display: inline-block;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal
}
.apps-app-combo .find-apps {
/*padding-left: 50px;*/
text-align: center;
margin: 10px 30px;
float: right;
width: 40%;
}
.apps-app-combo .find-apps .lead {
text-align: left;
}
.apps-app-combo .find-apps .form-group {
position: relative;
margin-bottom: 36px;
width: 260px;
}
.apps-app-combo .hr-vertical {
position: absolute;
z-index: 12;
width: 1px;
height: 100%;
background-color: #d5f9f9
}
.apps-app-combo .request-wrapper .lead {
margin-bottom: 80px;
font-size: 18px
}
.apps-app-combo .request-wrapper table {
margin: 0 auto;
width: 400px
}
.apps-app-combo .request-wrapper table tr:first-child td span {
display: inline-block;
margin-bottom: 12px;
font-size: 16px
}
.apps-app-combo .request-wrapper table td {
padding: 0 15px;
text-align: center
}
.apps-app-combo .request-wrapper table td img {
width: 120px;
height: 120px;
border-radius: 17.54%
}
.apps-app-combo .request-wrapper table .actions {
padding-top: 40px
}
.apps-app-combo .request-wrapper table .actions .btn {
margin-bottom: 20px;
min-width: 100px;
border-radius: 30px
}
.apps-app-combo .request-wrapper table .actions .btn-link {
color: #a9b1b3;
text-decoration: none;
border: 1px solid transparent;
-webkit-transition: border-color .25s;
transition: border-color .25s
}
.apps-app-combo .request-wrapper table .actions .btn-link:hover {
border-color: #a9b1b3
}
.apps-app-combo .request-wrapper table i.icon-combo {
font-size: 30px
}
.apps-app-combo .combo-loading-wrap {
margin-bottom: 8px
}
.apps-app-combo .combo-loading-wrap > .icon {
display: inline-block;
-webkit-animation: rotating 1s linear infinite;
animation: rotating 1s linear infinite
}
</style>

@ -0,0 +1,555 @@
<template>
<el-timeline>
<el-timeline-item v-for="(app) in release_apps" :key="app.release_id"
:timestamp="app.created_time|formatdatatimeline"
:color="app.master_color"
placement="top">
<el-card>
<div class="directive-view-release">
<i class="el-icon-cloudy" v-if="! app.is_master"></i>
<i class="el-icon-cloudy" style="background-color: #409eff" v-else></i>
<b class="ng-binding">{{app.build_version}} (Build {{app.app_version}})</b>
<div class="release-metainfo ng-hide"
v-if="app.release_type === 0">
<small>
<i class="icon-calendar"></i>
<span class="el-icon-date">&nbsp;{{app.created_time|formattimeline}}</span>
</small>
</div>
<div class="release-metainfo" v-else>
<small>
<i class="icon-calendar"></i>
<span class="el-icon-date">&nbsp;{{app.created_time|formattimeline}}</span>
</small> &nbsp;&nbsp;·&nbsp;&nbsp;
<small>{{app.release_type|getiOStype}}</small>
<!-- <i-->
<!-- v-if="app.changelog" class="ng-hide">&nbsp;&nbsp;·&nbsp;&nbsp;</i>-->
<!-- <small v-if="app.changelog"-->
<!-- ng-bind="activity.distribution_name"-->
<!-- class="ng-binding ng-hide"> {{app.changelog}}</small>-->
</div>
<p>{{app.changelog}}</p>
<textarea v-if="app.editing.changelog"
v-model="app.changelog"
placeholder="更新日志" >
</textarea>
<div class="release-actions editing " v-if="app.editing.changelog">
<button class="btn-cancel" @click="endEdit(app,'changelog')"><span
>取消</span></button>
<button class="btn-save" @click="updateChangelog(app,'changelog')"><span
>保存</span></button>
</div>
<el-input v-if="app.editing.binary_url"
v-model="app.binary_url"
placeholder="下载地址,默认本服务器,填写第三方可以 自动跳转到第三方平台" >
</el-input>
<div class="release-actions editing " v-if="app.editing.binary_url">
<button class="btn-cancel" @click="endEdit(app,'binary_url')"><span
>取消</span></button>
<button class="btn-save" @click="updateChangelog(app,'binary_url')"><span
>保存</span></button>
</div>
<div class="release-actions" v-show="!app.editing.changelog && !app.editing.binary_url ">
<el-tooltip class="tooltip-top" content="编辑更新日志" placement="top">
<el-button @click="startEdit(app,'changelog')"
tooltip="编辑更新日志"
class="tooltip-top ">
<i class="el-icon-edit"></i>
</el-button>
</el-tooltip>
<el-tooltip class="tooltip-top" content="下载原文件" placement="top">
<el-button class="tooltip-top" tooltip="下载原文件"
@click="downloadPackage(app)">
<i class="el-icon-download"></i>
<span >{{app.binary_size}}</span>
</el-button>
</el-tooltip>
<el-tooltip class="tooltip-top" :content="app.binary_url|downcontent" placement="top">
<el-button class="tooltip-top" tooltip="修改下载地址"
@click="startEdit(app,'binary_url')"><i
class="el-icon-link"></i> <span >下载地址</span>
</el-button>
</el-tooltip>
<el-button class="tooltip-top" @click="previewRelase(app)"><i
class="el-icon-view"></i> <span class="ng-binding">预览</span>
</el-button>
<el-button v-if="! app.is_master" class="tooltip-top" @click="make_master_release(app)">
<i class="el-icon-view"></i>
<span class="ng-binding ng-scope">标记上线</span>
</el-button>
<el-button v-if="(! app.is_master )|| (app.is_master && release_apps.length === 1)"
class="tooltip-top" @click="del_release_app(app)">
<i class="el-icon-delete"></i>
<span class="ng-binding ng-scope">删除</span>
</el-button>
</div>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</template>
<script>
import {deletereleaseapp, getapptimeline, updatereleaseapp} from "../restful"
export default {
name: "FirAppInfostimeline",
data() {
return {
release_apps: [],
currentapp: {},
activity: {
editing: {'changelog':false,'binary_url':false}
},
updatas:{},
}
},
methods: {
downloadPackage(app){
window.location.href=app.download_url;
},
previewRelase(app){
this.$router.push({name: 'FirDownload', params: { short: this.currentapp.short },query:{release_id:app.release_id}})
},
getapptimelineFun() {
getapptimeline(data => {
if (data.code === 1000) {
this.release_apps = data.data.release_apps;
this.currentapp = data.data.currentapp;
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
}
}, {
"app_id": this.$route.params.id,
"action": "timeline",
"methods": false
})
},
del_release_app(app) {
//APP
this.$confirm('确认删除 ' + this.currentapp.name + '下 当前 release 版本吗?')
// eslint-disable-next-line no-unused-vars
.then(res => {
deletereleaseapp(data => {
if (data.code === 1000) {
this.$message({
message: '删除成功',
type: 'success'
});
this.getapptimelineFun();
} else {
this.$message({
message: '删除失败,请联系管理员',
type: 'error'
});
}
}, {
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
});
});
},
updatereleaseappFun(params) {
updatereleaseapp(data => {
if (data.code === 1000) {
this.$message({
message: '更新成功',
type: 'success'
});
this.release_apps = data.data.release_apps;
this.currentapp = data.data.currentapp;
// this.getapptimelineFun();
// this.currentapp["icon_url"] = this.currentapp.master_release.icon_url;
// this.$store.dispatch('doucurrentapp', this.currentapp);
} else {
this.$message({
message: '更新失败,请联系管理员',
type: 'error'
});
}
}, params
);
},
make_master_release(app) {
this.updatereleaseappFun({
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
"data": {
"make_master": app.release_id
}
});
},
endEdit(app,type) {
if(type==='changelog'){
app.editing.changelog = false;
}else if(type === 'binary_url'){
app.editing.binary_url = false;
}
},
updateChangelog(app,type) {
if(type==='changelog'){
this.activity.editing.changelog = false;
this.updatas = {"changelog": app.changelog}
}else if(type === 'binary_url') {
this.activity.editing.binary_url = false;
this.updatas = {"binary_url": app.binary_url}
}
this.updatereleaseappFun({
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
"data": this.updatas
});
this.updatas={}
},
startEdit(app,type) {
if (type === 'changelog') {
app.editing.changelog = true;
} else if (type === 'binary_url') {
app.editing.binary_url = true;
}
}
}, created() {
}, watch: {},
computed: {}, mounted() {
this.$store.dispatch('doappInfoIndex', [[5, 5], [5, 5]]);
this.getapptimelineFun();
}, filters: {
downcontent(content){
if(content){
return content
}else{
return "修改下载地址"
}
},
formatdatatimeline: function (timestr) {
return timestr.split("T")[0];
},
formattimeline: function (timestr) {
return timestr.split(".")[0].split("T")[1];
},
getiOStype: function (type) {
let ftype = '';
if (type === "Adhoc") {
ftype = '内测版'
} else {
ftype = '企业版'
}
return ftype
},
}
}
</script>
<style scoped>
.directive-view-release {
position: relative;
padding-left: 80px;
color: #9b9b9b;
}
.directive-view-release > i {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
height: 50px;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
position: absolute;
left: 0;
z-index: 2;
width: 50px;
border: 1px solid rgba(151, 151, 151, .2);
border-radius: 50%;
background-color: #f6f6f6;
text-align: center;
font-size: 22px
}
.directive-view-release > i:before {
display: inline-block;
margin-top: 2px;
margin-left: 2px
}
.directive-view-release .release-metainfo {
margin-top: 2px
}
.directive-view-release .release-metainfo small {
display: inline-block;
vertical-align: middle;
margin: 8px 0;
line-height: 14px
}
.directive-view-release .release-metainfo small i, .directive-view-release .release-metainfo small span {
display: inline-block;
vertical-align: middle
}
.directive-view-release .release-metainfo small i {
margin-right: 2px;
line-height: 14px
}
.directive-view-release > b {
display: inline-block;
vertical-align: middle;
margin-right: 30px;
color: #4a4a4a;
font-weight: 400;
font-size: 20px
}
.directive-view-release pre, .directive-view-release textarea {
margin: 14px 0;
padding: 0;
border: 0;
background-color: transparent;
color: #4a4a4a;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
}
.directive-view-release textarea {
padding: 12px 16px;
width: 500px;
height: 120px;
border-radius: 5px;
resize: none;
border: 1px solid #bdc6c7;
color: #555;
font-size: 16px
}
.directive-view-release .tooltip-top {
position: relative;
overflow: visible;
color: #9b9b9b;
}
.directive-view-release .edit-pen {
cursor: pointer
}
.directive-view-release .release-type {
position: relative;
display: inline-block;
margin-left: 8px;
padding: 2px 6px;
border: 1px solid;
border-radius: 5px
}
.directive-view-release .release-info {
margin-top: 6px
}
.directive-view-release .release-info i, .directive-view-release .release-info span {
display: inline-block;
vertical-align: middle
}
.directive-view-release .release-info span {
margin-right: 12px;
margin-left: 4px
}
.directive-view-release .release-actions {
margin-top: 10px;
position: relative
}
.directive-view-release .release-actions .is-history {
display: inline-block
}
.directive-view-release .release-actions toggle {
margin-right: 8px
}
.directive-view-release .release-actions .comp-toggle.toggle-off {
background-color: #9b9b9b
}
.directive-view-release .release-actions a, .directive-view-release .release-actions button {
display: inline-block;
vertical-align: middle;
margin-right: 8px;
background-color: transparent;
border: 1px solid;
overflow: hidden;
border-radius: 17px;
padding: 4px 10px
}
.directive-view-release .release-actions .release-actions-group .mqc-wait-btn, .directive-view-release .release-actions .tooltip-top {
overflow: visible;
position: relative
}
.directive-view-release .release-actions a i, .directive-view-release .release-actions button i {
display: inline-block;
vertical-align: middle
}
.directive-view-release .release-actions a.btn-save, .directive-view-release .release-actions button.btn-save {
border-color: #3d6df8;
background-color: #92c1f8;
color: #fff
}
.directive-view-release .release-actions a.btn-cancel, .directive-view-release .release-actions button.btn-cancel {
border: 0;
}
.directive-view-release .release-actions a.btn-cancel:hover, .directive-view-release .release-actions button.btn-cancel:hover {
color: #686868
}
.directive-view-release .release-actions a {
color: #9b9b9b
}
.directive-view-release .release-actions a:focus, .directive-view-release .release-actions a:hover {
text-decoration: none
}
.directive-view-release .release-actions .has-text i {
border-right: 1px solid
}
.directive-view-release .release-actions.editing {
margin-top: 0;
text-align: right;
width: 500px
}
.time-line {
position: relative
}
.time-line:before {
position: absolute;
left: 25px;
z-index: 1;
height: 100%;
border-left: 1px solid rgba(151, 151, 151, .2);
content: ' '
}
.time-line li {
margin-top: 80px
}
.time-line li:first-child {
padding-left: 80px;
margin-top: 0
}
.time-line li:first-child .dot {
position: absolute;
left: 20px;
z-index: 2;
display: inline-block;
width: 10px;
height: 10px;
border: 1px solid rgba(151, 151, 151, .2);
border-radius: 50%;
background-color: #9b9b9b;
text-align: center
}
.time-line li:nth-child(2) {
margin-top: 20px
}
.time-line li:nth-child(3) {
margin-top: 40px
}
.time-line .filter {
position: relative;
top: -6px;
display: inline-block;
margin-right: 32px;
font-weight: 700;
cursor: pointer
}
.time-line .filter.active {
color: #4a4a4a
}
.time-line .filter.version-rollback .button {
display: inline-block;
vertical-align: middle;
background-color: transparent;
border: 1px solid;
padding: 4px 20px;
border-radius: 17px
}
.time-line .more button {
background: #f6f6f6;
border: 1px solid;
position: relative;
z-index: 99;
width: 160px;
padding: 10px 0;
border-radius: 40px
}
</style>

@ -0,0 +1,762 @@
<template>
<div>
<el-container >
<el-header style="height: 100px">
<el-dialog
:title="getDelappTitle"
:visible.sync="willDeleteApp"
width="50%"
>
<span>删除后不可恢复请谨慎操作</span>
<span slot="footer" class="dialog-footer">
<el-button @click="willDeleteApp = false"> </el-button>
<el-button type="danger" @click="delApp"> </el-button>
</span>
</el-dialog>
<el-row>
<el-col :span="4">
<el-radio-group v-model="searchfromtype">
<el-radio-button label="android" icon="el-icon-mobile-phone"><i class="iconfont icon-android2"/>
</el-radio-button>
<el-radio-button label="ios" ><i class="iconfont icon-ios"/>
</el-radio-button>
</el-radio-group>
</el-col>
<el-col :span="5">
<el-input
placeholder="请输入名称搜索"
v-model="keysearch"
@click="searchapps"
clearable prefix-icon="el-icon-search">
</el-input>
</el-col>
<el-col :span="7" class="surplus-card">
<el-row>
<el-col :span="8">
<div>
<span class="name">iOS应用</span>
<el-divider direction="vertical"></el-divider>
</div>
<div>
<span class="value">{{ hdata.ios_count }}</span>
<el-divider direction="vertical"></el-divider>
</div>
</el-col>
<el-col :span="8">
<div>
<span class="name">Android应用</span>
<el-divider direction="vertical"></el-divider>
</div>
<div>
<span class="value">{{ hdata.android_count }}</span>
<el-divider direction="vertical"></el-divider>
</div>
</el-col>
<el-col :span="8">
<div>
<span class="name">总应用</span>
<el-divider direction="vertical"></el-divider>
</div>
<div>
<span class="value">{{ hdata.ios_count + hdata.android_count}}</span>
<el-divider direction="vertical"></el-divider>
</div>
</el-col>
</el-row>
</el-col>
<el-col :span="8" class="surplus-card">
<el-row>
<el-col :span="8">
<div>
<span class="name">今日苹果下载次数</span>
<el-divider direction="vertical"></el-divider>
</div>
<div>
<span class="value">{{ hdata.ios_today_hits_count }}</span>
<el-divider direction="vertical"></el-divider>
</div>
</el-col>
<el-col :span="8">
<div>
<span class="name">今日安卓下载次数</span>
<el-divider direction="vertical"></el-divider>
</div>
<div>
<span class="value">{{ hdata.android_today_hits_count }}</span>
<el-divider direction="vertical"></el-divider>
</div>
</el-col>
<el-col :span="8">
<div>
<span class="name">总共下载次数</span>
<el-divider direction="vertical"></el-divider>
</div>
<div>
<span class="value">{{ hdata.all_hits_count}}</span>
<el-divider direction="vertical"></el-divider>
</div>
</el-col>
</el-row>
</el-col>
</el-row>
</el-header>
<div
ref="appmain" style="margin: 40px 20px">
<el-row style="max-height: 460px; margin: 0 auto;" :gutter="10" class="page-apps">
<el-col style="width: 33%;height: 460px ">
<div class=" app-animator appdownload">
<div class=" card app card-ios" style="padding: 0">
<el-upload
:on-success="handleAvataruploadsuccess"
drag
accept=".ipa , .apk"
:headers="uploadconf.AuthHeaders"
:action="uploadconf.UploadUrl"
multiple>
<i class="el-icon-upload" style="color: #fff"></i>
<div class="el-upload__text" style="color: #fff;margin-top: 20px">拖拽到这里上传</div>
</el-upload>
</div>
</div>
</el-col>
<el-col style="width: 33%;height: 460px"
v-for="(r,index) in applists" :key="r.id" @click="appInfos(index)">
<div class=" app-animator">
<div class="card app card-ios">
<i class=" type-icon iconfont icon-ios" v-if="r.type === 1" src=""></i>
<i class="type-icon iconfont icon-android2" v-if="r.type === 0"></i>
<div class="type-mark" v-if="r.type === 1"></div>
<div class="type-mark" style="border-top: 48px solid #A4C639" v-if="r.type === 0"></div>
<a class="appicon" @click="appInfos(r)">
<img class="icon ng-isolate-scope" width="100" height="100"
:src="r.master_release.icon_url|make_icon_url"></a>
<div class="combo-info ng-scope" v-if="r.has_combo !== null ">
<i class="el-icon-copy-document" style="transform:rotateX(180deg);"></i>
<a @click="appInfos(r.has_combo)">
<img class="icon ng-isolate-scope" width="45" height="45"
:src="r.has_combo.master_release.icon_url|make_icon_url">
</a>
</div>
<br>
<p class="appname"><i class="el-icon-user-solid"></i><span class="ng-binding">{{ r.name }}</span>
</p>
<table>
<tbody>
<tr>
<td class="ng-binding">应用大小</td>
<td><span
class="ng-binding">{{ r.master_release.binary_size }}</span>
</td>
</tr>
<tr>
<td class="ng-binding">应用平台</td>
<td><span class="ng-binding">{{ r.type |getapptype }}</span></td>
</tr>
<tr>
<td class="ng-binding">应用标识</td>
<td><span class="ng-binding">{{ r.bundle_id | autoformat }}</span></td>
</tr>
<tr>
<td class="ng-binding">最新版本</td>
<td><span class="ng-binding">{{ r.master_release.app_version }}Build {{ r.master_release.build_version }}</span>
</td>
</tr>
<tr v-if="r.type === 1">
<td class="ng-binding">打包类型</td>
<td><span class="ng-binding">
{{ r.master_release.release_type|getiOStype }}
</span></td>
</tr>
</tbody>
</table>
<div class="action">
<el-button @click="appInfos(r)">
<i class="icon-pen el-icon-edit"></i> 管理
</el-button>
<el-button @click="appDownload(r)" target="_blank" class="ng-binding">
<i class="icon-eye el-icon-view"></i> 预览
</el-button>
<el-button @click="DeleteApp(r)" class="btn btn-remove" icon="el-icon-delete"
circle></el-button>
</div>
</div>
</div>
</el-col>
</el-row>
</div>
</el-container>
</div>
</template>
<script>
import {getapps, deleteapp} from "../restful";
import {getScrollHeight,getScrollTop,getWindowHeight} from "../utils";
export default {
name: "FirApps",
data() {
return {
uploadconf: {"UploadUrl": ""},
keysearch: '',
searchfromtype: '',
applists: [],
orgapplists: [],
hdata: {},
willDeleteApp: false,
delapp: {},
has_next:false,
query:{'page':1,size:20},
searchflag:false,
uploadflag:false
}
}, methods: {
searchFun(){
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
if(keysearch === ''){
this.searchflag=false;
this.applists=[];
this.query.page=1;
if(this.searchfromtype){
this.getappsFun({"type": this.searchfromtype});
}else {
this.getappsFun({});
}
}else {
this.searchflag=true
}
if(this.searchflag){
if(this.searchfromtype){
this.getappsFun({"type": this.searchfromtype,'page':1,size:999});
}else {
this.getappsFun({'page':1,size:999});
}
}
},
auto_load(){
// eslint-disable-next-line no-console
// console.log(getScrollTop() , getWindowHeight(),getScrollTop() + getWindowHeight(), getScrollHeight());
if(getScrollTop() + getWindowHeight() >= getScrollHeight()){
if(this.has_next){ //
if(this.applists.length === 0){
this.query.page=1;
}else {
this.query.page+=1;
}
if(this.searchfromtype!=='') {
this.query.type = this.searchfromtype;
}
// this.getappsFun(dict(this.query,{"types": this.searchfromtype}));
// this.getappsFun({"type": this.searchfromtype});
// }else {
this.getappsFun(this.query);
// }
}else{
// eslint-disable-next-line no-console
console.log("end")
}
}
},
searchapps() {
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
let newapplists = [];
for (let i = 0; i < this.orgapplists.length; i++) {
if (this.orgapplists[i].name.search(keysearch) === 0) {
newapplists.push(this.orgapplists[i]);
}
}
if (keysearch === "") {
this.applists = this.orgapplists.slice();
} else {
this.applists = newapplists.slice();
}
},
getappsFun(parms) {
getapps(data => {
if (data.code === 1000) {
if(this.uploadflag){
this.applists = data.data ;
this.uploadflag = false;
}else {
this.applists = this.applists.concat(data.data) ;
}
this.has_next = data.has_next;
this.orgapplists = this.applists.slice(); //
this.hdata = data.hdata;
this.$store.dispatch("getUser",data.userinfo);
this.searchapps();
let upload_domain=data.hdata.upload_domain;
if(!upload_domain){
upload_domain=location.origin;
}
this.uploadconf = {
"UploadUrl": upload_domain+"/api/v1/fir/server/upload",
"AuthHeaders": {"Authorization": this.$cookies.get("auth_token")}
};
// this.$store.dispatch('doucurrentapp', {'firapps':1});
} else {
this.$router.push({name: 'FirLogin'});
}
}, parms);
},
// eslint-disable-next-line no-unused-vars
handleAvataruploadsuccess(response, file, fileList) {
if(response.code === 1000){
this.$message.success(file.name + '上传成功');
}else {
this.$message.error(file.name + '上传失败,'+response.msg);
}
for (let x = 0; x < fileList.length; x++) {
if (file.name === fileList[x].name) {
fileList.splice(x, 1)
}
}
this.uploadflag = true;
this.getappsFun({});
},
delApp() {
this.willDeleteApp = false;
// eslint-disable-next-line no-console
deleteapp(data => {
if (data.code === 1000) {
for(let i=0;i< this.applists.length;i++){
if(this.delapp.app_id === this.applists[i].app_id){
this.applists.splice(i,1);
this.orgapplists.splice(i,1);
}
}
this.$message.success(this.delapp.name+'删除成功');
this.delapp={};
} else {
this.$message.error('删除失败,请联系管理员');
}
}, {
"app_id": this.delapp.app_id
});
},
DeleteApp(delapp) {
//
this.willDeleteApp = true;
this.delapp = delapp;
},
appInfos(app) {
this.$router.push({name: 'FirAppInfostimeline', params: {id: app.app_id}})
},
appDownload(app) {
this.$router.push({name: 'FirDownload', params: { short: app.short }})
}
}, computed: {
getDelappTitle() {
return `删除应用 ${this.delapp.name}`
},
getBH: function () {
let sch = this.$refs.appmain.scrollHeight;
if (sch === 0 || sch < window.innerHeight) {
sch = window.innerHeight;
} else {
sch += 280;
}
return sch;
}
},
filters: {
getiOStype: function (type) {
let ftype = '';
if (type === 1) {
ftype = '内测版'
} else if(type === 2) {
ftype = '企业版'
}
return ftype
},
formatsize: function (size) {
return size / 1000;
},
autoformat: function (packname) {
if ((packname.length) > 20) {
return packname.split('').slice(0, 20).join('') + '...';
}
},
getapptype: function (type) {
let ftype = '';
if (type === 0) {
ftype = 'Android'
} else {
ftype = 'iOS'
}
return ftype
},
make_icon_url(icon_url){
if(!icon_url.startsWith("http")){
return location.origin+icon_url
}else {
return icon_url
}
}
}, mounted() {
// this.$store.dispatch('dosetAh',this.getBH);
window.addEventListener('scroll',this.auto_load);
this.getappsFun({});
},
destroyed(){
window.removeEventListener('scroll', this.auto_load, false);
},
watch: {
// eslint-disable-next-line no-unused-vars
keysearch: function (val, oldVal) {
// this.searchapps()
this.searchFun()
},
// eslint-disable-next-line no-unused-vars
searchfromtype: function (val, oldVal) {
this.applists=[];
this.query.page=1;
// this.keysearch='';
this.searchFun();
// this.getappsFun({"type": this.searchfromtype});
},
}
}
</script>
<style scoped>
.el-container {
margin: 10px auto 100px;
width: 1166px;
position: relative;
padding-bottom: 1px;
background-color: #bfe7f9;
color: #9b9b9b;
-webkit-font-smoothing: antialiased;
border-radius: 1%;
}
.el-header {
margin-top: 20px;
padding-top: 30px;
border-bottom: 1px solid rgba(208, 208, 208, .5);
background-color: #d5f9f9;
border-radius: 10px;
}
.surplus-card {
height: 40px;
text-align: right;
display: inline-block;
vertical-align: middle;
/*border-right: 1px solid #9b9b9b;*/
}
.surplus-card .name {
font-size: 12px;
color: #9b9b9b;
}
.surplus-card .value {
font-size: 16px;
color: #434343;
}
.appdownload {
width: 96%;
height: 96%;
margin: 2px auto; /*水平居中*/
/*background: #f8ba0b;*/
border-radius: 5px;
}
.appdownload /deep/ .el-upload-dragger {
width: 349.22px;
height: 430px;
background: #9cb8f8;
border: 0;
}
.appdownload /deep/ .el-icon-upload {
margin-top: 50%;
}
.appdownload /deep/ .el-upload-list__item-name {
margin-top: -43px;
}
.page-apps .card.app .action a, .page-apps .card.app .appname, .page-apps .card.app table tr td, .upload-modal .state-form .release-body .input-addon {
font-family: 'Open Sans', sans-serif
}
.page-apps .row-apps-top > div {
height: 100%
}
.page-apps .card.app .appicon img, .page-apps .card.app .combo-info img {
border-radius: 17.54%
}
.page-apps .card.app {
position: relative;
padding: 45px;
height: 340px;
background-color: #fff;
-webkit-transition: all .25s;
transition: all .25s
}
.page-apps .card.app:hover {
-webkit-transform: translateY(-4px);
transform: translateY(-4px);
box-shadow: 0 15px 30px rgba(0, 0, 0, .1)
}
.page-apps .card.app .type-icon {
position: absolute;
top: 9px;
right: 7px;
z-index: 2;
color: #fff;
}
.page-apps .card.app .type-mark {
position: absolute;
top: 0;
right: 0;
z-index: 1
}
.page-apps .card.app .type-mark i {
position: absolute;
top: 9px;
right: 7px
}
.page-apps .card.app .appicon {
display: inline-block;
width: 100px;
height: 100px;
cursor: pointer
}
.page-apps .card.app .appname {
margin-top: 36px;
font-size: 18px;
cursor: pointer;
display: inline-block
}
.page-apps .card.app .appname i {
color: #f8ba0b;
font-size: 16px;
margin-right: 6px
}
.page-apps .card.app:hover .appname {
color: #4a4a4a
}
.page-apps .card.app .combo-info {
display: inline-block;
margin-left: 26px;
vertical-align: bottom
}
.page-apps .card.app .combo-info i {
margin-right: 14px;
font-size: 20px
}
.page-apps .card.app table {
width: 100%;
table-layout: fixed
}
.page-apps .card.app table tr td {
padding: 2px 0;
font-size: 12px;
color: #9b9b9b;
}
.page-apps .card.app table tr td > span {
display: inline-block
}
.page-apps .card.app table tr td:last-child {
color: #1a1a1a;
width: 58%
}
.page-apps .card.app .action {
position: absolute;
padding: 40px 0 40px 40px;
left: 0;
bottom: 0;
width: 100%
}
.page-apps .card.app .action .el-button {
text-decoration: none;
border: 1px solid;
text-align: center;
padding: 8px 20px;
margin-right: 4px;
border-radius: 40px;
color: #9b9b9b;
-webkit-transition: all .25s;
transition: all .25s;
display: inline-block
}
.page-apps .card.app .action .el-button:hover {
color: #7e9bf8
}
.page-apps .card.app .action .el-button i {
font-size: 18px;
margin-right: 4px;
display: inline-block;
vertical-align: middle
}
.page-apps .card.app .action .btn-remove {
font-size: 0;
border: 1px solid;
background: 0;
padding: 10px;
border-radius: 50%;
vertical-align: top;
color: #9b9b9b;
}
.page-apps .card.app .action .btn-remove {
font-size: 16px
}
.page-apps .card.app .action .btn-remove:hover {
background-color: #ec4242;
color: #fff
}
.page-apps .card-android .type-mark {
height: 0;
width: 0;
border-top: 48px solid #a4c639;
border-left: 48px solid transparent
}
.page-apps .card-ios .type-mark {
height: 0;
width: 0;
border-top: 48px solid #c6c7c9;
/*border-top-right-radius: 10px;*/
border-left: 48px solid transparent;
}
.page-apps .card-ios {
border-radius: 10px;
}
.page-apps .card.card-invite .appname {
color: #4a4a4a;
margin-top: 0
}
.page-apps .card.card-invite .actions button {
display: block;
padding: 8px;
width: 120px;
border: 1px solid #4a4a4a;
border-radius: 20px;
background-color: transparent
}
.page-apps .card.card-invite .actions button:first-child {
position: absolute;
bottom: 44px;
margin: 0;
background-color: #4a4a4a;
color: #fff
}
.page-apps .card.card-invite .actions button:last-child {
position: absolute;
bottom: 44px;
left: 170px;
border-color: transparent
}
.page-apps .card.card-invite .actions button:last-child:hover {
color: #4a4a4a
}
</style>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,121 @@
<template>
<div>
<el-container class="navbar-wrapper">
<el-row :gutter="20">
<el-col :span="14" style="padding-top: 16px;margin-left: 60px">
<el-breadcrumb separator=">" style="height: 80px;font-size: 20px">
<el-breadcrumb-item :to="{ name:'FirIndex' }"><i class="el-icon-s-home elbi"></i>
</el-breadcrumb-item>
<el-breadcrumb-item :to="{ name:'FirApps'}"><i class="el-icon-apple elbi"></i>
</el-breadcrumb-item>
<el-breadcrumb-item v-if="$store.state.currentapp.name">{{ $store.state.currentapp.name}}
</el-breadcrumb-item>
</el-breadcrumb>
</el-col>
<el-col :span="4" :push="5">
<el-dropdown style="padding-top: 12px;" @command="handleCommand">
<el-button type="success" plain round>
{{$store.state.userinfo.first_name }}<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="userinfo">个人资料</el-dropdown-item>
<el-dropdown-item command="chpasswd">修改密码</el-dropdown-item>
<el-dropdown-item command="exit">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
</el-container>
</div>
</template>
<script>
export default {
name: "FirHeader",
data() {
return {
current_user: {},
appName: '',
}
}, methods: {
handleSelect() {
},
checkUrl() {
},
handleCommand(command) {
if (command === 'userinfo') {
this.$router.push({name: 'FirUserProfileInfo'})
} else if (command === 'chpasswd') {
this.$router.push({name: 'FirUserProfileChangePwd'})
}else if(command === 'exit'){
this.$cookies.remove("token");
this.$cookies.remove("auth_token");
this.$cookies.remove("username");
this.$cookies.remove("first_name");
this.$store.dispatch('doucurrentapp', {});
this.$store.dispatch('getUser', {});
this.$router.push({name: 'FirLogin'});
}
}
}, created() {
// this.$bus.$on('appName',val =>{
// this.appName = val;
// alert('header')
// })
this.appName = this.$route.params.id
}, watch: {
$route: function () {
this.appName = this.$route.params.id
}
}
}
</script>
<style scoped>
.el-container, .el-row {
margin: 10px auto;
width: 1166px;
}
.el-container {
margin: 10px auto 100px;
width: 1166px;
}
.navbar-wrapper {
background-color: #dbffeb;
font-size: 0;
border-radius: 10px;
}
.el-dropdown {
vertical-align: top;
}
.el-dropdown + .el-dropdown {
margin-left: 15px;
}
.el-icon-arrow-down {
font-size: 12px;
}
.elbi {
color: #67c23a;
}
</style>

@ -0,0 +1,39 @@
<template>
<div>
<div style="margin: 10px 0 10px 0 ;position:absolute;right:20px;top:auto;">
<!-- <el-button round icon="el-icon-star-off" @click="$router.push({name:'FirLogin'})"></el-button>-->
<el-button round icon="el-icon-apple" @click="$router.push({name:'FirApps'})"></el-button>
</div>
<div class="firplane">
<img src="../assets/plane.svg">
<div style="margin-top: -195px;margin-left: 107px">
<img src="../assets/propeller.svg">
</div>
</div>
</div>
</template>
<script>
export default {
name: "FirIndex",
data() {
return {}
},
created() {
}
}
</script>
<style scoped>
.firplane {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
</style>

@ -0,0 +1,174 @@
<template>
<el-container>
<div style="margin: 10px 0 10px 0 ;position:absolute;right:20px;top:auto;">
<el-button round icon="el-icon-arrow-left" @click="$router.go(-1)"></el-button>
<el-button round icon="el-icon-s-home" @click="$router.push({name:'FirIndex'})"></el-button>
</div>
<el-header>
<div>
<span>登录</span>
</div>
</el-header>
<el-main>
<el-form ref="form" :model="form">
<el-form-item>
<el-input v-model="form.email" prefix-icon="el-icon-user" placeholder="邮箱" autofocus></el-input>
</el-form-item>
<el-form-item>
<el-input v-model="form.password" prefix-icon="el-icon-lock" placeholder="密码"
show-password></el-input>
</el-form-item>
<el-form-item>
<el-button type="danger" @click="onSubmit">登录</el-button>
</el-form-item>
<!-- <el-form-item>-->
<!-- <el-button type="primary" @click="onRegister" plain>注册</el-button>-->
<!-- </el-form-item>-->
</el-form>
</el-main>
</el-container>
</template>
<script>
import {loginFun, set_auth_token} from "../restful";
export default {
name: "FirLogin",
data() {
return {
form: {
email: '',
password: '',
},
}
},
methods: {
onSubmit() {
let email = this.form.email;
let password = this.form.password;
if (this.isEmail(email) && password.length > 6) {
// alert(email,password);
loginFun(data => {
if (data.code == 1000) {
this.$message({
message: '登录成功',
type: 'success'
});
this.$cookies.remove("auth_token");
this.$cookies.set("token", data['token']);
this.$cookies.set("username", data.userinfo.username);
this.$cookies.set("first_name", data.userinfo.first_name);
this.$store.dispatch("get_user",data.userinfo);
set_auth_token();
this.$router.push({name: 'FirApps'})
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}, {
"username": email,
"password": password
});
} else {
this.$message({
message: '请输入正确的邮箱地址',
type: 'warning'
});
}
},
onRegister() {
this.$router.push({name: 'FirRegist'})
},
isEmail(input) {
if (input.match(/^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/)) {
return true;
}
return false;
}
},
mounted() {
}, created() {
}
}
</script>
<style scoped>
.el-container {
margin: 10px auto;
width: 1166px;
}
.el-header {
margin-top: 16%;
}
.el-form {
max-width: 360px;
margin: 0 auto;
}
.el-form-item .el-button {
max-width: 360px;
/*padding: 16px 20px;*/
width: 100%;
position: relative;
height: 50px;
}
.el-header {
text-align: center;
overflow: hidden;
margin-bottom: 50px
}
.el-header div span {
font-size: 24px;
display: inline-block;
vertical-align: middle;
padding: 8px 40px
}
.el-header div:before, .el-header div:after {
content: ' ';
display: inline-block;
vertical-align: middle;
width: 50%;
height: 1px;
background-color: #babfc3;
margin: 0 0 0 -50%
}
.el-header div {
text-align: center
}
.el-header div:after {
margin: 0 -50% 0 0
}
</style>

@ -0,0 +1,189 @@
<template>
<el-container>
<div style="margin: 10px 0 10px 0 ;position:absolute;right:20px;top:auto;">
<el-button round icon="el-icon-arrow-left" @click="$router.go(-1)"></el-button>
<el-button round icon="el-icon-s-home" @click="$router.push({name:'FirIndex'})"></el-button>
</div>
<el-header>
<div>
<span>注册</span>
</div>
</el-header>
<el-main>
<el-form ref="form" :model="form">
<el-form-item>
<el-input v-model="form.email" prefix-icon="el-icon-user" placeholder="邮箱" autofocus></el-input>
</el-form-item>
<el-form-item>
<el-input v-model="form.password" prefix-icon="el-icon-lock" placeholder="密码"
show-password></el-input>
</el-form-item>
<el-form-item style="height: 40px">
<el-row style="height: 40px">
<el-col :span="16">
<el-input placeholder="请输入验证码" v-model="form.authcode" maxlength="4"></el-input>
</el-col>
<el-col :span="8">
<el-image
style="margin:0px 4px;border-radius:4px;cursor:pointer;height: 40px"
:src="form.srccode"
fit="contain" @click="getsrcode">
</el-image>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-input v-model="form.phone" prefix-icon="el-icon-mobile" placeholder="手机"
maxlength="11"></el-input>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="16">
<el-input v-model="form.phonecode" prefix-icon="el-icon-mobile"
placeholder="验证码"></el-input>
</el-col>
<el-col :span="8">
<el-button type="info" @click="getphonecode" plain
style="margin:0px 4px;border-radius:4px;cursor:pointer;height: 40px">获取验证码
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-button type="danger" @click="onRegist">注册</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onLogin" plain>我是老用户,要登录</el-button>
</el-form-item>
</el-form>
</el-main>
</el-container>
</template>
<script>
export default {
name: "FirRegist",
data() {
return {
form: {
email: '',
password: '',
authcode: '',
srccode: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
phonecode: '',
phone: '',
},
}
},
methods: {
getsrcode() {
this.form.srccode = 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg';
},
getphonecode() {
},
onRegist() {
let email = this.form.email;
let password = this.form.password;
if (this.isEmail(email)) {
alert(email, password)
} else {
this.$message({
message: '请输入正确的邮箱地址',
type: 'warning'
});
}
},
onLogin() {
this.$router.push({name: 'FirLogin'})
},
isEmail(input) {
if (input.match(/^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/)) {
return true;
}
return false;
}
}
,
created() {
}
}
</script>
<style scoped>
.el-container {
margin: 10px auto;
width: 1166px;
}
.el-header {
margin-top: 16%;
}
.el-form {
max-width: 360px;
margin: 0 auto;
}
.el-form-item .el-button {
max-width: 360px;
width: 100%;
position: relative;
height: 50px;
}
.el-header {
text-align: center;
overflow: hidden;
margin-bottom: 50px
}
.el-header div span {
font-size: 24px;
display: inline-block;
vertical-align: middle;
padding: 8px 40px
}
.el-header div:before, .el-header div:after {
content: ' ';
display: inline-block;
vertical-align: middle;
width: 50%;
height: 1px;
background-color: #babfc3;
margin: 0 0 0 -50%
}
.el-header div {
text-align: center
}
.el-header div:after {
margin: 0 -50% 0 0
}
</style>

@ -0,0 +1,213 @@
<template>
<el-main>
<div class="user-info">
<el-upload
class="avatar-uploader"
:action="getuppicurl"
:show-file-list="false"
accept=".png , .jpg , .jpeg"
:headers="uploadconf.AuthHeaders"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
<img v-if="imageUrl" :src="imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<div class="name">
<input v-model="userinfo.first_name" @focusout="update_name">
</div>
<div class="user_pro_tabs">
<el-row :gutter="12" class="row">
<el-col :span="6" :offset="6">
<div class="col-4">
<a @click="$router.push({name:'FirUserProfileInfo'})" ref="userinfo" class="">
<span>
<i class="el-icon-user"></i>
</span>
个人资料
</a>
</div>
</el-col>
<el-col :span="6">
<div class="col-4">
<a ref="changepwd" class="" @click="$router.push({name:'FirUserProfileChangePwd'})">
<span><i class="el-icon-lock"></i></span>
修改密码
</a>
</div>
</el-col>
</el-row>
</div>
<div style="margin-top: 50px">
<router-view></router-view>
</div>
</div>
</el-main>
</template>
<script>
import { userinfos,getuserpicurl} from '../restful';
export default {
name: "FirUserProfile",
data() {
return {
imageUrl: '',
userinfo:{},
uploadconf:{},
}
},
methods: {
updateUserInfo(datainfo){
userinfos(data=>{
if(data.code === 1000){
this.userinfo = data.data;
this.$store.dispatch("getUser",data.data);
this.$store.dispatch('doucurrentapp', {});
this.imageUrl = data.data.head_img;
if(datainfo.data){
this.$message.success("更新成功")
}
}else {
this.$message.error("更新失败")
}
},datainfo)
},
update_name(){
this.updateUserInfo({"methods":'PUT','data':{"first_name":this.userinfo.first_name}});
},
handleAvatarSuccess(res, file) {
this.imageUrl = URL.createObjectURL(file.raw);
this.$message({
message: '应用图标上传成功',
type: 'success'
});
// this.updateUserInfo({"methods":false});
},
beforeAvatarUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 2;
if(file.type === 'image/jpeg' || file.type === 'image/png'|| file.type === 'image/jpg'){
if (isLt2M) {
return true;
}
else{
this.$message.error('上传头像图片大小不能超过 2MB!');
}
}else {
this.$message.error('上传头像图片只能是 JPG/PNG/JPEG 格式!');
}
return false;
},
autoSetInfoIndex() {
if (this.$store.state.userInfoIndex === 0) {
this.$refs.userinfo.classList.add('active');
this.$refs.changepwd.classList.remove('active');
} else if (this.$store.state.userInfoIndex === 1) {
this.$refs.changepwd.classList.add('active');
this.$refs.userinfo.classList.remove('active');
}
}
}, mounted() {
this.autoSetInfoIndex();
this.updateUserInfo({"methods":false});
this.uploadconf = {
"AuthHeaders": {"Authorization": this.$cookies.get("auth_token")}
};
}, watch: {
'$store.state.userInfoIndex': function () {
this.autoSetInfoIndex();
},
},filters:{
},computed:{
getuppicurl(){
return getuserpicurl()
}
}
}
</script>
<style scoped>
.el-main {
margin: 10px auto 100px;
width: 1166px;
position: relative;
padding-bottom: 1px;
background-color: #bfe7f9;
color: #9b9b9b;
-webkit-font-smoothing: antialiased;
border-radius: 1%;
}
.user-info {
position: relative;
text-align: center;
margin-bottom: 60px;
margin-top: 46px;
}
.avatar {
height: 100px;
width: 100px;
border-radius: 50%;
display: block;
}
.name {
text-align: center;
padding-bottom: 20px;
}
.name input {
text-align: center;
color: #889eff;
margin: 36px auto 0;
width: 280px;
padding: 0;
border: none;
background-color: transparent;
font-size: 30px;
}
.user_pro_tabs a {
width: 100%;
font-size: 16px;
text-align: center;
display: inline-block;
line-height: 48px;
height: 48px;
border-bottom: 1px solid #BABFC3;
text-decoration: none;
color: #BABFC3;
}
.user_pro_tabs a > span {
margin-right: 16px;
vertical-align: middle
}
.user_pro_tabs a.active {
color: #e2644c;
border-bottom-color: #e2644c
}
</style>

@ -0,0 +1,96 @@
<template>
<div>
<el-form ref="form" :model="form">
<el-form-item>
<el-row>
<el-col :span="22">
<el-input v-model="form.oldpassword" show-password autofocus="true" prefix-icon="el-icon-unlock"
placeholder="当前密码"></el-input>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="22">
<el-input v-model="form.newpassword" show-password prefix-icon="el-icon-lock"
placeholder="新密码"></el-input>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="22">
<el-input v-model="form.surepassword" show-password prefix-icon="el-icon-lock"
placeholder="确认密码"></el-input>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="22">
<el-button type="danger" @click="updatepasswd">更新密码</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {userinfos} from "../restful";
export default {
name: "FirUserProfileChangePwd",
data() {
return {
form: {
oldpassword: '',
newpassword: '',
surepassword: ''
},
}
}, methods: {
updatepasswd() {
if (this.form.newpassword === this.form.surepassword) {
userinfos(data=>{
if(data.code === 1000){
this.userinfo = data.data;
this.$message.success('密码修改成功');
}else {
this.$message.error('密码修改失败,'+data.msg);
}
},{"methods":'PUT','data':{
"oldpassword": this.form.oldpassword,
"surepassword": this.form.surepassword
}});
} else {
this.$message.error('密码不一致');
}
}
}, mounted() {
this.$store.dispatch('douserInfoIndex', 1);
}
}
</script>
<style scoped>
.el-form {
max-width: 360px;
margin: 0 auto;
}
.el-form-item .el-button {
max-width: 360px;
width: 100%;
height: 50px;
}
</style>

@ -0,0 +1,208 @@
<template>
<div>
<el-form ref="form" :model="userinfo">
<el-form-item>
<el-row>
<el-col :span="22">
<el-input v-model="userinfo.username" prefix-icon="el-icon-user" placeholder="用户名" disabled></el-input>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="18">
<el-input v-model="userinfo.qq" :readonly="editqq !== true" ref="qq" prefix-icon="el-icon-message"
placeholder="QQ"></el-input>
</el-col>
<el-col :span="2">
<el-button icon="el-icon-edit" @click="changeQQValue">
</el-button>
</el-col>
<el-col :span="4" v-if="editqq === true">
<el-button type="success" @click="saveQQ" plain
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 36px">保存
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="18">
<el-input v-model="userinfo.job" ref="position" :readonly="editposition !== true"
prefix-icon="el-icon-postcard" placeholder="职位"></el-input>
</el-col>
<el-col :span="2">
<el-button icon="el-icon-edit" @click="changePositionValue">
</el-button>
</el-col>
<el-col :span="4" v-if="editposition === true">
<el-button type="success" @click="savePositionValue" plain
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 36px">保存
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="18">
<el-input v-model="userinfo.mobile" ref="phone" :readonly="editphone !== true"
prefix-icon="el-icon-mobile" placeholder="手机" maxlength="11"></el-input>
</el-col>
<el-col :span="2">
<el-button icon="el-icon-edit" @click="changePhoneValue">
</el-button>
</el-col>
<el-col :span="4" v-if="editphone === true">
<el-button type="success" @click="savePhone" plain
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 36px">保存
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="editphone === true">
<el-row :gutter="10">
<el-col :span="12">
<el-input v-model="userinfo.sms_code" prefix-icon="el-icon-mobile" placeholder="验证码"
maxlength="6"></el-input>
</el-col>
<el-col :span="6">
<el-button type="info" @click="getphonecode" plain
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px">获取验证码
</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {userinfos} from '../restful'
export default {
name: "FirUserProfileInfo",
data() {
return {
userinfo: {
srccode: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
},
editphone: false,
editqq: false,
editposition: false
}
}, methods: {
saveQQ(){
this.updateUserInfo({"methods":'PUT','data':this.userinfo});
this.changeQQValue()
},
savePositionValue(){
this.updateUserInfo({"methods":'PUT','data':this.userinfo});
this.changePositionValue()
},
savePhone(){
this.updateUserInfo({"methods":'PUT','data':this.userinfo});
this.changePhoneValue()
},
updateUserInfo(datainfo){
userinfos(data=>{
if(data.code === 1000){
this.userinfo = data.data;
this.$store.dispatch("getUser",data.data);
if(data.data.sms_code){
this.$notify({
title: '验证码',
message: '您正在修改手机号码,验证码为:'+data.data.sms_code,
type: 'success'
});
}
if(datainfo.data){
this.$message.success("更新成功")
}
}else {
this.$message.error("更新失败")
}
},datainfo)
},
changePhoneValue() {
this.editphone = !this.editphone;
if (this.$refs.phone.$el.children[0].style.backgroundColor) {
this.$refs.phone.$el.children[0].style.backgroundColor = ''
} else {
this.$refs.phone.$el.children[0].style.backgroundColor = '#f6ffdc';
}
},
changePositionValue() {
this.editposition = !this.editposition;
if (this.$refs.position.$el.children[0].style.backgroundColor) {
this.$refs.position.$el.children[0].style.backgroundColor = ''
} else {
this.$refs.position.$el.children[0].style.backgroundColor = '#f6ffdc';
}
},
changeQQValue() {
this.editqq = !this.editqq;
if (this.$refs.qq.$el.children[0].style.backgroundColor) {
this.$refs.qq.$el.children[0].style.backgroundColor = ''
} else {
this.$refs.qq.$el.children[0].style.backgroundColor = '#f6ffdc';
}
},
getphonecode() {
this.updateUserInfo({"methods":false,'data':{'act':'sms'}});
}
}, mounted() {
this.$store.dispatch('douserInfoIndex', 0);
// this.updateUserInfo({"methods":false});
this.userinfo = this.$store.state.userinfo;
},watch:{
'$store.state.userinfo':function () {
this.userinfo = this.$store.state.userinfo;
}
}
}
</script>
<style scoped>
.el-form {
max-width: 360px;
margin: 0 auto;
}
.el-form-item .el-button {
max-width: 360px;
width: 100%;
position: relative;
height: 50px;
}
.el-button {
padding: 0;
border: none;
background-color: transparent;
color: #e2644c;
}
.el-button:hover {
background-color: #bfe7f9;
}
.el-button:focus {
background-color: #bfe7f9;
}
</style>

@ -0,0 +1,49 @@
import Vue from 'vue'
import App from "@/App";
import router from "@/router";
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import Vuex from 'vuex'
//使用vue-cookies
import VueCookies from 'vue-cookies'
Vue.use(VueCookies);
//导入全局的geetest.js
import './assets/gt'
//导入store实例
import store from "./store";
//全局导航守卫
router.beforeEach((to, from, next) => {
// ...
if (VueCookies.isKey('access_token')) {
let user = {
username: VueCookies.get('username'),
shop_cart_num: VueCookies.get('shop_cart_num'),
access_token: VueCookies.get('access_token'),
avatar: VueCookies.get('avatar'),
notice_num: VueCookies.get('notice_num')
};
store.dispatch('getUser', user)
}
next()
});
Vue.config.productionTip = false;
Vue.use(ElementUI);
Vue.use(Vuex);
new Vue({
render: h => h(App),
router,
store,
}).$mount('#app');

@ -0,0 +1,582 @@
import Axios from 'axios'
import VueCookies from 'vue-cookies'
import router from "../router";
const https = require('https');
const Base64 = require('js-base64').Base64;
Axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
Axios.defaults.withCredentials = true;
Axios.defaults.httpsAgent = new https.Agent({
keepAlive: true
});
// Axios.defaults.baseURL='';
const USERSEVER = 'https://fir.dvcloud.xin/api/v1/fir/server';
// const USERSEVER = 'http://192.168.1.112:8000/api/v1/fir/server';
export function set_auth_token() {
Axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
if (VueCookies.get('token')) {
// Axios.defaults.headers.common['Authorization'] = localStorage.getItem('access_token');
// console.log(config.headers);
if (VueCookies.get('auth_token')) {
config.headers.Authorization = VueCookies.get('auth_token')
} else {
let token = VueCookies.get('token');
let username = VueCookies.get('username');
VueCookies.set("auth_token", Base64.encode(token + ':' + username));
}
}
// 更改加载的样式
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
}
set_auth_token();
function getData(PostType = true, url, params = {}, callBack, load, isCode = false) {
let methods = PostType;
if (methods === "DELETE") {
Axios
.delete(url, params)
.then(function (response) {
if (isCode) {
callBack(response.data);
} else {
callBack(response.data.data);
}
})
.catch(function (error) {
// eslint-disable-next-line no-console
console.log(error, error.response);
if (error && error.response) {
switch (error.response.status) {
case 400:
error.message = '请求错误(400)';
break;
case 401:
error.message = '未授权,请重新登录(401)';
break;
case 403:
error.message = '拒绝访问(403)';
break;
case 404:
error.message = '请求出错(404)';
break;
case 408:
error.message = '请求超时(408)';
break;
case 500:
error.message = '服务器错误(500)';
break;
case 501:
error.message = '服务未实现(501)';
break;
case 502:
error.message = '网络错误(502)';
break;
case 503:
error.message = '服务不可用(503)';
break;
case 504:
error.message = '网络超时(504)';
break;
case 505:
error.message = 'HTTP版本不受支持(505)';
break;
default:
error.message = `连接出错(${error.response.status})!`;
}
} else {
error.message = '连接服务器失败!';
}
if (error.message === 'Network Error') {
alert('网络连接失败');
} else {
alert(error)
}
callBack(null);
});
}
else if (methods === "PUT") {
Axios
.put(url, params)
.then(function (response) {
if (isCode) {
callBack(response.data);
} else {
callBack(response.data.data);
}
})
.catch(function (error) {
// eslint-disable-next-line no-console
console.log(error, error.response);
if (error && error.response) {
switch (error.response.status) {
case 400:
error.message = '请求错误(400)';
break;
case 401:
error.message = '未授权,请重新登录(401)';
break;
case 403:
error.message = '拒绝访问(403)';
break;
case 404:
error.message = '请求出错(404)';
break;
case 408:
error.message = '请求超时(408)';
break;
case 500:
error.message = '服务器错误(500)';
break;
case 501:
error.message = '服务未实现(501)';
break;
case 502:
error.message = '网络错误(502)';
break;
case 503:
error.message = '服务不可用(503)';
break;
case 504:
error.message = '网络超时(504)';
break;
case 505:
error.message = 'HTTP版本不受支持(505)';
break;
default:
error.message = `连接出错(${error.response.status})!`;
}
} else {
error.message = '连接服务器失败!';
}
if (error.message === 'Network Error') {
alert('网络连接失败');
} else {
alert(error)
}
callBack(null);
});
}
else if (PostType===true) {
Axios
.post(url, params)
.then(function (response) {
let type = response.data.type;
if (isCode) {
callBack(response.data);
} else {
callBack(response.data.data);
}
let x = '';
switch (type) {
case 8001:
x = "服务器内部错误";
break;
case 8002:
x = "错误密码";
break;
case 8004:
x = "验证码错误";
break;
case 8005:
x = "没有这个玩家信息";
break;
case 8006:
x = "请重新登录账号";
break;
case 8009:
x = "没有权限";
break;
case 8011:
x = "账号被冻结";
break;
case 8013:
x = "用户名已存在";
break;
case 9001:
x = "系统繁忙";
break;
case 9007:
x = "错误的参数";
break;
case 20009:
x = "上传文件失败";
break;
}
if (x !== '') {
alert(x)
}
})
.catch(function (error) {
// eslint-disable-next-line no-console
console.log(error, error.response);
if (error && error.response) {
switch (error.response.status) {
case 400:
error.message = '请求错误(400)';
break;
case 401:
error.message = '未授权,请重新登录(401)';
break;
case 403:
error.message = '拒绝访问(403)';
break;
case 404:
error.message = '请求出错(404)';
break;
case 408:
error.message = '请求超时(408)';
break;
case 500:
error.message = '服务器错误(500)';
break;
case 501:
error.message = '服务未实现(501)';
break;
case 502:
error.message = '网络错误(502)';
break;
case 503:
error.message = '服务不可用(503)';
break;
case 504:
error.message = '网络超时(504)';
break;
case 505:
error.message = 'HTTP版本不受支持(505)';
break;
default:
error.message = `连接出错(${error.response.status})!`;
}
} else {
error.message = '连接服务器失败!';
}
if (error.message === 'Network Error') {
alert('网络连接失败');
} else {
alert(error)
}
callBack(null);
});
} else {
// console.log(JSON.stringify(params) + "参数")
var uri = '';
var keys = Object.keys(params);
var values = Object.values(params);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = values[i];
uri = uri + key + "=" + value;
if (i < keys.length - 1) {
uri = uri + "&"
}
}
if (uri !== "") {
uri = "?" + uri
}
url = url + uri;
Axios
.get(url, params)
.then(function (response) {
let type = response.data.type;
callBack(response.data);
let x = '';
switch (type) {
case 8001:
x = "服务器内部错误";
break;
case 8002:
x = "错误密码";
break;
case 8004:
x = "验证码错误";
break;
case 8005:
x = "没有这个玩家信息";
break;
case 8006:
x = "请重新登录账号";
break;
case 8009:
x = "没有权限";
break;
case 8011:
x = "账号被冻结";
break;
case 8013:
x = "用户名已存在";
break;
case 9001:
x = "系统繁忙";
break;
case 9007:
x = "错误的参数";
break;
case 20009:
x = "上传文件失败";
break;
}
if (x !== '') {
alert(x)
}
})
.catch(function (error) {
if (error && error.response) {
switch (error.response.status) {
case 400:
error.message = '请求错误(400)';
break;
case 401:
error.message = '未授权,请重新登录(401)';
break;
case 403:
error.message = '拒绝访问(403)';
break;
case 404:
error.message = '请求出错(404)';
break;
case 408:
error.message = '请求超时(408)';
break;
case 500:
error.message = '服务器错误(500)';
break;
case 501:
error.message = '服务未实现(501)';
break;
case 502:
error.message = '网络错误(502)';
break;
case 503:
error.message = '服务不可用(503)';
break;
case 504:
error.message = '网络超时(504)';
break;
case 505:
error.message = 'HTTP版本不受支持(505)';
break;
default:
error.message = `连接出错(${error.response.status})!`;
}
} else {
error.message = '连接服务器失败!';
}
if (error.message === 'Network Error') {
alert('网络连接失败')
} else {
if (error.response.status === 403) {
router.push({name: 'FirLogin'})
} else {
alert(error.message);
}
}
callBack(null);
});
}
}
/**用户登录 */
export function loginFun(callBack, params, load = true) {
getData(
true,
USERSEVER + '/login',
params,
data => {
callBack(data);
},
load,
true,
true
);
}
/**用户退出 */
export function logout(callBack, params, load = true) {
getData(
false,
USERSEVER + '/logout',
params,
data => {
callBack(data);
},
load,
true,
true
);
}
/**用户应用列表 */
export function getapps(callBack, params, load = true) {
getData(
false,
USERSEVER + '/apps',
params,
data => {
callBack(data);
},
load,
true,
true
);
}
/**用户应用详情 */
export function getappinfos(callBack, params, load = true) {
getData(
false,
USERSEVER + '/apps/' + params.app_id,
{},
data => {
callBack(data);
},
load,
true,
true
);
}
/**删除应用 */
export function deleteapp(callBack, params, load = true) {
getData(
"DELETE",
USERSEVER + '/apps/' + params.app_id,
{},
data => {
callBack(data);
},
load,
true,
true
);
}
/**删除release应用 */
export function deletereleaseapp(callBack, params, load = true) {
getData(
"DELETE",
USERSEVER + '/appinfos/' + params.app_id + '/' + params.release_id,
{},
data => {
callBack(data);
},
load,
true,
true
);
}
/**更新app应用 */
export function updateapp(callBack, params, load = true) {
getData(
"PUT",
USERSEVER + '/apps/' + params.app_id,
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}
/**更新release应用 */
export function updatereleaseapp(callBack, params, load = true) {
getData(
"PUT",
USERSEVER + '/appinfos/' + params.app_id + '/' + params.release_id,
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}
/**用户应用详情 */
export function getapptimeline(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/appinfos/' + params.app_id + '/' + params.action,
{},
data => {
callBack(data);
},
load,
true,
true
);
}
/**获取下载token */
export function getDownloadToken(callBack, params, load = true) {
getData(
false,
USERSEVER + '/download_token/' + params.short ,
params,
data => {
callBack(data);
},
load,
true,
true
);
}
/**用户个人信息 */
export function userinfos(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/userinfo',
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}
export function getuserpicurl() {
return USERSEVER+ '/userinfo'
}
export function getapppicurl(app_id) {
return USERSEVER + '/apps/' + app_id
}

@ -0,0 +1,117 @@
import Vue from 'vue'
//1.先导入
import VueRouter from 'vue-router'
//2.一定先use 一下
Vue.use(VueRouter);
// 解决push 同一个路由的错误
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
};
import FirIndex from "@/components/FirIndex";
import FirApps from "@/components/FirApps";
import FirLogin from "@/components/FirLogin";
import FirRegist from "@/components/FirRegist";
import FirDownload from "@/components/FirDownload";
import FirAppBase from "@/components/FirAppBase";
import FirAppInfosBase from "@/components/FirAppInfosBase";
import FirAppInfostimeline from "@/components/FirAppInfostimeline";
import FirAppInfosbaseinfo from "@/components/FirAppInfosbaseinfo";
import FirAppInfoscombo from "@/components/FirAppInfoscombo";
import FirUserProfile from "@/components/FirUserProfileBase";
import FirUserProfileInfo from "@/components/FirUserProfileInfo";
import FirUserProfileChangePwd from "@/components/FirUserProfileChangePwd";
const router = new VueRouter({
mode:'history',
routes: [
{
path: '/',
name: 'Index',
redirect: {name: 'FirIndex'}
},
{
path: '/index',
name: 'FirIndex',
component: FirIndex
},
{
path: '/apps',
component: FirAppBase,
children: [
{
path: '',
name: 'FirApps',
component: FirApps,
}
,
{
path: ':id',
component: FirAppInfosBase,
children: [
{
path: '',
name: 'FirAppInfostimeline',
component: FirAppInfostimeline,
}
,
{
path: 'info',
name: 'FirAppInfosbaseinfo',
component: FirAppInfosbaseinfo,
},
{
path: 'combo',
name: 'FirAppInfoscombo',
component: FirAppInfoscombo,
}
]
}
]
},
{
path: '/user',
component: FirAppBase,
children: [
{
path: '',
component: FirUserProfile,
children: [
{
path: 'info',
name: 'FirUserProfileInfo',
component: FirUserProfileInfo,
}, {
path: 'setpasswd',
name: 'FirUserProfileChangePwd',
component: FirUserProfileChangePwd
}
]
}
]
},
{
path: '/login',
name: 'FirLogin',
component: FirLogin
},
{
path: '/register',
name: 'FirRegist',
component: FirRegist
},
{
path: '/:short',
name: 'FirDownload',
component: FirDownload
},
]
});
export default router;

@ -0,0 +1,50 @@
import Vue from 'vue'
/*
1导入对象
2.Vue.use()
3.创建store对象
4.挂载
*/
import Vuex from 'vuex'
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
userinfo: {},
currentapp: {},
appInfoIndex: [],
userInfoIndex: 0,
},
mutations: {
get_user(state, data) {
state.userinfo = data;
},
setappInfoIndex(state, val) {
state.appInfoIndex = val
},
setuserInfoIndex(state, val) {
state.userInfoIndex = val
},
setcurrentapp(state, val) {
state.currentapp = val
}
},
actions: {
getUser(context, data) {
context.commit('get_user', data);
},
doappInfoIndex(context, val) {
context.commit('setappInfoIndex', val)
},
douserInfoIndex(context, val) {
context.commit('setuserInfoIndex', val)
},
doucurrentapp(context, val) {
context.commit('setcurrentapp', val)
}
}
});
export default store;

@ -0,0 +1,40 @@
//滚动条在Y轴上的滚动距离
export function getScrollTop(){
var scrollTop = 0, bodyScrollTop = 0, documentScrollTop = 0;
if(document.body){
bodyScrollTop = document.body.scrollTop;
}
if(document.documentElement){
documentScrollTop = document.documentElement.scrollTop;
}
scrollTop = (bodyScrollTop - documentScrollTop > 0) ? bodyScrollTop : documentScrollTop;
return Math.ceil(scrollTop);
}
//文档的总高度
export function getScrollHeight(){
var scrollHeight = 0, bodyScrollHeight = 0, documentScrollHeight = 0;
if(document.body){
bodyScrollHeight = document.body.scrollHeight;
}
if(document.documentElement){
documentScrollHeight = document.documentElement.scrollHeight;
}
scrollHeight = (bodyScrollHeight - documentScrollHeight > 0) ? bodyScrollHeight : documentScrollHeight;
return scrollHeight;
}
//浏览器视口的高度
export function getWindowHeight(){
var windowHeight = 0;
if(document.compatMode === "CSS1Compat"){
windowHeight = document.documentElement.clientHeight;
}else{
windowHeight = document.body.clientHeight;
}
return windowHeight;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,12 @@
from django.contrib import admin
# Register your models here.
from .models import *
admin.site.register(UserInfo)
admin.site.register(Apps)
admin.site.register(AppReleaseInfo)
admin.site.register(Token)
admin.site.register(VerifyName)

@ -0,0 +1,5 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
name = 'api'

@ -0,0 +1,119 @@
# Generated by Django 3.0.3 on 2020-03-03 11:27
from django.conf import settings
import django.contrib.auth.models
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0011_update_proxy_permissions'),
]
operations = [
migrations.CreateModel(
name='UserInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('username', models.CharField(max_length=64, unique=True, verbose_name='用户名')),
('email', models.EmailField(blank=True, max_length=255, null=True, unique=True, verbose_name='email address')),
('uid', models.CharField(max_length=64, unique=True)),
('mobile', models.BigIntegerField(help_text='用于手机验证码登录', null=True, unique=True, verbose_name='手机')),
('qq', models.CharField(blank=True, db_index=True, max_length=64, null=True, verbose_name='QQ')),
('is_active', models.BooleanField(default=True, verbose_name='账户状态')),
('job', models.TextField(blank=True, max_length=128, null=True, verbose_name='职位')),
('company', models.CharField(blank=True, max_length=128, null=True, verbose_name='公司')),
('gender', models.SmallIntegerField(choices=[(0, '保密'), (1, ''), (2, '')], default=0, verbose_name='性别')),
('password', models.CharField(help_text="<a class='btn-link' href='password'>重置密码</a>", max_length=128, verbose_name='password')),
('head_img', models.CharField(default='/static/frontend/head_portrait/logo@2x.png', max_length=256, verbose_name='个人头像')),
('role', models.SmallIntegerField(choices=[(0, '普通会员'), (1, 'VIP'), (2, 'SVIP'), (3, '管理员')], default=0, verbose_name='角色')),
('memo', models.TextField(blank=True, default=None, null=True, verbose_name='备注')),
('date_joined', models.DateTimeField(auto_now_add=True, verbose_name='注册时间')),
('download_times', models.IntegerField(default=100, verbose_name='下载次数')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={
'verbose_name': '账户信息',
'verbose_name_plural': '账户信息',
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
name='VerifyName',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='', max_length=32, verbose_name='真实姓名')),
('id_card', models.CharField(blank=True, max_length=32, null=True, verbose_name='身份证号或护照号')),
('address', models.CharField(blank=True, max_length=128, null=True, verbose_name='联系地址')),
('mobile', models.BigIntegerField(blank=True, null=True, verbose_name='联系电话')),
('date_verify', models.DateTimeField(auto_now_add=True, verbose_name='认证时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='verify_info', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
options={
'verbose_name': '实名认证',
'verbose_name_plural': '实名认证',
},
),
migrations.CreateModel(
name='Token',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('access_token', models.CharField(max_length=42, unique=True)),
('created', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='auth_token', to=settings.AUTH_USER_MODEL, verbose_name='关联用户')),
],
),
migrations.CreateModel(
name='Apps',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('app_id', models.CharField(max_length=64, unique=True)),
('type', models.SmallIntegerField(choices=[(0, 'ios'), (1, 'android')], default=0, verbose_name='类型')),
('name', models.CharField(blank=True, max_length=32, null=True, verbose_name='应用名称')),
('short', models.CharField(max_length=16, unique=True, verbose_name='短链接')),
('bundle_id', models.CharField(blank=True, max_length=64, verbose_name='bundle id')),
('icon_url', models.CharField(blank=True, max_length=128, verbose_name='图标url')),
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('count_hits', models.BigIntegerField(verbose_name='下载次数')),
('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('has_combo', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='combo_app_info', to='api.Apps', verbose_name='关联应用')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户ID')),
],
options={
'verbose_name': '应用信息',
'verbose_name_plural': '应用信息',
},
),
migrations.CreateModel(
name='AppReleaseInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_master', models.BooleanField(default=True, verbose_name='是否事master版本')),
('release_id', models.CharField(max_length=64, unique=True, verbose_name='release 版本id')),
('build_version', models.CharField(blank=True, max_length=16, verbose_name='build版本')),
('app_version', models.CharField(blank=True, max_length=16, verbose_name='app版本')),
('release_type', models.SmallIntegerField(choices=[(0, 'android'), (1, 'adhoc'), (2, '')], default=0, verbose_name='版本类型')),
('minimum_os_version', models.CharField(max_length=64, verbose_name='应用可安装的最低系统版本')),
('binary_size', models.BigIntegerField(verbose_name='应用大小')),
('binary_url', models.CharField(blank=True, max_length=128, verbose_name='APPurl')),
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('app_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='关联APP')),
],
options={
'verbose_name': '应用详情',
'verbose_name_plural': '应用详情',
},
),
]

@ -0,0 +1,19 @@
# Generated by Django 3.0.3 on 2020-03-03 11:40
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('api', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='apps',
name='has_combo',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='combo_app_info', to='api.Apps', verbose_name='关联应用'),
),
]

@ -0,0 +1,27 @@
# Generated by Django 3.0.3 on 2020-03-06 09:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('api', '0002_auto_20200303_1940'),
]
operations = [
migrations.AlterField(
model_name='appreleaseinfo',
name='app_id',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP'),
),
migrations.CreateModel(
name='AppStorage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('app_file', models.FileField(upload_to='')),
('name', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='api.AppReleaseInfo')),
],
),
]

@ -0,0 +1,27 @@
# Generated by Django 3.0.3 on 2020-03-06 15:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0003_auto_20200306_1734'),
]
operations = [
migrations.RemoveField(
model_name='apps',
name='icon_url',
),
migrations.AddField(
model_name='appreleaseinfo',
name='icon_url',
field=models.CharField(blank=True, max_length=128, verbose_name='图标url'),
),
migrations.AlterField(
model_name='appreleaseinfo',
name='release_type',
field=models.SmallIntegerField(choices=[(0, 'android'), (1, 'adhoc'), (2, 'Inhouse'), (3, 'unknown')], default=0, verbose_name='版本类型'),
),
]

@ -0,0 +1,22 @@
# Generated by Django 3.0.3 on 2020-03-07 00:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0004_auto_20200306_2331'),
]
operations = [
migrations.RemoveField(
model_name='apps',
name='name',
),
migrations.AddField(
model_name='appreleaseinfo',
name='name',
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='应用名称'),
),
]

@ -0,0 +1,22 @@
# Generated by Django 3.0.3 on 2020-03-07 00:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0005_auto_20200307_0800'),
]
operations = [
migrations.RemoveField(
model_name='appreleaseinfo',
name='name',
),
migrations.AddField(
model_name='apps',
name='name',
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='应用名称'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-03-07 01:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0006_auto_20200307_0810'),
]
operations = [
migrations.AddField(
model_name='appreleaseinfo',
name='changelog',
field=models.TextField(blank=True, default=None, null=True, verbose_name='更新日志'),
),
]

@ -0,0 +1,23 @@
# Generated by Django 3.0.3 on 2020-03-07 04:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0007_appreleaseinfo_changelog'),
]
operations = [
migrations.AddField(
model_name='apps',
name='description',
field=models.TextField(blank=True, default=None, null=True, verbose_name='描述'),
),
migrations.AlterField(
model_name='apps',
name='type',
field=models.SmallIntegerField(choices=[(0, 'android'), (1, 'ios')], default=0, verbose_name='类型'),
),
]

@ -0,0 +1,26 @@
# Generated by Django 3.0.3 on 2020-03-09 06:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0008_auto_20200307_1249'),
]
operations = [
migrations.AddField(
model_name='userinfo',
name='domain_name',
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='域名'),
),
migrations.AlterField(
model_name='apps',
name='count_hits',
field=models.BigIntegerField(default=0, verbose_name='下载次数'),
),
migrations.DeleteModel(
name='AppStorage',
),
]

@ -0,0 +1,27 @@
# Generated by Django 3.0.3 on 2020-03-09 11:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0009_auto_20200309_1424'),
]
operations = [
migrations.RemoveField(
model_name='userinfo',
name='head_img',
),
migrations.AlterField(
model_name='userinfo',
name='password',
field=models.CharField(max_length=128, verbose_name='password'),
),
migrations.AlterField(
model_name='userinfo',
name='qq',
field=models.BigIntegerField(blank=True, db_index=True, max_length=64, null=True, verbose_name='QQ'),
),
]

@ -0,0 +1,23 @@
# Generated by Django 3.0.3 on 2020-03-10 02:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0010_auto_20200309_1918'),
]
operations = [
migrations.AddField(
model_name='userinfo',
name='all_download_times',
field=models.IntegerField(default=0, verbose_name='总共下载次数'),
),
migrations.AlterField(
model_name='userinfo',
name='qq',
field=models.BigIntegerField(blank=True, db_index=True, null=True, verbose_name='QQ'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-03-14 07:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0011_auto_20200310_1019'),
]
operations = [
migrations.AddField(
model_name='userinfo',
name='head_img',
field=models.CharField(default='/static/frontend/head_portrait/logo@2x.png', max_length=256, verbose_name='个人头像'),
),
]

@ -0,0 +1,147 @@
from django.db import models
# Create your models here.
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.utils.safestring import mark_safe
from api.utils.randomstrings import make_random_uuid
from django.contrib.auth.models import AbstractUser
######################################## 用户表 ########################################
class UserInfo(AbstractUser):
username = models.CharField("用户名", max_length=64, unique=True)
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
blank=True,
null=True
)
uid = models.CharField(max_length=64, unique=True) # user_id,唯一标识
mobile = models.BigIntegerField(verbose_name="手机", unique=True, help_text="用于手机验证码登录", null=True)
qq = models.BigIntegerField(verbose_name="QQ", blank=True, null=True, db_index=True)
is_active = models.BooleanField(default=True, verbose_name="账户状态")
job = models.TextField("职位", max_length=128, blank=True, null=True)
company = models.CharField("公司", max_length=128, blank=True, null=True)
gender_choices = ((0, '保密'), (1, ''), (2, ''))
gender = models.SmallIntegerField(choices=gender_choices, default=0, verbose_name="性别")
# password = models.CharField('password', max_length=128,
# help_text=mark_safe('''<a class='btn-link' href='set_password'>重置密码</a>'''))
head_img = models.CharField(max_length=256, default='/files/imgs/head_img.jpeg',
verbose_name="个人头像")
role_choices = ((0, '普通会员'), (1, 'VIP'), (2, 'SVIP'), (3, '管理员'))
role = models.SmallIntegerField(choices=role_choices, default=0, verbose_name="角色")
memo = models.TextField('备注', blank=True, null=True, default=None, )
date_joined = models.DateTimeField(auto_now_add=True, verbose_name="注册时间")
download_times = models.IntegerField(default=100, verbose_name="下载次数")
all_download_times = models.IntegerField(default=0, verbose_name="总共下载次数")
domain_name = models.CharField(verbose_name="域名",blank=True,null=True,max_length=64)
# is_staff = models.BooleanField(verbose_name='staff status', default=False, help_text='决定着用户是否可登录管理后台')
# weixin = models.CharField(max_length=128, blank=True, null=True, db_index=True, verbose_name="微信")
# signature = models.CharField('个人签名', blank=True, null=True, max_length=255)
# openid = models.CharField(max_length=128, blank=True, null=True)
# alipay_card = models.CharField(max_length=128, blank=True, null=True, verbose_name="支付宝账户")
class Meta:
verbose_name = '账户信息'
verbose_name_plural = "账户信息"
def __str__(self):
return "%s(%s)" % (self.username, self.get_role_display())
def save(self, *args, **kwargs):
if len(self.uid) < 8:
self.uid = make_random_uuid()
super(UserInfo, self).save(*args, **kwargs)
class Token(models.Model):
"""
The default authorization token model.
"""
access_token = models.CharField(max_length=42, unique=True)
user = models.OneToOneField(
UserInfo, related_name='auth_token',
on_delete=models.CASCADE, verbose_name="关联用户"
)
created = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
def __str__(self):
return self.access_token
class VerifyName(models.Model):
user = models.OneToOneField(
UserInfo, related_name='verify_info',
on_delete=models.CASCADE, verbose_name="关联用户"
)
name = models.CharField(max_length=32, default="", verbose_name="真实姓名")
id_card = models.CharField(max_length=32, blank=True, null=True, verbose_name="身份证号或护照号")
address = models.CharField(max_length=128, blank=True, null=True, verbose_name="联系地址")
mobile = models.BigIntegerField(blank=True, verbose_name="联系电话", null=True)
date_verify = models.DateTimeField(auto_now_add=True, verbose_name="认证时间")
class Meta:
verbose_name = '实名认证'
verbose_name_plural = "实名认证"
def __str__(self):
return "%s" % (self.name)
######################################## APP表 ########################################
class Apps(models.Model):
app_id = models.CharField(max_length=64, unique=True) # ,唯一标识
user_id = models.ForeignKey(to="UserInfo",verbose_name="用户ID",on_delete=models.CASCADE)
type_choices = ((0, 'android'),(1, 'ios'))
type = models.SmallIntegerField(choices=type_choices, default=0, verbose_name="类型")
name = models.CharField(max_length=32,blank=True, null=True,verbose_name="应用名称")
short = models.CharField(max_length=16,unique=True,verbose_name="短链接")
bundle_id = models.CharField(max_length=64,blank=True,verbose_name="bundle id")
has_combo = models.OneToOneField(to="Apps", related_name='combo_app_info',
verbose_name="关联应用",on_delete=models.SET_NULL,null=True,blank=True)
# icon_img = models.ImageField(upload_to="course/%Y-%m", verbose_name='图标')
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
count_hits = models.BigIntegerField(verbose_name="下载次数",default=0)
description = models.TextField('描述', blank=True, null=True, default=None, )
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = '应用信息'
verbose_name_plural = "应用信息"
def __str__(self):
return "%s %s" % (self.name,self.get_type_display())
class AppReleaseInfo(models.Model):
is_master = models.BooleanField(verbose_name="是否事master版本",default=True)
release_id = models.CharField(max_length=64, unique=True,verbose_name="release 版本id")
app_id = models.ForeignKey(to="Apps",on_delete=models.CASCADE,verbose_name="属于哪个APP")
build_version = models.CharField(max_length=16,verbose_name="build版本",blank=True)
app_version = models.CharField(max_length=16,verbose_name="app版本",blank=True)
release_choices=((0,'android'),(1,'adhoc'),(2,'Inhouse'),(3,'unknown'))
release_type = models.SmallIntegerField(choices=release_choices, default=0, verbose_name="版本类型")
minimum_os_version = models.CharField(max_length=64,verbose_name="应用可安装的最低系统版本")
binary_size = models.BigIntegerField(verbose_name="应用大小")
binary_url = models.CharField(max_length=128,blank=True,verbose_name="APPurl")
icon_url = models.CharField(max_length=128,blank=True,verbose_name="图标url")
changelog = models.TextField('更新日志', blank=True, null=True, default=None, )
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
class Meta:
verbose_name = '应用详情'
verbose_name_plural = "应用详情"
def __str__(self):
return "%s" % (self.release_id)

@ -0,0 +1,192 @@
from django.test import TestCase
# # Create your tests here.
# import base64
# request_token='ZWZhN2MwNGMwMzg2MWVmMzA5YjUxNDlkMGU0ZWIwNDRiNTlmZWJjYzpuaW5ldmVuQHFxLmNvbQ%3D%3D=='
# print(base64.b64decode(request_token))
# import xml.etree.ElementTree as ET
# tree = ET.parse("xmlfir.xml")
# root = tree.getroot()
# print(root)
# print(root.tag) #data
# for child in root:
# print('========>',child.tag,child) #
# #========> country {'name': 'Liechtenstein'} Liechtenstein
# for i in child:
# print(i.tag,i.attrib,i.text)
import xml.etree.ElementTree as ET
ios_plist = ET.Element("plist", attrib={"version": "1.0"})
dict1 = ET.SubElement(ios_plist, "dict")
key1 = ET.SubElement(dict1, "key")
key1.text = 'items'
array1 = ET.SubElement(dict1, "array")
dict2 = ET.SubElement(array1, "dict")
et = ET.ElementTree(ios_plist) # 生成文档对象
# et.write("test.xml", encoding="utf-8", xml_declaration=True)
ET.dump(ios_plist) # 打印生成的格式
import json
import xmltodict
xjson = json.dumps(xmltodict.parse("""
<plist version="1.0"><dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string><![CDATA[https://ali-fir-pro-binary.jappstore.com/52e972d2749a404028e2ab7edb04d7a7f365184d?auth_key=1583720494-0-0-3dc9d1bf4f093c53f1193614ee5f6d3e]]></string>
</dict>
<dict>
<key>kind</key>
<string>display-image</string>
<key>needs-shine</key>
<integer>0</integer>
<key>url</key>
<string><![CDATA[https://ali-fir-pro-icon.fir.im/6be40db9e2bb729db1ce0db93b8ebdba50ea8b4d?auth_key=1583720494-0-0-0212635dc7c21a3c02cd2422d0213e99]]></string>
</dict>
<dict>
<key>kind</key>
<string>full-size-image</string>
<key>needs-shine</key>
<true/>
<key>url</key>
<string><![CDATA[https://ali-fir-pro-icon.fir.im/6be40db9e2bb729db1ce0db93b8ebdba50ea8b4d?auth_key=1583720494-0-0-0212635dc7c21a3c02cd2422d0213e99]]></string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>com.fengxin.im.test</string>
<key>bundle-version</key>
<string><![CDATA[1.1.7]]></string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string><![CDATA[亲友圈发发]]></string>
</dict>
</dict>
</array>
</dict>
</plist>
"""), indent=4)
# print(xjson)
# print(xjson["plist"]["dict"]["array"]["dict"])
# Fdata = xjson["plist"]["dict"]["array"]["dict"]
# odata = Fdata["array"]["dict"]
bin_url="bin_url"
img_url="img_url"
bundle_id="bundle_id"
bundle_version="bundle_version"
name="name"
ios_plist_tem="""
{
"plist": {
"@version": "1.0",
"dict": {
"key": "items",
"array": {
"dict": {
"key": [
"assets",
"metadata"
],
"array": {
"dict": [
{
"key": [
"kind",
"url"
],
"string": [
"software-package",
"%s"
]
},
{
"key": [
"kind",
"needs-shine",
"url"
],
"string": [
"display-image",
"%s"
],
"integer": "0"
},
{
"key": [
"kind",
"needs-shine",
"url"
],
"string": [
"full-size-image",
"%s"
],
"true": null
}
]
},
"dict": {
"key": [
"bundle-identifier",
"bundle-version",
"kind",
"title"
],
"string": [
"%s",
"%s",
"software",
"%s"
]
}
}
}
}
}
}
""" %(bin_url,img_url,img_url,bundle_id,bundle_version,name)
# print(xmltodict.unparse(json.loads(ios_plist_tem), indent=4))
tokenlist=[
{'a':{
'data':['1','fsdf'],
'time':65225
}}
]
for token in tokenlist:
print(token)
for k,v in token.items():
print(k,v)

@ -0,0 +1,45 @@
"""fir_ser URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,re_path,include
from rest_framework.routers import DefaultRouter
# from api.views.course import CourseView,CourseDetailView,CourseCategoryView
from api.views.login import LoginView,UserInfoView
from api.views.logout import LogoutView
from api.views.captcha import CaptchaView
from api.views.apps import AppsView,AppinfoView,AppReleaseinfoView
from api.views.uploads import UploadView,UploadImgView
from api.views.download import DownloadView,DownloadTokenView
# router=DefaultRouter()
# router.register("apps", AppsView)
urlpatterns = [
# path("",include(router.urls)),
re_path("login",LoginView.as_view()),
re_path("logout",LogoutView.as_view()),
re_path("captcha_check/",CaptchaView.as_view()),
re_path("apps$", AppsView.as_view()),
re_path("apps/(?P<app_id>\w+)", AppinfoView.as_view()),
re_path("appinfos/(?P<app_id>\w+)/(?P<act>\w+)", AppReleaseinfoView.as_view()),
re_path("upload$",UploadView.as_view()),
re_path("img/upload$",UploadImgView.as_view()),
re_path("userinfo",UserInfoView.as_view()),
# re_path("download/(?P<release_id>\w+)$", DownloadView.as_view()),
re_path("download_token/(?P<short>\w+)$", DownloadTokenView.as_view()),
]

@ -0,0 +1,47 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
"""
阿里巴巴旗下相关服务
蚂蚁金服
阿里云
因阿里云权限访问控制, 可能每种服务都会映射到一个访问key和秘钥(当然也可以用最大权限)
"""
import logging
import requests
logger = logging.getLogger(__name__)
class BaseAli(object):
API_BASE_URL = ""
_http = requests.session()
def __init__(self, timeout, auto_retry=False):
self.timeout = timeout
self.auto_retry = auto_retry
def request(self, *args, **kwargs):
raise NotImplementedError()
def get(self, url, **kwargs):
return self.request(
method="get",
url_or_endpoint=url,
**kwargs
)
def post(self, url, **kwargs):
return self.request(
method="post",
url_or_endpoint=url,
**kwargs
)

@ -0,0 +1,59 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
"""
阿里 api 调用出口
封装更易用的api
"""
from django.conf import settings
from django.utils.functional import LazyObject
from .pay.basic import AliPay
from .yun.basic import AliYunClient
class AliApi(object):
# 默认的支付配置
DEFAULT_ALI_PAY_CONFIG = settings.THIRD_PART_CONFIG["ALI_PAY"]["default"]
# 指定的支付配置(业务拓展)
SPECIFIC_ALI_PAY_CONFIG = settings.THIRD_PART_CONFIG["ALI_PAY"].get("pay") or DEFAULT_ALI_PAY_CONFIG
# 注册云配置
ALI_YUN_CONFIG = settings.THIRD_PART_CONFIG["ALI_YUN"]
def __init__(self, ):
# 支付类业务
self.pay = AliPay(
app_id=self.SPECIFIC_ALI_PAY_CONFIG["app_id"],
app_private_key_path=self.SPECIFIC_ALI_PAY_CONFIG["app_private_key_path"],
ali_public_key_path=self.SPECIFIC_ALI_PAY_CONFIG["alipay_public_key_path"],
notify_url=self.SPECIFIC_ALI_PAY_CONFIG["callback_url"],
return_url=self.SPECIFIC_ALI_PAY_CONFIG["callback_url"],
debug=self.SPECIFIC_ALI_PAY_CONFIG["debug"]
)
# 转账类业务
self.transfer = AliPay(
app_id=self.DEFAULT_ALI_PAY_CONFIG["app_id"],
app_private_key_path=self.DEFAULT_ALI_PAY_CONFIG["app_private_key_path"],
ali_public_key_path=self.DEFAULT_ALI_PAY_CONFIG["alipay_public_key_path"],
notify_url=self.SPECIFIC_ALI_PAY_CONFIG["callback_url"],
return_url=self.SPECIFIC_ALI_PAY_CONFIG["callback_url"],
debug=self.DEFAULT_ALI_PAY_CONFIG["debug"]
)
# 阿里云业务
self.yun = AliYunClient(
app_id=self.ALI_YUN_CONFIG.get("ACCESS_KEY"),
secret=self.ALI_YUN_CONFIG.get("SECRET")
)
class DefaultApi(LazyObject):
def _setup(self):
self._wrapped = AliApi()
ali_api = DefaultApi()

@ -0,0 +1,4 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2

@ -0,0 +1,4 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2

@ -0,0 +1,13 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
"""
实名认证相关接口实现, 实名认证三步走
认证初始化 > 开始认证 > 认证查询
"""
class AliCertification(object):
pass

@ -0,0 +1,4 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2

@ -0,0 +1,4 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2

@ -0,0 +1,10 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
from .page import AliPage
from .wap import AliWap
from .app import AliApp
from .transfer import AliTransfer
from .order import AliOrder

@ -0,0 +1,82 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
"""
App支付
"""
import logging
from .base import BaseAliPayAPI
logger = logging.getLogger(__name__)
class AliApp(BaseAliPayAPI):
def direct(self, subject, out_trade_no, total_amount, notify_url=None, return_url=None, **kwargs):
"""App快速下单接口
文档DOC地址: https://docs.open.alipay.com/203/105288/
Parameters
----------
subject : string
订单标题
out_trade_no : string
商户订单号唯一凭证
total_amount : string or float or int
支付金额单位元精确到分
notify_url: string
支付宝服务器主动通知商户服务器里指定的页面http/https路径
example: http://www.alipay.com/pay
return_url: string
同步返回地址HTTP/HTTPS开头字符串
kwargs : dict
以下均为可选参数
body : string
订单描述
goods_detail : json
订单包含的商品列表信息Json格式 {&quot;show_url&quot;:&quot;https://或http://打头的商品的展示地址&quot;}
在支付时可点击商品名称跳转到该地址
passback_params : string
公用回传参数如果请求时传递了该参数则返回给商户时会回传该参数
支付宝只会在异步通知时将该参数原样返回
本参数必须进行UrlEncode之后才可以发送给支付宝
goods_type : int
商品主类型0; 虚拟类商品1; 实物类商品默认
extend_params :
业务扩张参数(主要用于接入花呗分期)
Returns
-------
dict
"""
biz_content = {
"subject": subject,
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"product_code": "QUICK_MSECURITY_PAY", # 销售产品码,目前仅支持这个类型
# "qr_pay_mode":4
}
biz_content.update(**kwargs)
return self._generate_url("alipay.trade.app.pay", biz_content, notify_url, return_url)

@ -0,0 +1,32 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
class BaseAliPayAPI(object):
"""
支付宝支付API基类
"""
def __init__(self, client=None):
self._client = client
def _get(self, url, **kwargs):
if hasattr(self, "API_BASE_URL"):
kwargs['api_base_url'] = getattr(self, "API_BASE_URL")
return self._client.get(url, **kwargs)
def _post(self, url, **kwargs):
if hasattr(self, "API_BASE_URL"):
kwargs['api_base_url'] = getattr(self, "API_BASE_URL")
return self._client.post(url, **kwargs)
def _generate_url(self, method, *args, **kwargs):
return self._client.generate_url(method, *args, **kwargs)
@property
def app_id(self):
return self._client.app_id

@ -0,0 +1,44 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
"""
统一收单相关服务
"""
import logging
from .base import BaseAliPayAPI
logger = logging.getLogger(__name__)
class AliOrder(BaseAliPayAPI):
def query(self, out_biz_no=None, trade_no=None, **kwargs):
"""统一收单线下交易查询.
Parameters
----------
out_biz_no : string
订单支付时传入的商户订单号,和支付宝交易号不能同时为空
trade_no,out_trade_no如果同时存在优先取trade_no
trade_no : string
支付宝交易号和商户订单号不能同时为空
Returns
-------
dict
"""
biz_content = {
"out_biz_no": out_biz_no,
"trade_no": trade_no
}
biz_content.update(kwargs)
url = self._generate_url("alipay.trade.query", biz_content)
return self._get(url)

@ -0,0 +1,81 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
"""
PC支付场景
"""
import logging
from .base import BaseAliPayAPI
logger = logging.getLogger(__name__)
class AliPage(BaseAliPayAPI):
def direct(self, subject, out_trade_no, total_amount, notify_url=None, return_url=None, **kwargs):
"""PC快速下单接口
文档DOC地址: https://docs.open.alipay.com/270/alipay.trade.page.pay
Parameters
----------
subject : string
订单标题
out_trade_no : string
商户订单号唯一凭证
total_amount : string or float or int
支付金额单位元精确到分
notify_url: string
支付宝服务器主动通知商户服务器里指定的页面http/https路径
example: http://www.alipay.com/pay
return_url: string
同步返回地址HTTP/HTTPS开头字符串
kwargs : dict
以下均为可选参数
body : string
订单描述
goods_detail : json
订单包含的商品列表信息Json格式 {&quot;show_url&quot;:&quot;https://或http://打头的商品的展示地址&quot;}
在支付时可点击商品名称跳转到该地址
passback_params : string
公用回传参数如果请求时传递了该参数则返回给商户时会回传该参数
支付宝只会在异步通知时将该参数原样返回
本参数必须进行UrlEncode之后才可以发送给支付宝
goods_type : int
商品主类型0; 虚拟类商品1; 实物类商品默认
extend_params :
业务扩张参数(主要用于接入花呗分期)
Returns
-------
dict
"""
biz_content = {
"subject": subject,
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"product_code": "FAST_INSTANT_TRADE_PAY", # 销售产品码,目前仅支持这个类型
# "qr_pay_mode":4
}
biz_content.update(**kwargs)
return self._generate_url("alipay.trade.page.pay", biz_content, notify_url, return_url)

@ -0,0 +1,100 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
"""
转账支付 `api`
"""
import logging
from .base import BaseAliPayAPI
logger = logging.getLogger(__name__)
class AliTransfer(BaseAliPayAPI):
def transfer(self, out_biz_no, payee_account, amount, payee_real_name, is_verify=True, **kwargs):
"""转账接口
在构建完成数据结构体之前进行验证码校验
该方法加入了 业务代码, 业务若有变动, 请根据实际需求进行更改
Parameters
----------
out_biz_no : string
商户转账唯一凭证
payee_account : string
收款方账户
amount : string or int or float
转账金额(单位: , 保留俩位小数, 最小转帐金额 0.1)
payee_real_name: string
收款方真实姓名
is_verify: bool, default: True
是否进行验证码校验
kwargs : dict
以下均为可选参数
payer_show_name : string
付款方姓名
remark : string
转账备注支持200个英文/100个汉字当付款方为企业账户且转账金额达到大于等于50000remark不能为空
收款方可见会展示在收款用户的收支详情中
Returns
-------
dict
"""
# 如果需要校验, 获取验证码进行校验, 校验不通过直接响应校验不通过
if is_verify:
kwargs.pop("verify_code", None)
biz_content = {
"out_biz_no": out_biz_no,
"payee_account": payee_account,
"payee_type": "ALIPAY_LOGONID", # 收款方账户类型
"amount": amount,
"payee_real_name": payee_real_name
}
biz_content.update(**kwargs)
url = self._generate_url("alipay.fund.trans.toaccount.transfer", biz_content)
return self._get(url)
def query(self, out_biz_no, order_id, **kwargs):
"""转账交易查询接口.
Parameters
----------
out_biz_no : string
商户转账唯一凭证
order_id : string
支付宝商户转账唯一凭证
Returns
-------
dict
"""
biz_content = {
"out_biz_no": out_biz_no,
"order_id": order_id
}
biz_content.update(kwargs)
url = self._generate_url("alipay.fund.trans.order.query", biz_content)
return self._get(url)

@ -0,0 +1,81 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
"""
手机网站支付
"""
import logging
from .base import BaseAliPayAPI
logger = logging.getLogger(__name__)
class AliWap(BaseAliPayAPI):
def direct(self, subject, out_trade_no, total_amount, notify_url=None, return_url=None, **kwargs):
"""WAP快速下单接口
文档DOC地址: https://docs.open.alipay.com/203/105288/
Parameters
----------
subject : string
订单标题
out_trade_no : string
商户订单号唯一凭证
total_amount : string or float or int
支付金额单位元精确到分
notify_url: string
支付宝服务器主动通知商户服务器里指定的页面http/https路径
example: http://www.alipay.com/pay
return_url: string
同步返回地址HTTP/HTTPS开头字符串
kwargs : dict
以下均为可选参数
body : string
订单描述
goods_detail : json
订单包含的商品列表信息Json格式 {&quot;show_url&quot;:&quot;https://或http://打头的商品的展示地址&quot;}
在支付时可点击商品名称跳转到该地址
passback_params : string
公用回传参数如果请求时传递了该参数则返回给商户时会回传该参数
支付宝只会在异步通知时将该参数原样返回
本参数必须进行UrlEncode之后才可以发送给支付宝
goods_type : int
商品主类型0; 虚拟类商品1; 实物类商品默认
extend_params :
业务扩张参数(主要用于接入花呗分期)
Returns
-------
dict
"""
biz_content = {
"subject": subject,
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"product_code": "QUICK_WAP_WAY", # 销售产品码,目前仅支持这个类型
# "qr_pay_mode":4
}
biz_content.update(**kwargs)
return self._generate_url("alipay.trade.wap.pay", biz_content, notify_url, return_url)

@ -0,0 +1,198 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
import json
import inspect
from urllib.parse import quote_plus
from Crypto.PublicKey import RSA
from django.utils.timezone import now
from .. import BaseAli
from ..tools import calculate_signature
from .api.base import BaseAliPayAPI
from . import api
def _is_api_endpoint(instance):
return issubclass(instance.__class__, BaseAliPayAPI)
class AliPay(BaseAli):
# 在构造函数中初始化
API_BASE_URL = ""
# PC支付
pc = api.AliPage()
# Wap支付
wap = api.AliWap()
# App支付
app = api.AliApp()
# 转账
transfer = api.AliTransfer()
# 统一收单相关
order = api.AliOrder()
def __init__(
self, app_id, app_private_key_path, ali_public_key_path,
notify_url, return_url, debug,
):
# 如果是调试模式,则用调试api
if debug:
setattr(self, "API_BASE_URL", "https://openapi.alipaydev.com/gateway.do")
else:
setattr(self, "API_BASE_URL", "https://openapi.alipay.com/gateway.do")
# 暂时用不到父类方法参数(设置默认值)
super(AliPay, self).__init__(timeout=3, auto_retry=True)
self.app_id = app_id
self.notify_url = notify_url
self.return_url = return_url
# 加载应用的私钥
with open(app_private_key_path) as fp:
self.app_private_key = RSA.importKey(fp.read())
# 加载支付宝公钥
with open(ali_public_key_path) as fp:
self.ali_public_key = RSA.importKey(fp.read())
def __new__(cls, *args, **kwargs):
self = super(AliPay, cls).__new__(cls)
api_endpoints = inspect.getmembers(self, _is_api_endpoint)
for name, api_ins in api_endpoints:
api_cls = type(api_ins)
api_ins = api_cls(self)
setattr(self, name, api_ins)
return self
def request(self, method, url_or_endpoint, **kwargs):
if not url_or_endpoint.startswith(('http://', 'https://')):
api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL)
url = '{base}{endpoint}'.format(
base=api_base_url,
endpoint=url_or_endpoint
)
else:
url = url_or_endpoint
res = self._http.request(
method=method,
url=url,
**kwargs
)
res.raise_for_status()
return self._handle_result(res)
def _handle_result(self, res):
"""解析请求结果并校验签名
"""
return res.json()
def get_url_params(self, data):
data.pop("sign", None)
# 排序后[(k, v), ...]
ordered_items = sorted(
((k, v if not isinstance(v, dict) else json.dumps(v, separators=(',', ':')))
for k, v in data.items())
)
# 拼接成待签名的字符串
unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in ordered_items)
# 对上一步得到的字符串进行签名
sign = calculate_signature(unsigned_string.encode("utf-8"), self.app_private_key)
# 处理URL
quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in ordered_items)
# 添加签名,获得最终的订单信息字符串
signed_string = quoted_string + "&sign=" + quote_plus(sign)
return signed_string
def build_body(self, method, biz_content, notify_url=None, return_url=None):
"""构建请求体
公有请求体 + 业务请求体
Parameters
----------
method : string
请求API
biz_content: dict
业务参数
notify_url: string, default: None
异步通知地址
return_url: string, default: None
同步地址
Returns
-------
请求参数: dict
"""
data = {
"app_id": self.app_id,
"method": method,
"charset": "utf-8",
"sign_type": "RSA2",
"timestamp": now().strftime("%Y-%m-%d %H:%M:%S"),
"version": "1.0",
"biz_content": biz_content
}
# 同步回调
if notify_url is not None:
data["notify_url"] = notify_url
# 异步回调
if return_url is not None:
data["return_url"] = return_url
return data
def generate_url(self, method, biz_content, notify_url=None, return_url=None):
"""构建请求体.
公有请求体 + 业务请求体
Parameters
----------
method : string
请求API
biz_content: dict
业务参数
notify_url: string, default: None
异步通知 `url`
return_url: string, default: None
同步通知 `url`
Returns
-------
string
"""
if notify_url is None:
notify_url = self.notify_url
if return_url is None:
return_url = self.return_url
body = self.build_body(method, biz_content, notify_url, return_url)
params = self.get_url_params(body)
return "{}?{}".format(self.API_BASE_URL, params)

@ -0,0 +1,69 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/8/2
import json
import time
import uuid
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from base64 import encodebytes
from base64 import decodebytes
def format_params(params):
"""格式化参数
"""
return sorted(
((k, v if not isinstance(v, dict) else json.dumps(v, separators=(',', ':'))) for k, v in params.items())
)
def verify_signature(params, api_key):
"""验证签名
"""
sign = params.pop("sign", "")
if "sign_type" in params:
params.pop("sign_type")
# 针对字符串进行排序
ordered_items = format_params(params)
message = "&".join(u"{}={}".format(k, v) for k, v in ordered_items)
# 验签
signer = PKCS1_v1_5.new(api_key)
digest = SHA256.new()
digest.update(message.encode("utf8"))
if signer.verify(digest, decodebytes(sign.encode("utf8"))):
return True
return False
def calculate_signature(params, api_key):
"""计算签名
"""
signer = PKCS1_v1_5.new(api_key)
signature = signer.sign(SHA256.new(params))
# base64 编码,转换为unicode表示并移除回车
sign = encodebytes(signature).decode("utf8").replace("\n", "")
return sign
def get_uuid():
"""获取 `UUID`
"""
return str(uuid.uuid4())
def get_iso_8061_date():
"""获取 `iso` 标准时间
"""
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())

@ -0,0 +1,12 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/9/18
"""
阿里云相关服务, 目前提供支持
点播服务
短信服务
"""

@ -0,0 +1,7 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/9/18
from .vod import AliYunVod
from .sms import AliYunSms

@ -0,0 +1,42 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/9/18
class BaseAliYunAPI(object):
"""
阿里云服务API基类
"""
def __init__(self, client=None):
self._client = client
def _get(self, action, **kwargs):
if hasattr(self, "API_BASE_URL"):
kwargs['api_base_url'] = getattr(self, "API_BASE_URL")
if hasattr(self, "VERSION"):
kwargs["version"] = getattr(self, "VERSION")
return self._client.get(action, **kwargs)
def _post(self, action, **kwargs):
if hasattr(self, "API_BASE_URL"):
kwargs['api_base_url'] = getattr(self, "API_BASE_URL")
if hasattr(self, "VERSION"):
kwargs["version"] = getattr(self, "VERSION")
return self._client.post(action, **kwargs)
@property
def app_id(self):
return self._client.app_id
@property
def secret_key(self):
return self._client.secret_key

@ -0,0 +1,145 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/9/18
"""
短信服务
"""
from .base import BaseAliYunAPI
class AliYunSms(BaseAliYunAPI):
VERSION = "2017-05-25"
API_BASE_URL = "http://dysmsapi.aliyuncs.com/"
def send(self, phone_numbers, sign_name, template_code, template_param, **kwargs):
"""发送短信
详情参见
https://help.aliyun.com/document_detail/55284.html?spm=5176.8195934.1001856.3.a36a4183xdfqaD
Parameters
----------
phone_numbers : string
短信接收号码,支持以逗号分隔的形式进行批量调用批量上限为1000个手机号码.
发送国际/港澳台消息时接收号码格式为00+国际区号+号码0085200000000
sign_name: string
短信签名
template_code: string
短信模板ID发送国际/港澳台消息时请使用国际/港澳台短信模版
template_param: json
短信模板变量替换JSON串,友情提示:如果JSON中需要带换行符,请参照标准的JSON协议
kwargs: dict
可选字段
SmsUpExtendCode
上行短信扩展码,无特殊需要此字段的用户请忽略此字段
OutId
外部流水扩展字段
Returns
-------
dict
"""
data = {
"PhoneNumbers": phone_numbers,
"SignName": sign_name,
"TemplateCode": template_code,
"TemplateParam": template_param
}
data.update(**kwargs)
return self._get(action="SendSms", data=data)
def send_batch(
self,
phone_number_json,
sign_name_json,
template_code,
template_param_json,
sms_upextend_code_json=None,
):
"""短信批量发送
详情参见
https://help.aliyun.com/document_detail/66041.html?spm=a2c4g.11186623.6.565.290415e8yJ7E0N
Parameters
----------
phone_number_json : string
短信接收号码,JSON格式,批量上限为100个手机号码,
批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式
sign_name_json: string
短信签名,JSON格式
template_code: int
短信模板ID
template_param_json: int
短信模板变量替换JSON串,友情提示:如果JSON中需要带换行符,请参照标准的JSON协议
sms_upextend_code_json: string
上行短信扩展码,JSON格式无特殊需要此字段的用户请忽略此字段
"""
data = {
"PhoneNumberJson": phone_number_json,
"SignNameJson": sign_name_json,
"TemplateCode": template_code,
"TemplateParamJson": template_param_json
}
if sms_upextend_code_json is not None:
data["SmsUpExtendCodeJson"] = sms_upextend_code_json
return self._get(action="SendBatchSms", data=data)
def query(self, phone_number, send_date, page_size, current_page, biz_id=None):
"""短信查询
详情参见
https://help.aliyun.com/document_detail/55289.html?spm=a2c4g.11186623.6.563.56d7577756pHT4
Parameters
----------
phone_number : string
短信接收号码,如果需要查询国际短信,号码前需要带上对应国家的区号,区号的获取详见国际短信支持国家信息查询API接口
send_date: string
短信发送日期格式yyyyMMdd,支持最近30天记录查询
page_size: int
页大小Max=50
current_page: int
当前页码
biz_id: string OR None
可选, 发送流水号,从调用发送接口返回值中获取
Returns
-------
dict
"""
data = {
"PhoneNumber": phone_number,
"SendDate": send_date,
"PageSize": page_size,
"CurrentPage": current_page
}
if biz_id is not None:
data["BizId"] = biz_id
return self._get(action="QuerySendDetails", data=data)

@ -0,0 +1,213 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/9/18
"""
点播服务
"""
from .base import BaseAliYunAPI
class AliYunVod(BaseAliYunAPI):
VERSION = "2017-03-21"
API_BASE_URL = "http://vod.cn-shanghai.aliyuncs.com/"
def get_play_auth(self, video_id, timeout):
"""获取播放 `auth`
Parameters
----------
video_id : string
视频 `vid`
timeout: string
获取到的播放 `key` 的有效时间
Returns
-------
dict
"""
data = {
"VideoId": video_id,
"AuthInfoTimeout": timeout
}
return self._get(action="GetVideoPlayAuth", data=data)
def get_play_info(
self, video_id, formats=None, timeout=None, stream_type=None, definition=None,
result_type=None, output_type=None,
):
"""获取播放 `auth`
Parameters
----------
video_id : string
视频 `vid`
formats: string
视频流格式多个用逗号分隔支持格式mp4 m3u8 mp3
默认获取所有格式的流
timeout: string
播放地址过期时间单位
最小值为1未指定时以URL鉴权中用户设置的默认有效时长为准最大值无限制
只有开启了 URL鉴权 才会生效
stream_type: string
视频流类型多个用逗号分隔支持类型video audio
默认获取所有类型的流
definition: string
视频流清晰度多个用逗号分隔取值范围FD流畅LD标清SD高清HD超清OD原画2K2K4K4K
默认获取所有清晰度的流
result_type: string
返回数据类型取值范围
Single每种清晰度和格式只返回一路最新转码完成的流
Multiple每种清晰度和格式返回所有转码完成的流
默认为Single类型
output_type: string
输出地址类型取值范围
oss回源地址
cdn加速地址
默认为cdn类型注意仅支持播放格式为mp4的oss地址
Returns
-------
dict
"""
data = {
"VideoId": video_id,
"Formats": formats,
"AuthTimeout": timeout,
"StreamType": stream_type,
"Definition": definition,
"ResultType": result_type,
"OutputType": output_type
}
return self._get(action="GetPlayInfo", data=data)
def query_flow_data(
self, domain_name=None, start_time=None, end_time=None, time_merge=None, interval=None,
location_name_en=None, isp_name_en=None,
):
"""查询流量数据
获取加速域名的网络流量监控数据单位byte
不指定StartTime和EndTime时默认读取过去24小时的数据也可以按指定的起止时间查询
注意
支持域名批量查询多个域名用半角逗号分隔
最多可获取近90天的数据
Parameters
----------
domain_name : string
要查询的域名若为空则默认返回所有加速域名的合并数据支持批量查询多个域名用半角逗号分隔
start_time: string
获取数据起始时间点日期格式按照ISO8601表示法使用UTC时间
若为空则默认读取最近24小时的数据
end_time: string
结束时间需大于起始时间日期格式按照ISO8601表示法使用UTC时间格式为YYYY-MM-DDThh:mmZ
time_merge: string
时间合并取值范围
on默认值每条记录的时间间隔会根据时间跨度进行合并
off返回5分钟粒度数据最大时间跨度为31天
interval: string
查询数据的时间粒度单位取值范围300 3600 14400 28800 86400
默认值300
location_name_en: string
区域英文名默认为所有区域
isp_name_en: string
运营商英文名默认为所有运营商
Returns
-------
dict
"""
data = {
"DomainName": domain_name,
"StartTime": start_time,
"EndTime": end_time,
"TimeMerge": time_merge,
"Interval": interval,
"LocationNameEn": location_name_en,
"IspNameEn": isp_name_en
}
return self._get(action="DescribeDomainFlowData", data=data)
def query_bps_data(
self, domain_name=None, start_time=None, end_time=None, time_merge=None, interval=None,
location_name_en=None, isp_name_en=None,
):
"""查询网络带宽
获取加速域名的网络带宽监控数据单位bit/second
不指定StartTime和EndTime时默认读取过去24小时的数据也可以按指定的起止时间查询
注意
支持域名批量查询多个域名用半角逗号分隔
最多可获取近90天的数据
Parameters
----------
domain_name : string
要查询的域名若为空则默认返回所有加速域名的合并数据支持批量查询多个域名用半角逗号分隔
start_time: string
获取数据起始时间点日期格式按照ISO8601表示法使用UTC时间
若为空则默认读取最近24小时的数据
end_time: string
结束时间需大于起始时间日期格式按照ISO8601表示法使用UTC时间格式为YYYY-MM-DDThh:mmZ
time_merge: string
时间合并取值范围
on默认值每条记录的时间间隔会根据时间跨度进行合并
off返回5分钟粒度数据最大时间跨度为31天
interval: string
查询数据的时间粒度单位取值范围300 3600 14400 28800 86400
默认值300
location_name_en: string
区域英文名默认为所有区域
isp_name_en: string
运营商英文名默认为所有运营商
Returns
-------
dict
"""
data = {
"DomainName": domain_name,
"StartTime": start_time,
"EndTime": end_time,
"TimeMerge": time_merge,
"Interval": interval,
"LocationNameEn": location_name_en,
"IspNameEn": isp_name_en
}
return self._get(action="DescribeDomainBpsData", data=data)

@ -0,0 +1,226 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 2018/9/18
import hmac
import base64
import inspect
import logging
from urllib.parse import quote
from .. import BaseAli
from ..yun import api
from ..yun.api.base import BaseAliYunAPI
from ..tools import get_uuid
from ..tools import get_iso_8061_date
logger = logging.getLogger(__name__)
QUERY_STRING = None
def _get_signature(string_to_sign, secret, signer=hmac):
"""获取签名
"""
secret = "{}&".format(secret)
hmb = signer.new(secret.encode("utf-8"), string_to_sign.encode("utf-8"), "sha1").digest()
return quote(base64.standard_b64encode(hmb).decode("ascii"), safe="~")
def __pop_standard_urlencode(query):
ret = ""
for item in query:
if not item[1]:
continue
ret += quote(item[0], safe="~") + "=" + quote(item[1], safe="~") + "&"
return ret
def _compose_sign_string(query_params):
"""组成签名字符串
"""
sorted_parameters = sorted(query_params.items(), key=lambda url_params: url_params[0])
string_to_sign = __pop_standard_urlencode(sorted_parameters)
query_string = string_to_sign[:-1]
global QUERY_STRING
QUERY_STRING = query_string
tosign = "GET&%2F&" + quote(query_string, safe="~")
return tosign
def _is_api_endpoint(instance):
return issubclass(instance.__class__, BaseAliYunAPI)
class AliYunClient(BaseAli):
VERSION = "" # 每个接口都需要版本号
API_BASE_URL = "" # 阿里云api网关地址均从使用的接口定义
# 点播api
vod = api.AliYunVod()
# 短信api
sms = api.AliYunSms()
def __new__(cls, *args, **kwargs):
"""注册接口
"""
self = super(AliYunClient, cls).__new__(cls)
api_endpoints = inspect.getmembers(self, _is_api_endpoint)
for name, api_ins in api_endpoints:
api_cls = type(api_ins)
api_ins = api_cls(self)
setattr(self, name, api_ins)
return self
def __init__(self, app_id, secret, timeout=None, auto_retry=False):
super(AliYunClient, self).__init__(
timeout, auto_retry
)
self.app_id = app_id
self.secret = secret
def request(self, method, action, **kwargs):
"""构造请求用户授权的url
Parameters
----------
method : string
请求类型
action : string
API的命名固定值如发送短信API的值为SendSms
kwargs : dict
请求参数
Returns
-------
dict
"""
query_string = ""
api_base_url = kwargs.pop("api_base_url", self.API_BASE_URL)
# 获取请求参数
if isinstance(kwargs.get("data", {}), dict):
data = kwargs["data"]
data["Action"] = action
# 封装公有请求参数
# 接口版本号
if "Version" not in data:
data["Version"] = kwargs.pop("version", self.VERSION)
# 时间戳
data["Timestamp"] = get_iso_8061_date()
# 签名算法名称
data["SignatureMethod"] = "HMAC-SHA1"
# 签名版本
data["SignatureVersion"] = "1.0"
# 随机码
data["SignatureNonce"] = get_uuid()
# access key
data["AccessKeyId"] = self.app_id
# 接口响应类型均为 `JSON`
data["Format"] = "JSON"
# 计算签名
string_to_sign = _compose_sign_string(data)
signature = _get_signature(string_to_sign, self.secret)
query_string = '?{}{}'.format(QUERY_STRING, "&" + "Signature=" + signature)
# 结果解析器, 如果需要定制可单独定制
result_processor = kwargs.pop("result_processor", None)
url = "{}{}".format(api_base_url, query_string)
res = self._http.request(
method=method,
url=url,
**kwargs
)
# res.raise_for_status()
return self._handle_result(res, method, url, result_processor, **kwargs)
def _handle_result(self, res, method=None, url=None,
result_processor=None, **kwargs):
"""结果解析
Parameters
----------
res : request instance
响应对象 response
method : string
请求方法
url : string
请求的 `url`
result_processor: func OR None
结果处理器
kwargs: dict
更多参数
Returns
-------
dict
"""
if not isinstance(res, dict):
result = res.json()
else:
result = res
if not isinstance(result, dict):
return result
if "Code" in result:
code = result.get("Code")
if code != "OK":
logger.error("AliApi {} 调用异常, Code {} Errmsg {}".format(
url, code, result.get("Message")
))
if code in []:
self.request(method, url, **kwargs)
return result if not result_processor else result_processor(result)
def get(self, url, **kwargs):
return self.request(
method="get",
action=url,
**kwargs
)
def post(self, url, **kwargs):
return self.request(
method="post",
action=url,
**kwargs
)

@ -0,0 +1,76 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/8
import uuid
import string
import random
import time
from threading import Thread
class DownloadToken(object):
TokenLists = []
flag={'stat':1}
def __init__(self):
self.auto_clean_totken()
def make_token(self,release_id,time_limit=60):
random_str = uuid.uuid1().__str__().split("-")[0:-1]
user_ran_str = uuid.uuid5(uuid.NAMESPACE_DNS, release_id[0]).__str__().split("-")
user_ran_str.extend(random_str)
new_str = "".join(user_ran_str)
self.TokenLists.append({new_str:{
"atime":time.time()+time_limit,
"data":release_id
}})
return new_str
def verify_token(self,token,release_id):
try:
for itoken in self.TokenLists:
if itoken.get(token,None):
if release_id in itoken.get(token,None).get("data",None):
return True
except Exception as e:
print(e)
return False
return False
def auto_clean_totken(self):
if self.flag.get("stat"):
t=Thread(target=self.clean_Token)
t.start()
self.flag["stat"]=0
def clean_Token(self):
while True:
ncount = len(self.TokenLists)
ntime = time.time()
for itokendic in self.TokenLists:
for token, data in itokendic.items():
if int(ntime) - int(data["atime"]) > 0:
self.TokenLists.remove(itokendic)
print("开始清理token,现在token:%s 清理token:%s 剩余token:%s"%(ncount,ncount-len(self.TokenLists),len(self.TokenLists)))
time.sleep(30)
def generateTokenForMedium(medium):
if medium == 'email':
return generateAlphanumericTokenOfLength(32)
elif medium == 'wechat':
return 'WeChat'
else:
return generateNumericTokenOfLength(6)
def generateNumericTokenOfLength(length):
return "".join([random.choice(string.digits) for _ in range(length)])
def generateAlphanumericTokenOfLength(length):
return "".join([random.choice(string.digits + string.ascii_lowercase + string.ascii_uppercase) for _ in range(length)])

@ -0,0 +1,245 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/6
from fir_ser.settings import MEDIA_ROOT,MEDIA_URL
import os
from .firApi import AppInfo
from api.utils.randomstrings import make_app_uuid
from api.models import AppReleaseInfo,Apps
import random,xmltodict,json
from .TokenManager import DownloadToken
def make_download_token(release_id,time_limit):
release_ids = []
if release_id:
release_ids = [release_id]
dtoken = DownloadToken()
download_token = dtoken.make_token(release_ids,time_limit)
return download_token
def make_resigned(bin_url,img_url,bundle_id,bundle_version,name):
ios_plist_tem = """
{
"plist": {
"@version": "1.0",
"dict": {
"key": "items",
"array": {
"dict": {
"key": [
"assets",
"metadata"
],
"array": {
"dict": [
{
"key": [
"kind",
"url"
],
"string": [
"software-package",
"%s"
]
},
{
"key": [
"kind",
"needs-shine",
"url"
],
"string": [
"display-image",
"%s"
],
"integer": "0"
},
{
"key": [
"kind",
"needs-shine",
"url"
],
"string": [
"full-size-image",
"%s"
],
"true": null
}
]
},
"dict": {
"key": [
"bundle-identifier",
"bundle-version",
"kind",
"title"
],
"string": [
"%s",
"%s",
"software",
"%s"
]
}
}
}
}
}
}
""" % (bin_url, img_url, img_url, bundle_id, bundle_version, name)
return xmltodict.unparse(json.loads(ios_plist_tem),pretty=True)
def bytes2human(n):
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
for i, s in enumerate(symbols):
prefix[s] = 1 << (i + 1) * 10
for s in reversed(symbols):
if float(n) >= prefix[s]:
value = float(n) / prefix[s]
return '%.1f%s' % (value, s)
return '%sB' % n
def delete_apps_storage(app_file_name,release_type):
app_path = os.path.join(MEDIA_ROOT,"apps")
icon_path = os.path.join(MEDIA_ROOT,"icons")
try:
if release_type == 0:
app_file_full_name="%s.apk" %app_file_name
else:
app_file_full_name="%s.ipa" %app_file_name
remove_lists=[
os.path.join(app_path, app_file_full_name),
os.path.join(icon_path, "%s.png" % (app_file_name))
]
for delfiles in remove_lists:
os.remove(delfiles)
except Exception as e:
print(e)
def delete_apps_icon_storage(app_file_name):
icon_path = os.path.join(MEDIA_ROOT,"icons")
remove_lists=[
os.path.join(icon_path, "%s.png" % (app_file_name))
]
try:
for delfiles in remove_lists:
os.remove(delfiles)
except Exception as e:
print(e)
def get_release_type(app_file_name,appinfo):
extension = app_file_name.split(".")[1]
if extension == "ipa":
release_type = appinfo.get("release_type")
if release_type == "Adhoc": # Adhoc: 内测版 Inhouse:企业版
return 1
elif release_type == "Inhouse":
return 2
else:
return 3
else:
return 0
def get_app_type(app_file_name):
extension = app_file_name.split(".")[1]
if extension == "ipa":
return 1
if extension == "apk":
return 0
else:
return 2
def get_random_short():
short_url = ''.join(random.sample(
['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f',
'e', 'd', 'c', 'b', 'a'], 4))
appobj=Apps.objects.filter(short=short_url).first()
if appobj:
return get_random_short()
else:
return short_url
def AnalyzeUtil(app_file_name,request):
user_obj = request.user
app_path = os.path.join(MEDIA_ROOT,"apps")
icon_path = os.path.join(MEDIA_ROOT,"icons")
app_full_path = os.path.join(app_path,app_file_name)
try:
apiobj = AppInfo(app_full_path)
appinfo = apiobj.get_app_data()
app_img=apiobj.make_app_png(icon_path,app_file_name.split(".")[0])
except Exception as e:
print(e)
delete_apps_storage(app_file_name.split(".")[0], 0)
delete_apps_storage(app_file_name.split(".")[0], 1)
return
bundle_id = appinfo.get("bundle_id")
app_uuid = make_app_uuid(user_obj,bundle_id+app_file_name.split(".")[1])
##判断是否存在该app
appmobj=Apps.objects.filter(app_id=app_uuid,user_id=user_obj).first()
if not appmobj:
appdata={
"app_id":app_uuid,
"user_id":user_obj,
"type":get_app_type(app_file_name),
"name": appinfo["labelname"],
"short":get_random_short(),
"bundle_id":bundle_id,
"count_hits":0
}
try:
appmobj=Apps.objects.create(**appdata)
except Exception as e:
print(e)
delete_apps_storage(app_file_name.split(".")[0],get_release_type(app_file_name,appinfo))
return
AppReleaseInfo.objects.filter(app_id=appmobj).update(**{"is_master":False})
release_data={
"app_id":appmobj,
"icon_url":"/".join([MEDIA_URL.strip("/"),"icons",app_img]),
"release_id":app_file_name.split(".")[0],
"build_version":appinfo.get("version"),
"app_version":appinfo.get("versioncode"),
"release_type":get_release_type(app_file_name,appinfo),
"minimum_os_version":appinfo.get("miniOSversion", None),
"binary_size":os.path.getsize(app_full_path),
"is_master":True
}
AppReleaseInfo.objects.create(**release_data)

@ -0,0 +1,71 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 12月
# author: NinEveN
# date: 2019/12/19
import re
import os
import zipfile
# from biplist import *
from androguard.core.bytecodes import apk
import plistlib
def get_android_data( package_file):
try:
apkobj = apk.APK(package_file)
except Exception as err:
print(err)
else:
if apkobj.is_valid_APK():
versioncode = apkobj.get_androidversion_code()
bundle_id = apkobj.get_package()
labelname = apkobj.get_app_name()
versioname = apkobj.get_androidversion_name()
sdk_version = apkobj.get_target_sdk_version()
return labelname, bundle_id, versioncode, versioname, sdk_version
def get_ios_data( ios_file):
if zipfile.is_zipfile(ios_file):
ipaobj = zipfile.ZipFile(ios_file)
info_path = get_ios_info_path(ipaobj)
if info_path:
plist_data = ipaobj.read(info_path)
print(plist_data)
if b"ProvisionedDevices" in plist_data:
print("adhoc")
else:
print("inhouse")
def get_ios_info_path( ipaobj):
infopath_re = re.compile(r'.*.app/embedded.mobileprovision')
for i in ipaobj.namelist():
m = infopath_re.match(i)
if m is not None:
return m.group()
def get_ios_icon_path( ipaobj):
infopath_re = re.compile(r'Payload/[^/]*.app/AppIcon[^/]*[^(ipad)].png')
for i in ipaobj.namelist():
m = infopath_re.match(i)
if m is not None:
return m.group()
def get_package_size( package_path):
fsize = os.path.getsize(package_path)
fsize = fsize / float(1024 * 1024)
return round(fsize, 2)
# a=get_ios_data("/root/v1.1.8/Riot.ipa")
# print(a)
ipaobj = zipfile.ZipFile("M:\\Breeze.ipa")
info_path = get_ios_icon_path(ipaobj)
print(info_path)
with open('111222.png', 'wb') as f:
f.write(ipaobj.read(info_path))

@ -0,0 +1,385 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 12月
# author: NinEveN
# date: 2019/12/19
import requests
import zipfile, re, os, random,io
from androguard.core.bytecodes import apk
import plistlib
import qrcode
from PIL import Image
def bytes2human(n):
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
for i, s in enumerate(symbols):
prefix[s] = 1 << (i + 1) * 10
for s in reversed(symbols):
if n >= prefix[s]:
value = float(n) / prefix[s]
return '%.1f%s' % (value, s)
return '%sB' % n
class AppInfo(object):
def __init__(self, app_path):
self.app_path = app_path
self.result = {}
def make_app_png(self,png_path,filename):
zf = zipfile.ZipFile(self.app_path)
name_list = zf.namelist()
if self.app_path.endswith("apk"):
# pattern = re.compile(r'res/drawable[^/]*/app_icon.png')
iconfile = apk.APK(self.app_path).get_app_icon()
size = 999
elif self.app_path.endswith("ipa"):
size = 0
pattern = re.compile(r'Payload/[^/]*.app/AppIcon[^/]*[^(ipad)].png')
iconfile = ""
for path in name_list:
m = pattern.match(path)
if m is not None:
filepath = m.group()
fsize = len(zf.read(filepath))
if size == 0:
iconfile = filepath
if size < fsize:
size = fsize
iconfile = filepath
else:
raise Exception("File type error")
if size == 0:
raise Exception("File size error")
if not os.path.isdir(png_path):
os.makedirs(png_path)
with open(os.path.join(png_path, "%s.png" %filename), 'wb') as f:
f.write(zf.read(iconfile))
return "%s.png" %filename
def get_app_data(self):
if self.app_path.endswith("apk"):
return self.__get_android_data()
elif self.app_path.endswith("ipa"):
return self.__get_ios_data()
else:
raise Exception("File type error")
def __get_ios_info_path(self, ipaobj):
infopath_re = re.compile(r'Payload/[^/]*.app/Info.plist')
for i in ipaobj.namelist():
m = infopath_re.match(i)
if m is not None:
return m.group()
def __get_android_data(self):
try:
apkobj = apk.APK(self.app_path)
except Exception as err:
raise err
else:
if apkobj.is_valid_APK():
versioncode = apkobj.get_androidversion_code()
bundle_id = apkobj.get_package()
labelname = apkobj.get_app_name()
version = apkobj.get_androidversion_name()
sdk_version = apkobj.get_min_sdk_version()
self.result = {
"labelname": labelname,
"bundle_id": bundle_id,
"versioncode": versioncode,
"version": version,
"miniOSversion": sdk_version,
"type": "android",
}
return self.result
def __get_ios_data(self):
if zipfile.is_zipfile(self.app_path):
ipaobj = zipfile.ZipFile(self.app_path)
info_path = self.__get_ios_info_path(ipaobj)
if info_path:
plist_data = ipaobj.read(info_path)
plist_root = plistlib.loads(plist_data)
labelname = plist_root['CFBundleDisplayName']
versioncode = plist_root['CFBundleVersion']
bundle_id = plist_root['CFBundleIdentifier']
version = plist_root['CFBundleShortVersionString']
miniOSversion = plist_root["MinimumOSVersion"]
self.result = {
"labelname": labelname,
"bundle_id": bundle_id,
"versioncode": versioncode,
"version": version,
"miniOSversion":miniOSversion,
"type": "ios",
}
release_type_info = self.__get_ios_release_type(ipaobj)
if info_path:
plist_data = ipaobj.read(release_type_info)
if b"ProvisionedDevices" in plist_data:
self.result["release_type"] = "Adhoc"
else:
self.result["release_type"] = "Inhouse"
return self.result
def __get_ios_release_type(self, ipaobj):
infopath_re = re.compile(r'Payload/[^/]*.app/embedded.mobileprovision')
for i in ipaobj.namelist():
m = infopath_re.match(i)
if m is not None:
return m.group()
class FirApi(object):
def __init__(self, api_token):
'''
:param api_token: 长度为 32, 用户在 fir api_token
'''
self.api_token = api_token
self.app_url = "http://api.fir.im/apps"
def __request(self, action, *args, **kwargs):
fun_re = getattr(requests, action, None)
res = fun_re(*args, **kwargs)
return res
def get_version_by_id(self, id):
'''
:param id: 应用ID可在"应用管理"->"基本信息"查看
:return:
'''
url = "%s/latest/%s?api_token=%s" % (self.app_url, id, self.api_token)
res = self.__request("get", url)
if res.status_code == 200:
return res.json()
else:
raise Exception(res.content)
def get_version_by_bundle_id(self, bundle_id, type):
'''
:param bundle_id: Bundle ID(iOS) / Package name(Android)
:param type: 应用类型 ( ios / android )
:return:
'''
url = "%s/latest/%s?api_token=%s&type=%s" % (self.app_url,
bundle_id, self.api_token,
type) # 使用 bundle_id 请求必填 ( ios / android )
res = self.__request("get", url)
if res.status_code == 200:
return res.json()
else:
raise Exception(res.content)
def __get_upload_token(self, bundle_id, type):
'''
:param bundle_id: App bundleId发布新应用时必填
:param type: ios 或者 android发布新应用时必填
:return:
'''
data = {
"type": type,
"bundle_id": bundle_id,
"api_token": self.api_token,
}
res = self.__request("post", self.app_url, data)
if res.status_code == 201:
return res.json()
else:
raise Exception(res.content)
def upload_app(self, app_path):
appobj = AppInfo(app_path)
appinfo = appobj.get_app_data()
icon_path = appobj.make_app_png()
bundle_id = appinfo.get("bundle_id")
upcretsdata = self.__get_upload_token(bundle_id, appinfo.get("type"))
icon_key = upcretsdata["cert"]["icon"]["key"]
icon_token = upcretsdata["cert"]["icon"]["token"]
icon_upload_url = upcretsdata["cert"]["icon"]["upload_url"]
short_url = upcretsdata["short"]
icon_data = {
"key": icon_key,
"token": icon_token,
}
files = {'file': (os.path.basename(icon_path), open(icon_path, 'rb'), {'Expires': '0'})}
res = self.__request("post", icon_upload_url, data=icon_data, files=files)
if res.status_code == 200:
print(res.json())
else:
raise Exception(res.content)
binary_key = upcretsdata["cert"]["binary"]["key"]
binary_token = upcretsdata["cert"]["binary"]["token"]
binary_upload_url = upcretsdata["cert"]["binary"]["upload_url"]
binary_data = {
"key": binary_key,
"token": binary_token,
"x:name": appinfo["labelname"],
"x:version": appinfo["version"],
"x:build": appinfo["versioncode"],
"x:changelog": ""
}
if appinfo["type"] == "ios": binary_data["x:release_type"] = appinfo.get("release_type",
"Adhoc") # Adhoc: 内测版 Inhouse:企业版
files = {'file': (os.path.basename(app_path), open(app_path, 'rb'), {'Expires': '0'})}
res = self.__request("post", binary_upload_url, data=binary_data, files=files)
if res.status_code == 200:
print(res.json())
QrCode.make_logo_qr(os.path.join("https://fir.im/", short_url), icon_path, "fir_%s.png"%(appinfo["labelname"]))
else:
raise Exception(res.content)
def get_app_list(self):
url = "%s?api_token=%s" % (self.app_url, self.api_token)
res = self.__request("get", url)
if res.status_code == 200:
return res.json()
else:
raise Exception(res.content)
def get_app_infos_by_id(self, id):
'''
:param id: 应用id可在"应用管理"->"基本信息"查看
:return:
'''
url = "%s/%s?api_token=%s" % (self.app_url, id, self.api_token)
res = self.__request("get", url)
if res.status_code == 200:
return res.json()
else:
raise Exception(res.content)
def modify_app_info(self, id, name=None, desc=None, short=None, genre_id=None,
is_opened=None, is_show_plaza=None, passwd=None,
store_link_visible=None):
'''
:param id: 应用id可在"应用管理"->"基本信息"查看
:param name: app 名称
:param desc: app 详细描述
:param short: app 短链接
:param genre_id: 类别 id
:param is_opened: 是否公开
:param is_show_plaza: 是否展示在广场页
:param passwd: 密码访问(不能和is_opened is_show_plaza联合使用)
:param store_link_visible: 应用市场链接是否显示
:return:
'''
url = "%s/%s" % (self.app_url, id)
data = {
"api_token": self.api_token,
}
if name: data["name"] = name
if desc: data["desc"] = desc
if short: data["short"] = short
if genre_id: data["genre_id"] = genre_id
if is_opened: data["is_opened"] = is_opened
if is_show_plaza and not passwd: data["is_show_plaza"] = is_show_plaza
if passwd and not is_show_plaza: data["passwd"] = passwd
if is_show_plaza and passwd:
raise Exception("密码访问不能和is_opened is_show_plaza联合使用")
if store_link_visible: data["name"] = store_link_visible
res = self.__request("put", url, data)
if res.status_code == 200:
return res.json()
else:
raise Exception(res.content)
class QrCode(object):
@staticmethod
def make_logo_qr(str, logo_path, save_path=None):
qr = qrcode.QRCode(
version=4,
error_correction=qrcode.constants.ERROR_CORRECT_Q,
box_size=8,
border=2
)
# 添加转换内容
qr.add_data(str)
qr.make(fit=True)
# 生成二维码
img = qr.make_image()
#
img = img.convert("RGBA")
# 添加logo
if logo_path and os.path.exists(logo_path):
icon = Image.open(logo_path)
# 获取二维码图片的大小
img_w, img_h = img.size
factor = 4
size_w = int(img_w / factor)
size_h = int(img_h / factor)
# logo图片的大小不能超过二维码图片的1/4
icon_w, icon_h = icon.size
if icon_w > size_w:
icon_w = size_w
if icon_h > size_h:
icon_h = size_h
icon = icon.resize((icon_w, icon_h), Image.ANTIALIAS)
# 详见:http://pillow.readthedocs.org/handbook/tutorial.html
# 计算logo在二维码图中的位置
w = int((img_w - icon_w) / 2)
h = int((img_h - icon_h) / 2)
icon = icon.convert("RGBA")
img.paste(icon, (w, h), icon)
# 详见:http://pillow.readthedocs.org/reference/Image.html#PIL.Image.Image.paste
# 保存处理后图片
if save_path:
if not os.path.isdir(os.path.dirname(save_path)):
os.makedirs(os.path.dirname(save_path))
img.save(save_path)
else:
imgByteArr = io.BytesIO()
img.save(imgByteArr,format="PNG")
return imgByteArr.getvalue()
if __name__ == '__main__':
apk_path = "/root/hehemajiang.apk"
API_TOKEN = "3d2a41159a7d8c396ddf0e88799de14a"
fir = FirApi(API_TOKEN)
#上传
fir.upload_app(apk_path)
# 获取版本信息
# print(fir.get_version_by_id("5dfb54d3b2eb4612589c3040"))
#获取app详细信息
# print(fir.get_app_infos_by_id("5dfb54d3b2eb4612589c3040"))
#获取列表
# print(fir.get_app_list())

@ -0,0 +1,91 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 12月
# author: NinEveN
# date: 2019/12/19
'''
Python生成二维码 v1.0
主要将文本生成二维码图片
测试一将文本生成白底黑字的二维码图片
测试二将文本生成带logo的二维码图片
'''
import qrcode
from PIL import Image
import os
#生成二维码图片
def make_qr(str,save):
qr=qrcode.QRCode(
version=4, #生成二维码尺寸的大小 1-40 1:21*21(21+(n-1)*4)
error_correction=qrcode.constants.ERROR_CORRECT_M, #L:7% M:15% Q:25% H:30%
box_size=10, #每个格子的像素大小
border=2, #边框的格子宽度大小
)
qr.add_data(str)
qr.make(fit=True)
img=qr.make_image()
img.save(save)
#生成带logo的二维码图片
def make_logo_qr(str,logo,save):
#参数配置
qr=qrcode.QRCode(
version=4,
error_correction=qrcode.constants.ERROR_CORRECT_Q,
box_size=8,
border=2
)
#添加转换内容
qr.add_data(str)
#
qr.make(fit=True)
#生成二维码
img=qr.make_image()
#
img=img.convert("RGBA")
#添加logo
if logo and os.path.exists(logo):
icon=Image.open(logo)
#获取二维码图片的大小
img_w,img_h=img.size
factor=4
size_w=int(img_w/factor)
size_h=int(img_h/factor)
#logo图片的大小不能超过二维码图片的1/4
icon_w,icon_h=icon.size
if icon_w>size_w:
icon_w=size_w
if icon_h>size_h:
icon_h=size_h
icon=icon.resize((icon_w,icon_h),Image.ANTIALIAS)
#详见:http://pillow.readthedocs.org/handbook/tutorial.html
#计算logo在二维码图中的位置
w=int((img_w-icon_w)/2)
h=int((img_h-icon_h)/2)
icon=icon.convert("RGBA")
img.paste(icon,(w,h),icon)
#详见:http://pillow.readthedocs.org/reference/Image.html#PIL.Image.Image.paste
#保存处理后图片
img.save(save)
if __name__=='__main__':
save_path='base__runmethod_02.png' #生成后的保存文件
logo='/tmp/pycharm_project_986/fir/tmp/icon/cmjpwyltznbioseh' #logo图片
str="https://fir.im/luy4"
make_logo_qr(str,logo,save_path)

@ -0,0 +1,66 @@
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache
import datetime
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from rest_framework.exceptions import AuthenticationFailed
from api.models import Token, UserInfo
import pytz
import base64
class ExpiringTokenAuthentication(BaseAuthentication):
def authenticate1(self, request):
'''
1 对token设置14天有效时间
2 缓存存储
:param request:
:return:
'''
if request.method == "OPTIONS":
return None
token = request.META.get("HTTP_AUTHORIZATION")
# 1 校验是否存在token字符串
# 1.1 缓存校验
user = cache.get(token)
if user:
print("缓存校验成功")
return user, token
# 1.2 数据库校验
token_obj = Token.objects.filter(key=token).first()
if not token_obj:
raise AuthenticationFailed("认证失败!")
# 2 校验是否在有效期内
now = datetime.datetime.now() # 2018-1-12- 0 0 0
delta = now - token_obj.created
state = delta < datetime.timedelta(weeks=2)
if state:
# 校验成功,写入缓存中
delta = datetime.timedelta(weeks=2) - delta
cache.set(token_obj.key, token_obj.user, min(delta.total_seconds(), 3600 * 24 * 7))
print("数据库校验成功")
return token_obj.user, token_obj.key
else:
raise AuthenticationFailed("认证超时!")
def authenticate(self, request):
if request.method == "OPTIONS":
return None
request_token = request.META.get("HTTP_AUTHORIZATION", request.query_params.get("token", None))
try:
format_token = base64.b64decode(request_token).decode()
auth_token = format_token.split(":")[0]
user_name = format_token.split(":")[1]
except Exception:
raise AuthenticationFailed({"code": 1001, "error": "token不合法"})
if not auth_token:
raise AuthenticationFailed({"code": 1001, "error": "缺少token"})
token_obj = Token.objects.filter(access_token=auth_token).first()
if not token_obj:
raise AuthenticationFailed({"code": 1001, "error": "无效的token"})
if user_name != token_obj.user.username:
raise AuthenticationFailed({"code": 1001, "error": "token校验失败"})
return token_obj.user, token_obj

@ -0,0 +1,47 @@
from django.conf import settings
from api.utils.geetest import GeeTestLib
def verify(verify_data, uid=None, extend_params=None):
"""第三方滑动验证码校验.
选用第三方的验证组件, 根据参数进行校验
根据布尔值辨别是否校验通过
Parameters
----------
verify_data : dict
请求数据
uid: string, default: None
用户UID, 如果存在就免受滑动验证码的限制
extend_params : dict
预留的扩展参数
Returns
-------
True OR False
"""
captcha_config = settings.GEE_TEST
if captcha_config.get("verify_status"):
status = True
if uid in captcha_config.get("not_verify"):
return True
gt = GeeTestLib(captcha_config["gee_test_access_id"], captcha_config["gee_test_access_key"])
challenge = verify_data.get(gt.FN_CHALLENGE, '')
validate = verify_data.get(gt.FN_VALIDATE, '')
seccode = verify_data.get(gt.FN_SECCODE, '')
# status = request.session.get(gt.GT_STATUS_SESSION_KEY, 1)
# user_id = request.session.get("user_id")
if status:
result = gt.success_validate(challenge, validate, seccode, None)
else:
result = gt.failback_validate(challenge, validate, seccode)
return True if result else False
else:
return True

@ -0,0 +1,10 @@
class CommonException(Exception):
def __init__(self,code,msg):
self.error=msg
self.code=code

@ -0,0 +1,23 @@
from api import models
from rest_framework.filters import BaseFilterBackend
class CourseFilter(BaseFilterBackend):
"""
课程展示 过滤器
"""
def filter_queryset(self,request, queryset, view):
extra = {}
print("request.query_params",request.query_params)
category_id = str(request.query_params.get("category_id"))
# 如果分类ID不是数字或分类ID传输的为0
print("category_id",category_id)
if not category_id.isdigit() or category_id == "0":
extra = extra
else:
extra.update({"course_category_id":category_id})
print("extra",extra)
print("queryset.filter(**extra)",queryset.filter(**extra))
return queryset.filter(**extra)

@ -0,0 +1,199 @@
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import sys
import random
import json
import requests
import time
from hashlib import md5
if sys.version_info >= (3,):
xrange = range
VERSION = "3.2.0"
class GeeTestLib(object):
FN_CHALLENGE = "geetest_challenge"
FN_VALIDATE = "geetest_validate"
FN_SECCODE = "geetest_seccode"
GT_STATUS_SESSION_KEY = "gt_server_status"
API_URL = "http://api.geetest.com"
REGISTER_HANDLER = "/register.php"
VALIDATE_HANDLER = "/validate.php"
def __init__(self, captcha_id, private_key):
self.private_key = private_key
self.captcha_id = captcha_id
self.sdk_version = VERSION
self._response_str = ""
def pre_process(self, user_id=None):
"""
验证初始化预处理.
"""
status, challenge = self._register(user_id)
self._response_str = self._make_response_format(status, challenge)
return status
def _register(self, user_id=None):
challenge = self._register_challenge(user_id)
if len(challenge) == 32:
challenge = self._md5_encode("".join([challenge, self.private_key]))
return 1, challenge
else:
return 0, self._make_fail_challenge()
def get_response_str(self):
return self._response_str
def _make_fail_challenge(self):
rnd1 = random.randint(0, 99)
rnd2 = random.randint(0, 99)
md5_str1 = self._md5_encode(str(rnd1))
md5_str2 = self._md5_encode(str(rnd2))
challenge = md5_str1 + md5_str2[0:2]
return challenge
def _make_response_format(self, success=1, challenge=None):
if not challenge:
challenge = self._make_fail_challenge()
string_format = json.dumps(
{'success': success, 'gt': self.captcha_id, 'challenge': challenge})
return string_format
def _register_challenge(self, user_id=None):
if user_id:
register_url = "{api_url}{handler}?gt={captcha_ID}&user_id={user_id}".format(
api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id, user_id=user_id)
else:
register_url = "{api_url}{handler}?gt={captcha_ID}".format(
api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id)
try:
response = requests.get(register_url, timeout=2)
if response.status_code == requests.codes.ok:
res_string = response.text
else:
res_string = ""
except:
res_string = ""
return res_string
def success_validate(self, challenge, validate, seccode, user_id=None, gt=None, data='', userinfo=''):
"""
正常模式的二次验证方式.向geetest server 请求验证结果.
"""
if not self._check_para(challenge, validate, seccode):
return 0
if not self._check_result(challenge, validate):
return 0
validate_url = "{api_url}{handler}".format(
api_url=self.API_URL, handler=self.VALIDATE_HANDLER)
query = {
"seccode": seccode,
"sdk": ''.join(["python_", self.sdk_version]),
"user_id": user_id,
"data": data,
"timestamp": time.time(),
"challenge": challenge,
"userinfo": userinfo,
"captchaid": gt
}
backinfo = self._post_values(validate_url, query)
if backinfo == self._md5_encode(seccode):
return 1
else:
return 0
def _post_values(self, apiserver, data):
response = requests.post(apiserver, data)
return response.text
def _check_result(self, origin, validate):
encodeStr = self._md5_encode(self.private_key + "geetest" + origin)
if validate == encodeStr:
return True
else:
return False
def failback_validate(self, challenge, validate, seccode):
"""
failback模式的二次验证方式.在本地对轨迹进行简单的判断返回验证结果.
"""
if not self._check_para(challenge, validate, seccode):
return 0
validate_str = validate.split('_')
encode_ans = validate_str[0]
encode_fbii = validate_str[1]
encode_igi = validate_str[2]
decode_ans = self._decode_response(challenge, encode_ans)
decode_fbii = self._decode_response(challenge, encode_fbii)
decode_igi = self._decode_response(challenge, encode_igi)
validate_result = self._validate_fail_image(
decode_ans, decode_fbii, decode_igi)
return validate_result
def _check_para(self, challenge, validate, seccode):
return (bool(challenge.strip()) and bool(validate.strip()) and bool(seccode.strip()))
def _validate_fail_image(self, ans, full_bg_index, img_grp_index):
thread = 3
full_bg_name = str(self._md5_encode(str(full_bg_index)))[0:10]
bg_name = str(self._md5_encode(str(img_grp_index)))[10:20]
answer_decode = ""
for i in range(0, 9):
if i % 2 == 0:
answer_decode += full_bg_name[i]
elif i % 2 == 1:
answer_decode += bg_name[i]
x_decode = answer_decode[4:]
x_int = int(x_decode, 16)
result = x_int % 200
if result < 40:
result = 40
if abs(ans - result) < thread:
return 1
else:
return 0
def _md5_encode(self, values):
if type(values) == str:
values = values.encode()
m = md5(values)
return m.hexdigest()
def _decode_rand_base(self, challenge):
str_base = challenge[32:]
i = 0
temp_array = []
for i in xrange(len(str_base)):
temp_char = str_base[i]
temp_ascii = ord(temp_char)
result = temp_ascii - 87 if temp_ascii > 57 else temp_ascii - 48
temp_array.append(result)
decode_res = temp_array[0] * 36 + temp_array[1]
return decode_res
def _decode_response(self, challenge, userresponse):
if len(userresponse) > 100:
return 0
shuzi = (1, 2, 5, 10, 50)
chongfu = set()
key = {}
count = 0
for i in challenge:
if i in chongfu:
continue
else:
value = shuzi[count % 5]
chongfu.add(i)
count += 1
key.update({i: value})
res = 0
for i in userresponse:
res += key.get(i, 0)
res = res - self._decode_rand_base(challenge)
return res

@ -0,0 +1,18 @@
from django.utils.deprecation import MiddlewareMixin
class CorsMiddleWare(MiddlewareMixin):
def process_response(self,request,response):
if request.method=="OPTIONS":
response["Access-Control-Allow-Methods"] = "GET,POST,DELETE,PUT"
response["Access-Control-Allow-Headers"] = "Content-Type,AUTHORIZATION"
response["Access-Control-Allow-Origin"] = request.META.get("HTTP_ORIGIN")
response["Access-Control-Allow-Credentials"] = 'true'
response["Cache-Control"] = "no-cache"
return response

@ -0,0 +1,21 @@
from django.core.cache import cache
import datetime
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from api.models import Token
from rest_framework.permissions import BasePermission
class LoginUserPermission(BaseAuthentication):
def has_permission(self,request,view):
if request.user:
return True
else:
return False

@ -0,0 +1,28 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/6
import uuid
def make_from_user_uuid(userinfo):
user_id = userinfo.uid
random_str = uuid.uuid1().__str__().split("-")[0:-1]
user_ran_str=uuid.uuid5(uuid.NAMESPACE_DNS, user_id).__str__().split("-")
user_ran_str.extend(random_str)
new_str = "".join(user_ran_str)
return new_str
def make_app_uuid(userinfo,bundleid):
user_id = userinfo.uid
app_uuid=uuid.uuid5(uuid.NAMESPACE_DNS, "%s"%(user_id+bundleid)).__str__().split("-")
fapp_uuid = "".join(app_uuid)
return fapp_uuid
def make_random_uuid():
random_str = uuid.uuid1().__str__().split("-")
return "".join(random_str)

@ -0,0 +1,13 @@
class BaseResponse(object):
def __init__(self):
self.code=1000
self.msg=""
self.data=None
@property
def dict(self):
return self.__dict__

@ -0,0 +1,294 @@
from rest_framework import serializers
from api import models
from api.utils.app.analyze import bytes2human,make_download_token
# class CourseCategorySerializer(serializers.ModelSerializer):
# class Meta:
# model = models.Course
# fields = (
# "id",
# "name",
# )
#
# class CourseSerializer(serializers.ModelSerializer):
# level = serializers.CharField(source="get_level_display")
# coursedetail_id=serializers.CharField(source="coursedetail.pk")
# class Meta:
# model = models.Course
# fields = (
# 'id',
# 'name',
# 'course_img',
# 'brief',
# 'level',
# "coursedetail_id"
# )
#
# def to_representation(self, instance):
#
# data = super(CourseSerializer, self).to_representation(instance)
# # 购买人数
# # data["people_buy"] = instance.order_details.all().count()
# # 价格套餐列表
# price_policies = instance.price_policy.all().order_by("price").only("price")
#
# price = getattr(price_policies.first(), "price", 0)
#
# if price_policies and price == 0:
# is_free = True
# price = "免费"
# origin_price = "原价¥{}".format(price_policies.last().price)
# else:
# is_free = False
# price = "¥{}".format(price)
# origin_price = None
#
# # 是否免费
# data["is_free"] = is_free
# # 展示价格
# data["price"] = price
# # 原价
# data["origin_price"] = origin_price
#
# return data
#
#
# class CourseDetailSerializer(serializers.ModelSerializer):
#
# name=serializers.CharField(source="course.name")
# prices = serializers.SerializerMethodField()
# brief = serializers.StringRelatedField(source='course.brief')
# study_all_time = serializers.StringRelatedField(source='hours')
# level = serializers.CharField(source='course.get_level_display')
#
# teachers_info = serializers.SerializerMethodField()
# is_online = serializers.SerializerMethodField()
# recommend_coursesinfo = serializers.SerializerMethodField()
# # learnnumber = serializers.SerializerMethodField()
# # OftenAskedQuestion = serializers.SerializerMethodField()
#
# class Meta:
# model = models.CourseDetail
# fields="__all__"
#
# def get_prices(self, obj):
# return PricePolicySerializer(
# obj.course.price_policy.all(), many=True, context=self.context
# ).data
#
# def get_study_all_time(self, obj):
# return "30小时"
#
#
# def get_recommend_coursesinfo(self, obj):
# courses = RecommendCourseSerializer(obj.recommend_courses.all(), many=True)
# return courses.data
#
# def get_teachers_info(self, obj):
# teachers = TeacherSerializer(obj.teachers.all(), many=True)
# return teachers.data
#
# def get_is_online(self, obj):
# if obj.course.status == 0:
# return True
# elif obj.course.status == 2:
# return False
# else:
# return ''
#
# # def get_learnnumber(self, obj):
# # return obj.course.order_details.all().count()
#
#
# # def get_OftenAskedQuestion(self, obj):
# # question_queryset = models.OftenAskedQuestion.objects.filter(content_type__model='Course',
# # object_id=obj.course.id)
# # serializer = OftenAskedQuestionSerializer(question_queryset, many=True)
# # return serializer.data
# #
#
#
# class RecommendCourseSerializer(serializers.ModelSerializer):
#
# course_id = serializers.CharField(source="pk")
# course_name = serializers.CharField(source="name")
#
# class Meta:
# model = models.Course
# fields = ('course_id', 'course_name',)
#
#
# class PricePolicySerializer(serializers.ModelSerializer):
# valid_period_name = serializers.StringRelatedField(source="get_valid_period_display")
#
# class Meta:
# model = models.PricePolicy
# fields = ('id', 'valid_period', 'valid_period_name', 'price',)
#
#
# class CourseChapterSerializer(serializers.ModelSerializer):
# chapter_name = serializers.SerializerMethodField()
# chapter_symbol = serializers.SerializerMethodField()
#
# class Meta:
# model = models.CourseChapter
# fields = (
# 'id',
# 'chapter_name',
# 'chapter_symbol',
# )
#
# def get_chapter_name(self, instance):
# return '第%s章·%s' % (instance.chapter, instance.name)
#
# def get_chapter_symbol(self, instance):
# return "chapter_%s_%s" % (self.context.get('enrolled_course_id', 1), instance.id)
#
# def to_representation(self, instance):
#
# data = super(CourseChapterSerializer, self).to_representation(instance)
#
# queryset = instance.coursesections.all().order_by("order")
# # 获取章节对应的课时数量
# data["section_of_count"] = queryset.count()
# data["free_trail"] = queryset.filter(free_trail=True).exists()
# data["coursesections"] = SectionSerializer(
# queryset, many=True, read_only=True, context=self.context
# ).data
#
# return data
#
#
# class OftenAskedQuestionSerializer(serializers.ModelSerializer):
# question_tittle = serializers.SerializerMethodField()
# question_answer = serializers.SerializerMethodField()
#
# class Meta:
# model = models.OftenAskedQuestion
# fields = ('question_tittle', 'question_answer')
#
# def get_question_tittle(self, obj):
# return obj.question
#
# def get_question_answer(self, obj):
# return obj.answer
#
#
# class TeacherSerializer(serializers.ModelSerializer):
#
# teacher_id = serializers.CharField(source="pk")
# teacher_name = serializers.CharField(source="name")
# teacher_brief = serializers.CharField(source="brief")
# teacher_image = serializers.CharField(source="image")
#
#
# class Meta:
# model = models.Teacher
# fields = ('teacher_id', 'teacher_name', 'title', 'signature', 'teacher_image', 'teacher_brief')
#
#
class UserInfoSerializer(serializers.ModelSerializer):
# gender = serializers.SerializerMethodField(source="get_gender_display")
# def get_gender(self, obj):
# return obj.gender
class Meta:
model = models.UserInfo
# fields="__all__"
# exclude = ["password","is_active","user_permissions","role",]
fields=["username","uid","qq","mobile","job","email","domain_name","last_login","first_name",'head_img']
head_img = serializers.SerializerMethodField()
def get_head_img(self,obj):
return "/".join([obj.domain_name, obj.head_img])
class AppsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Apps
# depth = 1
exclude = ["user_id", "id"]
has_combo = serializers.SerializerMethodField()
def get_has_combo(self, obj):
if obj.has_combo:
obj.has_combo.has_combo = None
return AppsSerializer(obj.has_combo,context=self.context).data
master_release = serializers.SerializerMethodField()
def get_master_release(self, obj):
master_release_obj = models.AppReleaseInfo.objects.filter(app_id=obj, is_master=True).first()
if self.context.get("release_id", None) and self.context.get("release_id") != "undefined":
master_release_obj = models.AppReleaseInfo.objects.filter(app_id=obj,
release_id=self.context.get("release_id")).first()
if master_release_obj:
icon_url = "/".join([obj.user_id.domain_name, master_release_obj.icon_url])
datainfo = {
"app_version": master_release_obj.app_version,
"icon_url": icon_url,
"build_version": master_release_obj.build_version,
"release_type": master_release_obj.release_type,
"minimum_os_version": master_release_obj.minimum_os_version,
"created_time": master_release_obj.created_time,
"binary_size": bytes2human(master_release_obj.binary_size),
"release_id": master_release_obj.release_id,
"changelog": master_release_obj.changelog,
"binary_url":master_release_obj.binary_url,
}
if self.context.get("download_token", None) and self.context.get("download_token") != "download_token":
download_url = "/".join([obj.user_id.domain_name, "download", master_release_obj.release_id])
download_url = download_url + "?token=" + self.context.get("download_token")
datainfo["download_url"] = download_url
return datainfo
else:
return {}
class AppReleaseSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppReleaseInfo
fields = ["app_version", "icon_url", "build_version",
"release_type", "minimum_os_version",
"created_time", "binary_size", "release_id", "size", "type", "editing", "master_color", "changelog",
"is_master",'download_url','binary_url']
download_url = serializers.SerializerMethodField()
size = serializers.SerializerMethodField()
type = serializers.SerializerMethodField()
editing = serializers.SerializerMethodField()
master_color = serializers.SerializerMethodField()
icon_url = serializers.SerializerMethodField()
binary_size= serializers.SerializerMethodField()
def get_binary_size(self,obj):
return bytes2human(obj.binary_size)
def get_download_url(self,obj):
download_url = "/".join([obj.app_id.user_id.domain_name, "download", obj.release_id])
download_url = download_url + "?token=" + make_download_token(obj.release_id,300)
return download_url
def get_icon_url(self, obj):
return "/".join([obj.app_id.user_id.domain_name, obj.icon_url])
def get_master_color(self, obj):
if obj.is_master:
return '#0bbd87'
def get_size(self, obj):
return "large"
def get_type(self, obj):
return "primary"
def get_editing(self, obj):
return {"changelog":False,"binary_url":False}

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

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

Loading…
Cancel
Save