commit
e26ae1da18
@ -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> |
||||||
|
After Width: | Height: | Size: 29 KiB |
|
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"; |
||||||
|
} |
||||||
|
|
||||||
Binary file not shown.
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 |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
|
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"> {{ 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"> {{ appinfos.bundle_id }}</b></span> |
||||||
|
<span class="version ng-scope">{{ master_release.minimum_os_version }} 或者高版本</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"> {{app.created_time|formattimeline}}</span> |
||||||
|
</small> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="release-metainfo" v-else> |
||||||
|
<small> |
||||||
|
<i class="icon-calendar"></i> |
||||||
|
<span class="el-icon-date"> {{app.created_time|formattimeline}}</span> |
||||||
|
</small> · |
||||||
|
|
||||||
|
<small>{{app.release_type|getiOStype}}</small> |
||||||
|
|
||||||
|
<!-- <i--> |
||||||
|
<!-- v-if="app.changelog" class="ng-hide"> · </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格式: {"show_url":"https://或http://打头的商品的展示地址"} |
||||||
|
在支付时,可点击商品名称跳转到该地址 |
||||||
|
|
||||||
|
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格式: {"show_url":"https://或http://打头的商品的展示地址"} |
||||||
|
在支付时,可点击商品名称跳转到该地址 |
||||||
|
|
||||||
|
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个汉字)。当付款方为企业账户,且转账金额达到(大于等于)50000元,remark不能为空。 |
||||||
|
收款方可见,会展示在收款用户的收支详情中。 |
||||||
|
|
||||||
|
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格式: {"show_url":"https://或http://打头的商品的展示地址"} |
||||||
|
在支付时,可点击商品名称跳转到该地址 |
||||||
|
|
||||||
|
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(原画)2K(2K)4K(4K) |
||||||
|
默认获取所有清晰度的流。 |
||||||
|
|
||||||
|
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…
Reference in new issue