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