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

3
Core/core.py

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

10
Core/login.py

@ -5,10 +5,10 @@ import functools
import json import json
import os import os
import pickle import pickle
from logger import logger from Logger.logger import logger
from config import global_config from Config.settings import config
from exception import SKException from Core.exception import SKException
from util import ( from Core.util import (
parse_json, parse_json,
wait_some_time, wait_some_time,
response_status, response_status,
@ -21,7 +21,7 @@ class SpiderSession:
""" """
def __init__(self): def __init__(self):
self.cookies_dir_path = "./cookies/" 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() self.session = self._init_session()

38
Core/spider.py

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

26
Core/timer.py

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

6
Core/util.py

@ -79,13 +79,13 @@ def wait_some_time():
def send_wechat(message): 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 = { payload = {
"text":'抢购结果', "text":'抢购结果',
"desp": message "desp": message
} }
headers = { 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) requests.get(url, params=payload, headers=headers)
@ -111,6 +111,6 @@ def open_image(image_file):
def save_image(resp, 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): for chunk in resp.iter_content(chunk_size=1024):
f.write(chunk) f.write(chunk)

6
Message/message.py

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

68
Server/api.py

@ -1,28 +1,25 @@
import copy import copy
import psutil
from Config.settings import config 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): 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') file_page_file = open(file_path, 'r')
return str(file_page_file.read()) 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): def serverConfig(request):
appConfig = copy.deepcopy(config._config._sections) appConfig = copy.deepcopy(config._config._sections)
@ -32,3 +29,38 @@ def serverConfig(request):
value = appConfig[model][item] value = appConfig[model][item]
# DEBUG print(model, item, value, type(value)) # DEBUG print(model, item, value, type(value))
return appConfig 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 @@
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 @@
import os, json import os, json, urllib, time
import time
from Logger.logger import logger from Logger.logger import logger
from http.server import BaseHTTPRequestHandler from http.server import BaseHTTPRequestHandler
@ -16,6 +15,7 @@ class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self): def do_GET(self):
self.rootPath = config.path() + "/Static" self.rootPath = config.path() + "/Static"
url = self.requestline[4:-9] url = self.requestline[4:-9]
print(url)
request_data = {} # 存放GET请求数据 request_data = {} # 存放GET请求数据
try: try:
if url.find('?') != -1: if url.find('?') != -1:
@ -25,15 +25,35 @@ class RequestHandler(BaseHTTPRequestHandler):
for i in parameters: for i in parameters:
key, val = i.split('=', 1) key, val = i.split('=', 1)
request_data[key] = val request_data[key] = val
#request_data['body'] = self.rfile.read()
except: except:
logger.error("URL Format Error") logger.error("URL Format Error")
if (url == "/"): if (url == "/"):
self.home() self.home()
elif (url == ""):
self.noFound()
elif ("/api" in url): elif ("/api" in url):
self.api(url[4:], request_data) self.api(url[4:], request_data)
else: else:
self.file(url) 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): def log_message(self, format, *args):
SERVER_LOGGER = config.settings("Logger", "SERVER_LOGGER") SERVER_LOGGER = config.settings("Logger", "SERVER_LOGGER")
if SERVER_LOGGER: if SERVER_LOGGER:
@ -103,7 +123,6 @@ class RequestHandler(BaseHTTPRequestHandler):
# ---------------------------------------------------------------- # ----------------------------------------------------------------
# 此处写API # 此处写API
content = urls(url, request_data) content = urls(url, request_data)
# ---------------------------------------------------------------- # ----------------------------------------------------------------
localtime = time.localtime(time.time()) localtime = time.localtime(time.time())
date = \ date = \

5
Server/url.py

