Browse Source

feat(web-ui): append web ui

master
louisyoungx 3 years ago
parent
commit
3294231002
  1. 16
      Config/config.ini
  2. 3
      Core/core.py
  3. 10
      Core/login.py
  4. 38
      Core/spider.py
  5. 26
      Core/timer.py
  6. 6
      Core/util.py
  7. 6
      Message/message.py
  8. 68
      Server/api.py
  9. 42
      Server/app.py
  10. 25
      Server/handler.py
  11. 5
      Server/url.py
  12. 267
      Static/404.html
  13. 36
      Static/css/index.css
  14. BIN
      Static/img/qr_code.png
  15. 250
      Static/index.html
  16. 10
      Static/js/element_index.js
  17. 200
      Static/js/index.js
  18. 4
      Static/js/vue.js
  19. BIN
      cookies/Louis_Young.cookies

16
Config/config.ini

@ -3,21 +3,21 @@ @@ -3,21 +3,21 @@
eid = "2PBCMB2WINZUQI6XRDCSKOZXTCHEOHLDABNAVSAEYTS6DBH2DWOACQPUEKXGHV7ZYTKCRDXZX34SPL2XI3KQYVNVSM"
fp = "63a2c6fceb97a082753cdf00710f4f46"
# area到area_id目录下人工查找,例如:江西省南昌市昌北区蛟桥镇的area是21_1827_4101_40925
area = 21_1827_4101_40925
area = "21_1827_4101_40925"
# 商品id
sku_id = 10027576824361
sku_id = "10027576824361"
# 购买数量
amount = 1
# 设定时间 # 2020-12-09 10:00:00.100000(修改成每天的几点几分几秒几毫秒)
buy_time = 2021-11-13 04:30:00.000
buy_time = "2021-11-13 04:30:00.000"
# 提交订单失败重试次数
retry = 100
# 查询库存请求超时(秒),可选配置,默认10秒
timeout = 10
# 默认UA
DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
# 是否使用随机 useragent,默认为 false
random_useragent = false
# 是否使用随机 useragent,默认为 False
random_useragent = False
# 多进程数量
work_count = 1
@ -33,8 +33,8 @@ payment_pwd = "" @@ -33,8 +33,8 @@ payment_pwd = ""
# 使用了Server酱的推送服务
# 如果想开启下单成功后消息推送,则将 enable 设置为 true,默认为 false 不开启推送
# 开启消息推送必须填入 sckey,如何获取请参考 http://sc.ftqq.com/3.version。感谢Server酱~
enable = false
sckey = ???????
enable = False
sckey = "?????"
@ -86,7 +86,7 @@ SERVER_VERSION = "0.4.8" @@ -86,7 +86,7 @@ SERVER_VERSION = "0.4.8"
START_USING = True
SERVER_HOST = "www.server.com"
LOCAL_HOST = "0.0.0.0"
PORT = 12020
PORT = 12021
PROCESS_MODEL = False
PROCESS_COUNT = 4

3
Core/core.py

@ -1,3 +1,2 @@ @@ -1,3 +1,2 @@
def main():
# 此处填写业务
print("===== 此处业务处理 =====")
pass

10
Core/login.py

@ -5,10 +5,10 @@ import functools @@ -5,10 +5,10 @@ import functools
import json
import os
import pickle
from logger import logger
from config import global_config
from exception import SKException
from util import (
from Logger.logger import logger
from Config.settings import config
from Core.exception import SKException
from Core.util import (
parse_json,
wait_some_time,
response_status,
@ -21,7 +21,7 @@ class SpiderSession: @@ -21,7 +21,7 @@ class SpiderSession:
"""
def __init__(self):
self.cookies_dir_path = "./cookies/"
self.user_agent = global_config.getRaw('config', 'DEFAULT_USER_AGENT')
self.user_agent = config.settings('Spider', 'DEFAULT_USER_AGENT')
self.session = self._init_session()

38
Core/spider.py

@ -7,30 +7,31 @@ import json @@ -7,30 +7,31 @@ import json
import random
import sys
from concurrent.futures import ProcessPoolExecutor
from exception import SKException
from Core.exception import SKException
from bs4 import BeautifulSoup
from Config.settings import config
from Logger.logger import logger
from login import SpiderSession, QrLogin
from Core.login import SpiderSession, QrLogin
from Message.message import sendMessage
from timer import Timer
from util import parse_json
from Core.timer import Timer
from Core.util import parse_json
class Waiter():
def __init__(self,
skuids=config.global_config.getRaw("Spider", "sku_id"),
area=config.global_config.getRaw("Spider", "area"),
eid=config.global_config.getRaw("Spider", "eid"),
fp=config.global_config.getRaw("Spider", "fp"),
count=config.global_config.getRaw("Spider", "amount"),
payment_pwd=config.global_config.getRaw(
skuids=config.settings("Spider", "sku_id"),
area=config.settings("Spider", "area"),
eid=config.settings("Spider", "eid"),
fp=config.settings("Spider", "fp"),
count=config.settings("Spider", "amount"),
payment_pwd=config.settings(
"Account", "payment_pwd"),
retry=eval(config.global_config.getRaw("Spider", "retry")),
work_count=eval(config.global_config.getRaw(
'Spider', 'work_count')),
timeout=float(config.global_config.getRaw(
"Spider", "timeout"))
retry=config.settings("Spider", "retry"),
work_count=config.settings(
'Spider', 'work_count'),
timeout=float(config.raw(
"Spider", "timeout")),
date=config.settings('Spider', 'buy_time').__str__()
):
self.skuids = skuids
@ -42,6 +43,7 @@ class Waiter(): @@ -42,6 +43,7 @@ class Waiter():
self.retry = retry
self.work_count = work_count
self.timeout = timeout
self.buyTime = date
self.spider_session = SpiderSession()
self.spider_session.load_cookies_from_local()
@ -51,7 +53,7 @@ class Waiter(): @@ -51,7 +53,7 @@ class Waiter():
self.item_cat = {}
self.item_vender_ids = {} # 记录商家id
self.headers = {'User-Agent': self.user_agent}
self.timers = Timer()
self.timers = Timer(self.buyTime)
def login_by_qrcode(self):
"""
@ -82,9 +84,11 @@ class Waiter(): @@ -82,9 +84,11 @@ class Waiter():
return func(self, *args, **kwargs)
return new_func
@ check_login
def waitForSell(self):
self._waitForSell()
@ check_login
def waitTimeForSell(self):
self._waitTimeForSell()

26
Core/timer.py

@ -9,10 +9,10 @@ from Config.settings import config @@ -9,10 +9,10 @@ from Config.settings import config
class Timer(object):
def __init__(self, sleep_interval=0.5):
def __init__(self, buyTime, sleep_interval=0.5):
# '2018-09-28 22:45:50.000'
# buy_time = 2020-12-22 09:59:59.500
buy_time_everyday = config.global_config.getRaw('config', 'buy_time').__str__()
buy_time_everyday = buyTime
localtime = time.localtime(time.time())
#self.buy_time = datetime.strptime(
# localtime.tm_year.__str__() + '-' + localtime.tm_mon.__str__() + '-' + localtime.tm_mday.__str__()
@ -52,15 +52,13 @@ class Timer(object): @@ -52,15 +52,13 @@ class Timer(object):
def start(self):
logger.info('正在等待到达设定时间:{}'.format(self.buy_time))
logger.info('正检测本地时间与京东服务器时间误差为【{}】毫秒'.format(self.diff_time))
mode = config.global_config.getMode()
if mode != '3':
while True:
# 本地时间减去与京东的时间差,能够将时间误差提升到0.1秒附近
# 具体精度依赖获取京东服务器时间的网络时间损耗
if self.local_time() - self.diff_time >= self.buy_time_ms:
logger.info('时间到达,开始执行……')
break
else:
time.sleep(self.sleep_interval)
else:
pass
while True:
# 本地时间减去与京东的时间差,能够将时间误差提升到0.1秒附近
# 具体精度依赖获取京东服务器时间的网络时间损耗
if self.local_time() - self.diff_time >= self.buy_time_ms:
logger.info('时间到达,开始执行……')
break
else:
time.sleep(self.sleep_interval)

6
Core/util.py

@ -79,13 +79,13 @@ def wait_some_time(): @@ -79,13 +79,13 @@ def wait_some_time():
def send_wechat(message):
"""推送信息到微信"""
url = 'http://sc.ftqq.com/{}.send'.format(config.global_config.getRaw('messenger', 'sckey'))
url = 'http://sc.ftqq.com/{}.send'.format(config.settings('messenger', 'sckey'))
payload = {
"text":'抢购结果',
"desp": message
}
headers = {
'User-Agent':config.global_config.getRaw('config', 'DEFAULT_USER_AGENT')
'User-Agent':config.settings('config', 'DEFAULT_USER_AGENT')
}
requests.get(url, params=payload, headers=headers)
@ -111,6 +111,6 @@ def open_image(image_file): @@ -111,6 +111,6 @@ def open_image(image_file):
def save_image(resp, image_file):
with open(image_file, 'wb') as f:
with open(config.path() + '/Static/img/' + image_file, 'wb') as f:
for chunk in resp.iter_content(chunk_size=1024):
f.write(chunk)

6
Message/message.py

@ -5,8 +5,8 @@ import json @@ -5,8 +5,8 @@ import json
import requests
from logger import logger
from config import global_config
from Logger.logger import logger
from Config.settings import config
class Messenger(object):
@ -41,7 +41,7 @@ class Messenger(object): @@ -41,7 +41,7 @@ class Messenger(object):
logger.error('Fail to send message [text: %s, desp: %s]: %s', text, desp, e)
sckey = global_config.getRaw("messenger", "sckey")
sckey = config.settings("Message", "sckey")
message = Messenger(sckey)

68
Server/api.py

@ -1,28 +1,25 @@ @@ -1,28 +1,25 @@
import copy
import psutil
from Config.settings import config
from Core.spider import Waiter
from threading import Thread
class Global(object):
def __init__(self):
self.waiter = None
self.login = None
self.thread= None
def update(self):
self.login = self.waiter.qrlogin.is_login
glo = Global()
def log(request):
file_path = config.path() + config.settings("Logger", "FILE_PATH") + config.settings("Logger", "FILE_NAME")
file_path = config.path() + config.settings("Logger", "FILE_PATH") + \
config.settings("Logger", "FILE_NAME")
file_page_file = open(file_path, 'r')
return str(file_page_file.read())
def systemInfo(request):
info = {}
info['cpu_count'] = psutil.cpu_count() # CPU逻辑数量
info['cpu_percent'] = psutil.cpu_percent(interval=1) # CPU使用率
info['cpu_times'] = psutil.cpu_times() # CPU的用户/系统/空闲时间
info['virtual_memory'] = psutil.virtual_memory() # 物理内存
info['swap_memory'] = psutil.swap_memory() # 交换内存
info['disk_partitions'] = psutil.disk_partitions() # 磁盘分区信息
info['disk_partitions'] = psutil.disk_usage('/') # 磁盘使用情况
info['disk_partitions'] = psutil.disk_io_counters() # 磁盘IO
info['disk_partitions'] = psutil.net_io_counters() # 获取网络读写字节/包的个数
info['disk_partitions'] = psutil.net_if_addrs() # 获取网络接口信息
info['disk_partitions'] = psutil.net_if_stats() # 获取网络接口状态
# need sudo: info['disk_partitions'] = psutil.net_connections() # 获取当前网络连接信息
return info
def serverConfig(request):
appConfig = copy.deepcopy(config._config._sections)
@ -32,3 +29,38 @@ def serverConfig(request): @@ -32,3 +29,38 @@ def serverConfig(request):
value = appConfig[model][item]
# DEBUG print(model, item, value, type(value))
return appConfig
def jdShopper(request):
mode = request['mode']
date = request['date']
skuids = request['skuid']
area = request['area']
eid = request['eid']
fp = request['fp']
count = request['count']
retry = request['retry']
work_count = request['work_count']
timeout = request['timeout']
if mode == '1':
glo.waiter = Waiter(skuids=skuids, area=area, eid=eid, fp=fp, count=count,
retry=retry, work_count=work_count, timeout=timeout)
glo.thread = Thread(target=glo.waiter.waitForSell)
glo.thread.start()
elif mode == '2':
date = date.replace("T", " ")
date = date.replace("Z", "")
glo.waiter = Waiter(skuids=skuids, area=area, eid=eid, fp=fp, count=count,
retry=retry, work_count=work_count, timeout=timeout, date=date)
glo.thread = Thread(target=glo.waiter.waitTimeForSell)
glo.thread.start()
glo.update()
print(glo.login)
return glo.login
def loginStatus(request):
try:
glo.update()
except:
pass
return glo.login

42
Server/app.py

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
import subprocess
import sys
import os
from flask import request, Flask, make_response, render_template
HOME = os.path.dirname(os.path.abspath(__file__))
app = Flask(__name__,
instance_path=HOME,
instance_relative_config=True,
template_folder="../Static",
static_folder="../Static",
static_url_path='')
app.config['JSON_AS_ASCII'] = False
@app.route("/")
def homePage():
return render_template('index.html')
@app.route('/api/zpy', methods=['POST', 'OPTIONS'])
def zpy():
if request.method == 'POST':
code = request.json['code']
output = subprocess.check_output([sys.executable, '-c', code])
print(output.decode().encode())
response = make_response(output.decode())
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET,POST'
response.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type'
return response
else:
# CORS跨域配置
response = make_response()
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET,POST'
response.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type'
return response
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0', port=5000)

25
Server/handler.py

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
import os, json
import time
import os, json, urllib, time
from Logger.logger import logger
from http.server import BaseHTTPRequestHandler
@ -16,6 +15,7 @@ class RequestHandler(BaseHTTPRequestHandler): @@ -16,6 +15,7 @@ class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.rootPath = config.path() + "/Static"
url = self.requestline[4:-9]
print(url)
request_data = {} # 存放GET请求数据
try:
if url.find('?') != -1:
@ -25,15 +25,35 @@ class RequestHandler(BaseHTTPRequestHandler): @@ -25,15 +25,35 @@ class RequestHandler(BaseHTTPRequestHandler):
for i in parameters:
key, val = i.split('=', 1)
request_data[key] = val
#request_data['body'] = self.rfile.read()
except:
logger.error("URL Format Error")
if (url == "/"):
self.home()
elif (url == ""):
self.noFound()
elif ("/api" in url):
self.api(url[4:], request_data)
else:
self.file(url)
def do_POST(self):
LOCAL_HOST = config.settings("Server", "LOCAL_HOST")
PORT = config.settings("Server", "PORT")
hostLen = len(f'/{LOCAL_HOST}:{PORT}') + 5
self.rootPath = config.path() + "/Static"
url = self.requestline[hostLen:-9]
request_data = json.loads(self.rfile.read(int(self.headers['content-length'])).decode())
if (url == "/"):
self.home()
elif (url == ""):
self.noFound()
elif ("/api" in url):
self.api(url[4:], request_data)
else:
self.file(url)
def log_message(self, format, *args):
SERVER_LOGGER = config.settings("Logger", "SERVER_LOGGER")
if SERVER_LOGGER:
@ -103,7 +123,6 @@ class RequestHandler(BaseHTTPRequestHandler): @@ -103,7 +123,6 @@ class RequestHandler(BaseHTTPRequestHandler):
# ----------------------------------------------------------------
# 此处写API
content = urls(url, request_data)
# ----------------------------------------------------------------
localtime = time.localtime(time.time())
date = \

5
Server/url.py

@ -1,7 +1,8 @@ @@ -1,7 +1,8 @@
from Server.api import log, systemInfo, serverConfig
from Server.api import log, serverConfig, jdShopper, loginStatus
def urls(url, request):
if (url == "/log"): return log(request)
elif (url == "/system-info"): return systemInfo(request)
elif (url == "/config"): return serverConfig(request)
elif (url == "/jd-shopper"): return jdShopper(request)
elif (url == "/jd-login-status"): return loginStatus(request)
else: return "No Response"

267
Static/404.html

@ -0,0 +1,267 @@ @@ -0,0 +1,267 @@
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>404 No Found - Louis Young</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<style>
html, body {
height: 100%;
overflow: hidden;
}
.error-page {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
height: 100%;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
}
.error-page h1 {
font-size: 30vh;
font-weight: bold;
position: relative;
margin: -8vh 0 0;
padding: 0;
}
.error-page h1:after {
content: attr(data-h1);
position: absolute;
top: 0;
left: 0;
right: 0;
color: transparent;
/* webkit only for graceful degradation to IE */
background: -webkit-repeating-linear-gradient(-45deg, #71b7e6, #69a6ce, #b98acc, #ee8176, #b98acc, #69a6ce, #9b59b6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 400%;
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.25);
animation: animateTextBackground 10s ease-in-out infinite;
}
.error-page h1 + p {
color: #d6d6d6;
font-size: 8vh;
font-weight: bold;
line-height: 10vh;
max-width: 600px;
position: relative;
}
.error-page h1 + p:after {
content: attr(data-p);
position: absolute;
top: 0;
left: 0;
right: 0;
color: transparent;
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.5);
-webkit-background-clip: text;
-moz-background-clip: text;
background-clip: text;
}
#particles-js {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
@keyframes animateTextBackground {
0% {
background-position: 0 0;
}
25% {
background-position: 100% 0;
}
50% {
background-position: 100% 100%;
}
75% {
background-position: 0 100%;
}
100% {
background-position: 0 0;
}
}
@media (max-width: 767px) {
.error-page h1 {
font-size: 32vw;
}
.error-page h1 + p {
font-size: 8vw;
line-height: 10vw;
max-width: 70vw;
}
}
a.back {
position: fixed;
right: 40px;
bottom: 40px;
background: -webkit-repeating-linear-gradient(-45deg, #71b7e6, #69a6ce, #b98acc, #ee8176);
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
color: #fff;
font-size: 16px;
font-weight: bold;
line-height: 24px;
padding: 15px 30px;
text-decoration: none;
transition: 0.25s all ease-in-out;
}
a.back:hover {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
}
</style>
<script>
window.console = window.console || function (t) {
};
</script>
<script>
if (document.location.search.match(/type=embed/gi)) {
window.parent.postMessage("resize", "*");
}
</script>
</head>
<body translate="no">
<div class="error-page">
<div>
<!--h1(data-h1='400') 400-->
<!--p(data-p='BAD REQUEST') BAD REQUEST-->
<!--h1(data-h1='401') 401-->
<!--p(data-p='UNAUTHORIZED') UNAUTHORIZED-->
<!--h1(data-h1='403') 403-->
<!--p(data-p='FORBIDDEN') FORBIDDEN-->
<h1 data-h1="404">404</h1>
<p data-p="NOT FOUND">NOT FOUND</p>
<!--h1(data-h1='500') 500-->
<!--p(data-p='SERVER ERROR') SERVER ERROR-->
</div>
</div>
<div id="particles-js">
<canvas class="particles-js-canvas-el" width="1090" height="1330" style="width: 100%; height: 100%;"></canvas>
</div>
<!--a(href="#").back GO BACK-->
<script src="https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-8216c69d01441f36c0ea791ae2d4469f0f8ff5326f00ae2d00e4bb7d20e24edb.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/particles.js/2.0.0/particles.min.js"></script>
<script id="rendered-js">
particlesJS("particles-js", {
"particles": {
"number": {
"value": 5,
"density": {
"enable": true,
"value_area": 800
}
},
"color": {
"value": "#fcfcfc"
},
"shape": {
"type": "circle"
},
"opacity": {
"value": 0.5,
"random": true,
"anim": {
"enable": false,
"speed": 1,
"opacity_min": 0.2,
"sync": false
}
},
"size": {
"value": 140,
"random": false,
"anim": {
"enable": true,
"speed": 10,
"size_min": 40,
"sync": false
}
},
"line_linked": {
"enable": false
},
"move": {
"enable": true,
"speed": 8,
"direction": "none",
"random": false,
"straight": false,
"out_mode": "out",
"bounce": false,
"attract": {
"enable": false,
"rotateX": 600,
"rotateY": 1200
}
}
},
"interactivity": {
"detect_on": "canvas",
"events": {
"onhover": {
"enable": false
},
"onclick": {
"enable": false
},
"resize": true
}
},
"retina_detect": true
});
//# sourceURL=pen.js
</script>
</body>
</html>
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->

36
Static/css/index.css

@ -1,16 +1,35 @@ @@ -1,16 +1,35 @@
.data_dict, .data_dict_goods{
display: none;
body {
display: flex;
justify-content: center;
align-self: center;
height: 100vh;
width: 100vw;
}
#form_container{
width: 600px;
margin: 0 auto;
width: 95vw;
max-width: 600px;
border: 1px solid gainsboro;
height: auto;
display: flex;
flex-direction: column;
justify-content: center;
align-self: center;
}
h1{
text-align: center;
margin: 30px 0;
padding-bottom: 20px;
}
#log {
resize: none;
height: 50vh;
border: none;
font-size: 16px;
outline: none;
background-color: rgba(245, 245, 245);
white-space: pre-wrap;
padding: 2vh;
}
#account_input, #password_input{
width: 300px;
}
@ -30,4 +49,13 @@ h1{ @@ -30,4 +49,13 @@ h1{
}
#run_button{
margin-left: 30px;
}
.el-dialog__body {
width: 250px;
height: 250px;
}
.el-image__inner {
width: 250px;
height: 250px;
}