@ -1,7 +1,8 @@
from Server.api import log, systemInfo, serverConfig from Server.api import log, serverConfig, jdShopper, loginStatus
def urls(url, request): def urls(url, request):
if (url == "/log"): return log(request) if (url == "/log"): return log(request)
elif (url == "/system-info"): return systemInfo(request)
elif (url == "/config"): return serverConfig(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" else: return "No Response"

267
Static/404.html

@ -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 @@
.data_dict, .data_dict_goods{ body {
display: none; display: flex;
justify-content: center;
align-self: center;
height: 100vh;
width: 100vw;
} }
#form_container{ #form_container{
width: 600px; width: 95vw;
margin: 0 auto; max-width: 600px;
border: 1px solid gainsboro; border: 1px solid gainsboro;
height: auto;
display: flex;
flex-direction: column;
justify-content: center;
align-self: center;
} }
h1{ h1{
text-align: center; text-align: center;
margin: 30px 0; margin: 30px 0;
padding-bottom: 20px; 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{ #account_input, #password_input{
width: 300px; width: 300px;
} }
@ -30,4 +49,13 @@ h1{
} }
#run_button{ #run_button{
margin-left: 30px; 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 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>订单自动提交</title> <title>订单自动提交</title>
<link rel="Shortcut Icon" href="title.ico" type="image/x-icon" /> <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/element_index.css" />
<link type="text/css" rel="stylesheet" href="css/index.css" /> <link type="text/css" rel="stylesheet" href="./css/index.css" />
</head> </head>
<body> <body>
<div id="form_container"> <div id="form_container">
<h1>订单自动提交</h1> <h1>订单自动提交</h1>
<el-form ref="form" :model="form" label-width="80px"> <textarea v-show="!task" id="log" onpropertychange="this.scrollTop=this.scrollHeight" οnfοcus="this.scrollTop=this.scrollHeight" ></textarea>
<el-form-item label="登录方式"> <el-form v-show="task" ref="form" label-width="80px">
<el-radio v-model="radio_login" label="1" @change="LoginMode" border <el-form-item label="下单模式">
>自动登录</el-radio <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 v-model="mode" label="2" @change="buyMode" border
>手动登录</el-radio >定时下单</el-radio
> >
</el-form-item> </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 <el-input
id="account_input" id="account_input"
v-model="account_value" v-model="area"
placeholder="请输入账号" placeholder="所属地区ID"
:disabled="input_writeable"
></el-input> ></el-input>
</el-form-item> </el-form-item>
<el-form-item label="密码" id="password_form"> <el-form-item label="商品" id="goods_link_form">
<el-input <el-input
id="password_input" class="goods_input"
v-model="password_value" v-model="skurl"
placeholder="请输入密码" placeholder="填入商品链接"
:disabled="input_writeable"
show-password
></el-input> ></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>
<el-form-item label="运行时间"> <el-form-item label="超时时间">
<el-select v-model="date_value" placeholder="选择日期(默认今天)"> <el-input-number
<el-option v-model="timeout"
v-for="item in date_options" :min="1"
:key="item.date_value" :max="1000"
:label="item.label" label="超时时间"
:value="item.date_value" ></el-input-number>
> </el-form-item>
</el-option> <el-form-item label="重试次数">
</el-select> <el-input-number
<el-time-picker v-model="retry"
v-model="time_value" :min="1"
:picker-options="{}" :max="1000"
placeholder="选择时间(默认现在)" label="重试次数"
> ></el-input-number>
</el-time-picker> </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> </el-form-item>
<goods_link></goods_link> <el-button id="add_button" @click="reset()">重置选项</el-button>
<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 <el-button
id="run_button" id="run_button"
@click="changeRunStatus" @click="upload()"
type="primary" type="primary"
:class="run_status"
>开始运行</el-button >开始运行</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> </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> </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> </body>
<script src="js/vue.js"></script> <script src="./js/vue.js"></script>
<script src="js/element_index.js"></script> <script src="./js/element_index.js"></script>
<script> <script src="https://gias.jd.com/js/td.js"></script>
Vue.component("goods_link", { <script src="./js/index.js"></script>
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>
</html> </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 @@
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 @@
return Vue; 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