BIN
Static/img/qr_code.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

250
Static/index.html

@ -3,178 +3,126 @@ @@ -3,178 +3,126 @@
<head>
<meta charset="UTF-8" />
<title>订单自动提交</title>
<link rel="Shortcut Icon" href="title.ico" type="image/x-icon" />
<link type="text/css" rel="stylesheet" href="css/element_index.css" />
<link type="text/css" rel="stylesheet" href="css/index.css" />
<link rel="Shortcut Icon" href="title.ico" type="./image/x-icon" />
<link type="text/css" rel="stylesheet" href="./css/element_index.css" />
<link type="text/css" rel="stylesheet" href="./css/index.css" />
</head>
<body>
<div id="form_container">
<h1>订单自动提交</h1>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="登录方式">
<el-radio v-model="radio_login" label="1" @change="LoginMode" border
>自动登录</el-radio
<textarea v-show="!task" id="log" onpropertychange="this.scrollTop=this.scrollHeight" οnfοcus="this.scrollTop=this.scrollHeight" ></textarea>
<el-form v-show="task" ref="form" label-width="80px">
<el-form-item label="下单模式">
<el-radio v-model="mode" label="1" @change="buyMode" border
>有货自动下单</el-radio
>
<el-radio v-model="radio_login" label="2" @change="LoginMode" border
>手动登录</el-radio
<el-radio v-model="mode" label="2" @change="buyMode" border
>定时下单</el-radio
>
</el-form-item>
<el-form-item label="账号">
</el-form-item>
<el-form-item label="运行时间">
<el-date-picker
v-model="date"
type="datetime"
:disabled="timeSelectAble"
placeholder="选择日期时间">
</el-date-picker>
</el-form-item>
<el-form-item label="地区ID">
<el-input
id="account_input"
v-model="account_value"
placeholder="请输入账号"
:disabled="input_writeable"
v-model="area"
placeholder="所属地区ID"
></el-input>
</el-form-item>
<el-form-item label="密码" id="password_form">
<el-form-item label="商品" id="goods_link_form">
<el-input
id="password_input"
v-model="password_value"
placeholder="请输入密码"
:disabled="input_writeable"
show-password
class="goods_input"
v-model="skurl"
placeholder="填入商品链接"
></el-input>
<el-input-number
class="num_select"
v-model="count"
:min="1"
:max="999"
label="购买数量"
></el-input-number>
</el-form-item>
<el-form-item label="运行时间">
<el-select v-model="date_value" placeholder="选择日期(默认今天)">
<el-option
v-for="item in date_options"
:key="item.date_value"
:label="item.label"
:value="item.date_value"
>
</el-option>
</el-select>
<el-time-picker
v-model="time_value"
:picker-options="{}"
placeholder="选择时间(默认现在)"
>
</el-time-picker>
<el-form-item label="超时时间">
<el-input-number
v-model="timeout"
:min="1"
:max="1000"
label="超时时间"
></el-input-number>
</el-form-item>
<el-form-item label="重试次数">
<el-input-number
v-model="retry"
:min="1"
:max="1000"
label="重试次数"
></el-input-number>
</el-form-item>
<el-form-item label="线程数">
<el-input-number
v-model="work_count"
:min="1"
:max="3"
label="线程数"
></el-input-number>
</el-form-item>
<goods_link></goods_link>
<div v-for="(d,index) in goods_link_counter" :key="index">
<goods_link></goods_link>
</div>
<el-button id="add_button" @click="addGoods">继续添加</el-button>
<el-button id="add_button" @click="reset()">重置选项</el-button>
<el-button
id="run_button"
@click="changeRunStatus"
@click="upload()"
type="primary"
:class="run_status"
>开始运行</el-button
>
<el-dialog
title="二维码"
:visible.sync="qrVisible"
width="30%">
<el-image v-if="qrReset" :src="qrUrl" :key="qrID"></el-image>
<span slot="footer" class="dialog-footer">
</span>
</el-dialog>
<el-dialog
title="title"
:visible.sync="dialogVisible"
width="30%">
<span>{{dialog}}</span>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</span>
</el-dialog>
</el-form>
<ul class="data_dict">
<!--登录方式, 账号, 密码, 运行时间, url-->
<li class="is_manual">{{is_manual}}</li>
<li class="jd_conut">{{account_value}}</li>
<li class="jd_password">{{password_value}}</li>
<li class="run_date">{{date_value}}</li>
<li class="run_time">{{time_value}}</li>
</ul>
</div>
<template id="goods_link">
<el-form-item label="添加商品" id="goods_link_form">
<el-input
class="goods_input"
v-model="goods_url"
placeholder="商品链接"
></el-input>
<el-input-number
class="num_select"
v-model="num"
@change="handleChange"
:min="1"
:max="999"
label="数量"
></el-input-number>
<ul class="data_dict_goods">
<li class="goods_url">{{goods_url}}</li>
<li class="goods_num">{{num}}</li>
</ul>
</el-form-item>
</template>
</body>
<script src="js/vue.js"></script>
<script src="js/element_index.js"></script>
<script>
Vue.component("goods_link", {
template: "#goods_link",
data: function () {
return {
num: 1,
goods_url: "",
};
},
// props:{
// num:{
// type: Number,
// default: 1
// },
// goods_url: {
// type: String,
// default: ''
// }
// },
methods: {
handleChange(value) {},
},
});
new Vue({
el: "#form_container",
data: function () {
return {
account_value: "",
password_value: "",
radio_login: "1",
is_manual: 0,
input_writeable: false,
run_status: "wait",
goods_link_counter: [],
date_value: "",
date_options: [
{
date_value: "today",
label: "今天",
},
{
date_value: "tomorrow",
label: "明天",
},
],
// time_value: new Date(2016, 9, 10, 18, 40),
time_value: "",
form: {
name: "",
region: "",
},
};
},
methods: {
LoginMode: function (value) {
if (this.radio_login === "1" || this.radio_login === 1) {
this.is_manual = 0;
this.input_writeable = false;
} else {
this.is_manual = 1;
this.input_writeable = true;
}
},
addGoods: function () {
this.goods_link_counter.push({});
},
changeRunStatus: function () {
if (!this.date_value) {
this.date_value = "today";
}
if (!this.time_value) {
this.time_value = new Date();
}
this.run_status = "trigger_run";
},
},
});
</script>
<script src="./js/vue.js"></script>
<script src="./js/element_index.js"></script>
<script src="https://gias.jd.com/js/td.js"></script>
<script src="./js/index.js"></script>
</html>
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->
<!-- // -->

10
Static/js/element_index.js

File diff suppressed because one or more lines are too long

200
Static/js/index.js

@ -0,0 +1,200 @@ @@ -0,0 +1,200 @@
new Vue({
el: "#form_container",
data() {
return {
mode: "1", // 模式:定时下单/有货自动下单
date: "",
area: "", // 所在地区
skurl: "", // 商品url
count: "1", // 购买数量
retry: "10", // 重试次数
work_count: "1", // 启动线程数
timeout: "3", // 超时时间
eid: "",
fp: "",
timeSelectAble: true,
dialogVisible: false,
dialog: "",
skuid: "",
qrUrl: "./img/qr_code.png",
qrVisible: false,
qrReq: undefined,
qrID: 0,
qrReset: true,
title: "错误",
task: true
};
},
mounted() {
this.getEidFp()
setTimeout(() => {
this.main()
}, 100)
},
methods: {
main() {
},
upload() {
if (!this.checkValid()) return
let url = "0.0.0.0:12021/api/jd-shopper"
let data = {
mode: this.mode,
date: this.date,
area: this.area,
skuid: this.skuid,
count: this.count,
retry: this.retry,
work_count: this.work_count,
timeout: this.timeout,
eid: this.eid,
fp: this.fp,
};
fetch(url, {
body: JSON.stringify(data), // must match 'Content-Type' header
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, *omit
headers: {
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json'
},
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
}).then(response => {
return response.json()
}).then(res => {
console.log(res)
setTimeout(() => {
this.qrShow()
this.loginCheck()
}, 200)
})
},
buyMode(value) {
if (this.mode === "1" || this.mode === 1) {
this.timeSelectAble = true;
} else {
this.timeSelectAble = false;
}
},
getEidFp() {
let that = this
setTimeout(() => {
try {
getJdEid(function (eid, fp, udfp) {
that.eid = eid
that.fp = fp
});
} catch (e) {
that.dialogShow("获取eid与fp失败,请手动获取。")
}
}, 0);
},
reset() {
this.mode = "1" // 模式:定时下单/有货自动下单
this.date = ""
this.area = "" // 所在地区
this.skurl = "" // 商品url
this.count = "1" // 购买数量
this.retry = "10" // 重试次数
this.work_count = "1" // 启动线程数
this.timeout = "3" // 超时时间
this.eid = ""
this.fp = ""
},
dialogShow(mes) {
this.dialog = mes
this.dialogVisible = true
},
checkValid() {
if (this.area == "" || this.skurl == "") {
this.dialogShow("地区ID与商品链接不能为空")
return false
}
else if (this.mode == "2" && this.date == "") {
this.dialogShow("定时下单需设置时间")
return false
}
let skuid = this.skurl.match(new RegExp(`https://item.jd.com/(.*?).html`))
skuid = skuid ? skuid[1] : null
if (skuid == null) {
skuid= this.skurl.replace(/[^0-9]/ig,"")
reNum = /^[0-9]+.?[0-9]*/
if (!reNum.test(skuid)) {
this.dialogShow("请输入正确的网址")
return false
}
}
this.skuid = skuid
return true
},
qrShow() {
this.qrVisible = true
this.qrID = 0
this.qrReq = setInterval(function () {
let imgDiv = document.getElementsByClassName("el-image")[0]
imgDiv.removeChild(imgDiv.childNodes[0])
this.qrID++
this.qrUrl = './img/qr_code.png'
this.qrReset = false
}, 3000)
},
loginCheck() {
let url = './api/jd-login-status'
let loginReq = setInterval(() => {
let imgDiv = document.getElementsByClassName("el-image")[0]
imgDiv.innerHTML = `<img src="./img/qr_code.png?id={this.qrID}" class="el-image__inner">`
fetch(url)
.then(response => {
return response.json();
})
.then(res => {
console.log(res);
if (res.data) {
this.qrVisible = false
clearInterval(this.qrReq)
clearInterval(loginReq)
this.getLog()
this.task = false
}
});
}, 1000)
},
getLog() {
let url = './api/log'
fetch(url)
.then(response => {
return response.json();
})
.then(res => {
console.log(res.data);
document.getElementById('log').innerHTML = res.data
});
setInterval(() => {
fetch(url)
.then(response => {
return response.json();
})
.then(res => {
console.log(res.data);
document.getElementById('log').innerHTML = res.data
});
}, 10000)
}
},
});
// confirm,e.prototype.$prompt=ya.prompt,e.prototype.$notify=tl,e.prototype.$message=ou};
//"undefined"!=typeof window&&window.Vue&&Ld(window.Vue);t.default={version:"2.15.0",
//locale:j.use,i18n:j.i18n,install:Ld,CollapseTransition:ii,Loading:_l,Pagination:pt,
//Dialog:gt,Autocomplete:kt,Dropdown:At,DropdownMenu:Bt,DropdownItem:Wt,Menu:ei,
//Submenu:ai,MenuItem:di,MenuItemGroup:vi,Input:ne,InputNumber:_i,Radio:Si,RadioGroup:Mi,
//RadioButton:Ii,Checkbox:Vi,CheckboxButton:Ri,CheckboxGroup:Yi,Switch:Xi,Select:ct,
//Option:ht,OptionGroup:en,Button:Et,ButtonGroup:Pt,Table:Un,TableColumn:ir

4
Static/js/vue.js

@ -11963,3 +11963,7 @@ @@ -11963,3 +11963,7 @@
return Vue;
}));
// confirm,e.prototype.$prompt=ya.prompt,e.prototype.$notify=tl,e.prototype.$message=ou};
//"undefined"!=typeof window&&window.Vue&&Ld(window.Vue);t.default={version:"2.15.0",

BIN
cookies/Louis_Young.cookies

Binary file not shown.
Loading…
Cancel
Save