diff --git a/.gitignore b/.gitignore index 1fe1b00..577f783 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,9 @@ .idea/ node_modules/ +ServiceWrapper/ +Data/ +/GPUCache +*.exe +*.ini +*.7z +*.mp4 \ No newline at end of file diff --git a/ExcuteStage/.gitignore b/ExcuteStage/.gitignore index e037b88..03d53ab 100644 --- a/ExcuteStage/.gitignore +++ b/ExcuteStage/.gitignore @@ -7,3 +7,5 @@ dist/ build/ __pycache__/ *.spec +Chrome/ +Data/ diff --git a/ExcuteStage/Readme.md b/ExcuteStage/Readme.md new file mode 100644 index 0000000..b584185 --- /dev/null +++ b/ExcuteStage/Readme.md @@ -0,0 +1,2 @@ +pyinstaller -F --icon=favicon.ico ServiceWrapper_ExcuteStage.py +出现permission denied请关闭杀毒软件 \ No newline at end of file diff --git a/ExcuteStage/ServiceWrapper_ExcuteStage.py b/ExcuteStage/ServiceWrapper_ExcuteStage.py index 0d049f8..33550bd 100644 --- a/ExcuteStage/ServiceWrapper_ExcuteStage.py +++ b/ExcuteStage/ServiceWrapper_ExcuteStage.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import atexit # 遇到错误退出时应执行的代码 import json import re import sys @@ -17,21 +18,32 @@ from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import TimeoutException from selenium.common.exceptions import StaleElementReferenceException +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities import random import numpy import csv import os +from selenium.webdriver.common.by import By + + +saveName, log, OUTPUT, browser, SAVED = None, "", "", None, False + +desired_capabilities = DesiredCapabilities.CHROME +desired_capabilities["pageLoadStrategy"] = "none" + class Time: - def __init__(self,type1=""): - self.t = int(round(time.time() * 1000)) + def __init__(self, type1=""): + self.t = int(round(time.time() * 1000)) self.type = type1 - + def end(self): at = int(round(time.time() * 1000)) Log(str(self.type)+":"+str(at-self.t)) # 记录log + + def recordLog(str=""): global log log = log + str + "\n" @@ -43,17 +55,48 @@ def Log(text, text2=""): if switch: print(text, text2) +# 屏幕滚动函数 + + +def scrollDown(para, rt=""): + try: + if para["scrollType"] != 0 and para["scrollCount"] > 0: # 控制屏幕向下滚动 + for i in range(para["scrollCount"]): + time.sleep(1) # 下拉完等1秒 + Log("下拉完等待1秒") + body = browser.find_element_by_css_selector("body") + if para["scrollType"] == 1: + body.send_keys(Keys.PGDN) + else: + body.send_keys(Keys.END) + except TimeoutException: + Log('time out after 10 seconds when scrolling. ') + recordLog('time out after 10 seconds when scrolling') + browser.execute_script('window.stop()') + if para["scrollType"] != 0 and para["scrollCount"] > 0: # 控制屏幕向下滚动 + for i in range(para["scrollCount"]): + time.sleep(1) # 下拉完等1秒 + Log("下拉完等待1秒") + body = browser.find_element_by_css_selector("body") + if para["scrollType"] == 1: + body.send_keys(Keys.PGDN) + else: + body.send_keys(Keys.END) + if rt != "": + rt.end() + # 执行节点关键函数部分 def excuteNode(nodeId, loopValue="", clickPath="", index=0): node = procedure[nodeId] WebDriverWait(browser, 10).until - (EC.visibility_of_element_located((By.XPATH, node["parameters"]["xpath"]))) # 等待元素出现才进行操作,10秒内未出现则报错 + # 等待元素出现才进行操作,10秒内未出现则报错 + (EC.visibility_of_element_located((By.XPATH, node["parameters"]["xpath"]))) # 根据不同选项执行不同操作 if node["option"] == 0 or node["option"] == 10: # root操作,条件分支操作 for i in node["sequence"]: # 从根节点开始向下读取 - excuteNode(i, loopValue,clickPath,index) + excuteNode(i, loopValue, clickPath, index) elif node["option"] == 1: # 打开网页操作 recordLog("openPage") openPage(node["parameters"], loopValue) @@ -67,10 +110,10 @@ def excuteNode(nodeId, loopValue="", clickPath="", index=0): inputInfo(node["parameters"], loopValue) elif node["option"] == 8: # 循环 recordLog("loop") - loopExcute(node, loopValue,clickPath,index) # 执行循环 + loopExcute(node, loopValue, clickPath, index) # 执行循环 elif node["option"] == 9: # 条件分支 recordLog("judge") - judgeExcute(node, loopValue,clickPath,index) + judgeExcute(node, loopValue, clickPath, index) # 执行完之后进行等待 if node["option"] != 0: @@ -82,7 +125,7 @@ def excuteNode(nodeId, loopValue="", clickPath="", index=0): # 对判断条件的处理 -def judgeExcute(node, loopElement,clickPath="",index=0): +def judgeExcute(node, loopElement, clickPath="", index=0): rt = Time("条件判断") global bodyText # 引入bodyText excuteBranchId = 0 # 要执行的BranchId @@ -101,7 +144,7 @@ def judgeExcute(node, loopElement,clickPath="",index=0): continue elif tType == 2: # 当前页面包含元素 try: - if browser.find_element_by_xpath(cnode["parameters"]["value"]): + if browser.find_element(By.XPATH, cnode["parameters"]["value"]): excuteBranchId = i break except: # 找不到元素或者xpath写错了,下一个条件 @@ -115,22 +158,23 @@ def judgeExcute(node, loopElement,clickPath="",index=0): continue elif tType == 4: # 当前循环元素包括元素 try: - if loopElement.find_element_by_xpath(cnode["parameters"]["value"][1:]): + if loopElement.find_element(By.XPATH, cnode["parameters"]["value"][1:]): excuteBranchId = i break except: # 找不到元素或者xpath写错了,下一个条件 continue rt.end() - excuteNode(excuteBranchId, loopElement,clickPath,index) + excuteNode(excuteBranchId, loopElement, clickPath, index) # 对循环的处理 -def loopExcute(node, loopValue,clickPath="",index=0): +def loopExcute(node, loopValue, clickPath="", index=0): time.sleep(0.1) # 第一次执行循环的时候强制等待1秒 Log("循环执行前等待0.1秒") global history thisHandle = browser.current_window_handle # 记录本次循环内的标签页的ID - thisHistoryLength = browser.execute_script('return history.length') # 记录本次循环内的history的length + thisHistoryLength = browser.execute_script( + 'return history.length') # 记录本次循环内的history的length history["index"] = thisHistoryLength history["handle"] = thisHandle @@ -139,9 +183,10 @@ def loopExcute(node, loopValue,clickPath="",index=0): count = 0 # 执行次数 while True: # do while循环 try: - element = browser.find_element_by_xpath(node["parameters"]["xpath"]) + element = browser.find_element(By.XPATH, + node["parameters"]["xpath"]) for i in node["sequence"]: # 挨个执行操作 - excuteNode(i, element, node["parameters"]["xpath"],0) + excuteNode(i, element, node["parameters"]["xpath"], 0) Log("click: ", node["parameters"]["xpath"]) recordLog("click:" + node["parameters"]["xpath"]) except NoSuchElementException: @@ -153,14 +198,17 @@ def loopExcute(node, loopValue,clickPath="",index=0): count = count + 1 Log("页数:", count) recordLog("页数:" + str(count)) + # print(node["parameters"]["exitCount"], "-------") if node["parameters"]["exitCount"] == count: # 如果达到设置的退出循环条件的话 break elif int(node["parameters"]["loopType"]) == 1: # 不固定元素列表 try: - elements = browser.find_elements_by_xpath(node["parameters"]["xpath"]) + elements = browser.find_elements(By.XPATH, + node["parameters"]["xpath"]) for index in range(len(elements)): for i in node["sequence"]: # 挨个执行操作 - excuteNode(i, elements[index], node["parameters"]["xpath"], index) + excuteNode(i, elements[index], + node["parameters"]["xpath"], index) if browser.current_window_handle != thisHandle: # 如果执行完一次循环之后标签页的位置发生了变化 while True: # 一直关闭窗口直到当前标签页 browser.close() # 关闭使用完的标签页 @@ -168,9 +216,11 @@ def loopExcute(node, loopValue,clickPath="",index=0): if browser.current_window_handle == thisHandle: break if history["index"] != thisHistoryLength and history[ - "handle"] == browser.current_window_handle: # 如果执行完一次循环之后历史记录发生了变化,注意当前页面的判断 - difference = thisHistoryLength - history["index"] # 计算历史记录变化差值 - browser.execute_script('history.go(' + str(difference) + ')') # 回退历史记录 + "handle"] == browser.current_window_handle: # 如果执行完一次循环之后历史记录发生了变化,注意当前页面的判断 + difference = thisHistoryLength - \ + history["index"] # 计算历史记录变化差值 + browser.execute_script( + 'history.go(' + str(difference) + ')') # 回退历史记录 if node["parameters"]["historyWait"] > 2: # 回退后要等待的时间 time.sleep(node["parameters"]["historyWait"]) else: @@ -186,7 +236,7 @@ def loopExcute(node, loopValue,clickPath="",index=0): elif int(node["parameters"]["loopType"]) == 2: # 固定元素列表 for path in node["parameters"]["pathList"].split("\n"): # 千万不要忘了分割!! try: - element = browser.find_element_by_xpath(path) + element = browser.find_element(By.XPATH, path) for i in node["sequence"]: # 挨个执行操作 excuteNode(i, element, path, 0) if browser.current_window_handle != thisHandle: # 如果执行完一次循环之后标签页的位置发生了变化 @@ -196,9 +246,11 @@ def loopExcute(node, loopValue,clickPath="",index=0): if browser.current_window_handle == thisHandle: break if history["index"] != thisHistoryLength and history[ - "handle"] == browser.current_window_handle: # 如果执行完一次循环之后历史记录发生了变化,注意当前页面的判断 - difference = thisHistoryLength - history["index"] # 计算历史记录变化差值 - browser.execute_script('history.go(' + str(difference) + ')') # 回退历史记录 + "handle"] == browser.current_window_handle: # 如果执行完一次循环之后历史记录发生了变化,注意当前页面的判断 + difference = thisHistoryLength - \ + history["index"] # 计算历史记录变化差值 + browser.execute_script( + 'history.go(' + str(difference) + ')') # 回退历史记录 if node["parameters"]["historyWait"] > 2: # 回退后要等待的时间 time.sleep(node["parameters"]["historyWait"]) else: @@ -216,16 +268,18 @@ def loopExcute(node, loopValue,clickPath="",index=0): for text in textList: recordLog("input: " + text) for i in node["sequence"]: # 挨个执行操作 - excuteNode(i, text, "",0) + excuteNode(i, text, "", 0) elif int(node["parameters"]["loopType"]) == 4: # 固定网址列表 pass # 以后再做 history["index"] = thisHistoryLength history["handle"] = browser.current_window_handle + scrollDown(node["parameters"]) # 打开网页事件 def openPage(para, loopValue): rt = Time("打开网页") + time.sleep(2) # 打开网页后强行等待至少2秒 global links global urlId global history @@ -248,18 +302,7 @@ def openPage(para, loopValue): browser.execute_script('window.stop()') history["index"] = browser.execute_script("return history.length") rt.end() - try: - if para["scrollType"] != 0 and para["scrollCount"] > 0: # 控制屏幕向下滚动 - for i in range(para["scrollCount"]): - time.sleep(1) # 下拉完等1秒 - Log("下拉等待1秒") - body = browser.find_element_by_css_selector("body") - body.send_keys(Keys.END) - except TimeoutException: - Log('time out after 10 seconds when loading page: ' + url) - recordLog('time out after 10 seconds when loading page: ' + url) - browser.execute_script('window.stop()') - rt.end() + scrollDown(para, rt) # 控制屏幕向下滚动 if containJudge: global bodyText # 每次执行点击,输入元素和打开网页操作后,需要更新bodyText try: @@ -285,7 +328,7 @@ def inputInfo(para, loopValue): Log("输入前等待1秒") rt = Time("输入文字") try: - textbox = browser.find_element_by_xpath(para["xpath"]) + textbox = browser.find_element(By.XPATH, para["xpath"]) except: Log("找不到输入框元素:" + para["xpath"] + "请尝试执行前等待") recordLog("找不到输入框元素:" + para["xpath"] + "请尝试执行前等待") @@ -313,7 +356,8 @@ def clickElement(para, loopElement=None, clickPath="", index=0): path = para["xpath"] # 不然使用元素定义的xpath tempHandleNum = len(browser.window_handles) # 记录之前的窗口位置 try: - script = 'var result = document.evaluate(`' + path + '`, document, null, XPathResult.ANY_TYPE, null);for(let i=0;i 0: # 控制屏幕向下滚动 - for i in range(para["scrollCount"]): - time.sleep(1) # 下拉完等1秒 - Log("下拉完等待1秒") - body = browser.find_element_by_css_selector("body") - body.send_keys(Keys.END) - except TimeoutException: - Log('time out after 10 seconds when scrolling. ') - recordLog('time out after 10 seconds when scrolling') - browser.execute_script('window.stop()') - if para["scrollType"] != 0 and para["scrollCount"] > 0: # 控制屏幕向下滚动 - for i in range(para["scrollCount"]): - time.sleep(1) # 下拉完等1秒 - Log("下拉完等待1秒") - body = browser.find_element_by_css_selector("body") - body.send_keys(Keys.END) - rt.end() + scrollDown(para, rt) # 根据参数配置向下滚动 if containJudge: # 有判断语句才执行以下操作 global bodyText # 每次执行点击,输入元素和打开网页操作后,需要更新bodyText try: @@ -393,13 +420,19 @@ def getData(para, loopElement, isInLoop=True): if p["relativeXpath"] == "": # 相对xpath有时候就是元素本身,不需要二次查找 element = loopElement else: - element = loopElement.find_element_by_xpath(p["relativeXpath"][1:]) + element = loopElement.find_element(By.XPATH, + p["relativeXpath"][1:]) else: - element = browser.find_element_by_xpath(p["relativeXpath"]) + element = browser.find_element(By.XPATH, p["relativeXpath"]) except NoSuchElementException: # 找不到元素的时候,使用默认值 - outputParameters[p["name"]] = p["default"] - Log('Element not found,use default') - recordLog('Element not found,use default') + # print(p) + try: + content = p["default"] + except Exception as e: + content = "" + outputParameters[p["name"]] = content + Log('Element %s not found,use default' % p["relativeXpath"]) + recordLog('Element %s not found, use default' % p["relativeXpath"]) continue except TimeoutException: # 超时的时候设置超时值 Log('time out after 10 seconds when getting data') @@ -409,9 +442,10 @@ def getData(para, loopElement, isInLoop=True): if p["relativeXpath"] == "": # 相对xpath有时候就是元素本身,不需要二次查找 element = loopElement else: - element = loopElement.find_element_by_xpath(p["relativeXpath"][1:]) + element = loopElement.find_element(By.XPATH, + p["relativeXpath"][1:]) else: - element = browser.find_element_by_xpath(p["relativeXpath"]) + element = browser.find_element(By.XPATH, p["relativeXpath"]) rt.end() try: if p["contentType"] == 2: @@ -428,7 +462,8 @@ def getData(para, loopElement, isInLoop=True): }\ var str = arr.join(" "); \ return str;' - content = browser.execute_script(command, element).replace("\n", "").replace("\\s+", " ") + content = browser.execute_script(command, element).replace( + "\n", "").replace("\\s+", " ") if p["nodeType"] == 2: if element.get_attribute("href") != None: content = element.get_attribute("href") @@ -461,19 +496,22 @@ def getData(para, loopElement, isInLoop=True): content = element.get_attribute("src") else: content = "" - except StaleElementReferenceException: #发生找不到元素的异常后,等待几秒重新查找 + except StaleElementReferenceException: # 发生找不到元素的异常后,等待几秒重新查找 recordLog('StaleElementReferenceException:'+p["relativeXpath"]) - time.sleep(3) + time.sleep(3) try: if p["relative"]: # 是否相对xpath if p["relativeXpath"] == "": # 相对xpath有时候就是元素本身,不需要二次查找 element = loopElement recordLog('StaleElementReferenceException:loopElement') else: - element = loopElement.find_element_by_xpath(p["relativeXpath"][1:]) - recordLog('StaleElementReferenceException:loopElement+relativeXPath') + element = loopElement.find_element(By.XPATH, + p["relativeXpath"][1:]) + recordLog( + 'StaleElementReferenceException:loopElement+relativeXPath') else: - element = browser.find_element_by_xpath(p["relativeXpath"]) + element = browser.find_element( + By.XPATH, p["relativeXpath"]) recordLog('StaleElementReferenceException:relativeXpath') if p["contentType"] == 2: content = element.get_attribute('innerHTML') @@ -489,7 +527,8 @@ def getData(para, loopElement, isInLoop=True): }\ var str = arr.join(" "); \ return str;' - content = browser.execute_script(command, element).replace("\n", "").replace("\\s+", " ") + content = browser.execute_script(command, element).replace( + "\n", "").replace("\\s+", " ") if p["nodeType"] == 2: if element.get_attribute("href") != None: content = element.get_attribute("href") @@ -524,7 +563,7 @@ def getData(para, loopElement, isInLoop=True): content = "" except StaleElementReferenceException: recordLog('StaleElementReferenceException:'+p["relativeXpath"]) - continue # 再出现类似问题直接跳过 + continue # 再出现类似问题直接跳过 outputParameters[p["name"]] = content global OUTPUT line = [] @@ -541,13 +580,12 @@ def isnull(s): return len(s) != 0 -import atexit #遇到错误退出时应执行的代码 -@atexit.register -def clean(): - global saveName,log, OUTPUT,browser,SAVED +@atexit.register +def clean(): + global saveName, log, OUTPUT, browser, SAVED if not SAVED: print('清理环境保存数据') - with open("Data/"+saveName + '_log.txt', 'w',encoding='utf-8-sig') as file_obj: + with open("Data/"+saveName + '_log.txt', 'w', encoding='utf-8-sig') as file_obj: file_obj.write(log) file_obj.close() with open("Data/"+saveName + '.csv', 'w', encoding='utf-8-sig', newline="") as f: @@ -557,42 +595,53 @@ def clean(): f.close() browser.quit() + if __name__ == '__main__': options = Options() + exe_path = "chromedriver.exe" if os.path.exists(os.getcwd()+"/ServiceWrapper"): - options.binary_location="ServiceWrapper/Chrome/chrome.exe" #指定chrome位置 + options.binary_location = "ServiceWrapper/Chrome/chrome.exe" # 指定chrome位置 + exe_path = "ServiceWrapper/Chrome/chromedriver.exe" elif os.path.exists(os.getcwd()+"/Debug"): - options.binary_location="Debug/Chrome/chrome.exe" #指定chrome位置 - elif os.getcwd().find("ExcuteStage") >= 0: #如果直接执行 - options.binary_location = "../../bin/x64/Debug/Chrome/chrome.exe" # 指定chrome位置 + options.binary_location = "Debug/Chrome/chrome.exe" # 指定chrome位置 + exe_path = "Debug/Chrome/chromedriver.exe" + elif os.getcwd().find("ExcuteStage") >= 0: # 如果直接执行 + options.binary_location = "./Chrome/chrome.exe" # 指定chrome位置 + exe_path = "./Chrome/chromedriver.exe" else: - options.binary_location="chrome.exe" #指定chrome位置 - browser = webdriver.Chrome(chrome_options=options) + options.binary_location = "chrome.exe" # 指定chrome位置 + browser = webdriver.Chrome(options=options, executable_path=exe_path) browser.get('about:blank') - browser.set_page_load_timeout(10) # 加载页面最大超时时间 - if len(sys.argv)>1: - id = int(sys.argv[1]) #taskId这里修改 + browser.set_page_load_timeout(10) # 加载页面最大超时时间 + browser.set_script_timeout(10) + if len(sys.argv) > 1: + id = int(sys.argv[1]) # taskId这里修改 else: - id = 7 #设置默认值 - print("id:",id) - if len(sys.argv)>2: - saveName = "task_" + str(id) + "_" + sys.argv[2] # 保存文件的名字 + id = 7 # 设置默认值 + print("id:", id) + if len(sys.argv) > 2: + backEndAddress = sys.argv[2] else: - saveName = "task_" + str(id) + "_" + str(random.randint(0, 999999999)) # 保存文件的名字 - content = requests.get("http://183.129.170.180:8041/backEnd/queryTask?id=" + str(id)) + backEndAddress = "http://servicewrapper.naibo.wang" + if len(sys.argv) > 3: + saveName = "task_" + str(id) + "_" + sys.argv[3] # 保存文件的名字 + else: + saveName = "task_" + str(id) + "_" + \ + str(random.randint(0, 999999999)) # 保存文件的名字 + content = requests.get(backEndAddress + "/backEnd/queryTask?id=" + str(id)) service = json.loads(content.text) # 加载服务信息 - print("name:",service["name"]) + print("name:", service["name"]) procedure = service["graph"] # 程序执行流程 links = list(filter(isnull, service["links"].split("\n"))) # 要执行的link的列表 OUTPUT = [] # 采集的数据 OUTPUT.append([]) # 添加表头 - containJudge = service["containJudge"] #是否含有判断语句 + containJudge = service["containJudge"] # 是否含有判断语句 bodyText = "" # 记录bodyText tOut = service["outputParameters"] # 生成输出参数对象 outputParameters = {} log = "" # 记下现在总共开了多少个标签页 - history = {"index":0,"handle":None} #记录页面现在所以在的历史记录的位置 - SAVED=False #记录是否已经存储了 + history = {"index": 0, "handle": None} # 记录页面现在所以在的历史记录的位置 + SAVED = False # 记录是否已经存储了 for para in tOut: outputParameters[para["name"]] = "" OUTPUT[0].append(para["name"]) @@ -604,7 +653,7 @@ if __name__ == '__main__': print("执行完成!") recordLog("Done!") # dataPath = os.path.abspath(os.path.join(os.getcwd(), "../Data")) - with open("Data/"+saveName + '_log.txt', 'w',encoding='utf-8-sig') as file_obj: + with open("Data/"+saveName + '_log.txt', 'w', encoding='utf-8-sig') as file_obj: file_obj.write(log) file_obj.close() with open("Data/"+saveName + '.csv', 'w', encoding='utf-8-sig', newline="") as f: @@ -614,4 +663,4 @@ if __name__ == '__main__': f.close() SAVED = True browser.quit() - sys.exit(0) \ No newline at end of file + sys.exit(0) diff --git a/Extension/ServiceWrapper.crx b/Extension/ServiceWrapper.crx index eaa6b45..1e4523a 100644 Binary files a/Extension/ServiceWrapper.crx and b/Extension/ServiceWrapper.crx differ diff --git a/Extension/ServiceWrapper.pem b/Extension/ServiceWrapper.pem index 8584fd9..484e41b 100644 --- a/Extension/ServiceWrapper.pem +++ b/Extension/ServiceWrapper.pem @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCuAZmXoS3RN1dQ -IWpPjMZMX08JQRiR9XF8fPuZ4r/aauafNKncQmC2FEFylAcBMaRVSjAsZlXVqTlE -s1LRtt9PiJEiDv73alerwznzNfuN0Dqrj2YpvKlck/sdFDeJNGi3dAl4kWZOKlE0 -kpHXO6BQBKB9t9Up5qBku0epGYNZ6/foCl82U5bw8bBaZ5p0F2hhE4/PWP0p/MED -HR7yvnr5qO0B6qQia/59q4opxH6sswa6XAc3mgU1Yq8m1cXRctmvxCBt7biU7Gpw -jLB4Qdgikq02hWxn1YUbGAjHRjLrNxOIFBP6Sx2SNllM664bUmzwchnEzsLILyDQ -jzKlafobAgMBAAECggEAGdp7APXIYkbKs56DYSvAECYzuWLxTPPJgimVExmG+o4v -kzAL8iyamfMLK676wMlTaqi0h6RHfztpcDUEn8wqrx0naIa53fZFmGulTJGUvU6s -U5k2y19l+oSyJsaTidCTo85F1KLQhISdFCaYLKvbTIhOIDUUIumf+IIq9OD+4c6C -nhAKhkjspDt1ctyFy9QXE5Wbi6o8AdOuaw9QW+y7msi1kcTWovxFBQkN1Ck4D2m7 -y72M5dDYq3nWEyQDJvioDM2yxvywiX3piHjULBj60Ybt52EEyRNlBMkbafU0puYU -aajlPSv0X0mQjdtYLmd2dEVTH5rNLkVRwtx0i5MYoQKBgQDnJkJY6sj6HzhnFtWo -NiorwAteNG6fIchZPAJjdtAQNu2stakGtnRoQ3WfVi2ePUKntoyqS4WanJE/z4pX -lTMAePB8madwqe+AgSJzl0+YnOqBQCk6DeIrJ9u0O+nb7wlkZMbaULj6Sx8bg+N4 -jM/MSD4uo1eYJdo6bIq6KNHXTQKBgQDAtqJlZ+EweMD9K652htwW0HE0IXD7zxMh -8rODaubAp+glapW77GYltFrJ++Q8ObBdm+jLo8CbWUTus61qpsinv+CV//dIEiYp -vbdf+oFnCEHvFTN1opzsS9ThYZhRnnonuxYmMCWsx55GG7oyAVtYLBUERm6XurLY -FXkceAvzBwKBgD/UB8Qn6SFwV5HX813EvzZfjIQR9G4K1RkXAg3XNDMWB+GGNEt/ -PHvCDQvXrzcf8XUAaq1nt1fvXgiB1dDDiVbbDoVJpLvuoQ0aG5pRsRASXIseXYQD -a23BTuDhHn217yEC1wpX+gxbjBZ7/+c88vCVDl6wijKgWTeC23f2Z6ONAoGAP2CQ -3cqg+2DrDxe7g41sejBI2n0Y/CcowqRftxuEEd3mcc+wuKHRIhtDNGQbtla+krqA -f+A1qqFcEyiSIp4BJXHr+Ui52UDmvhl/YhgvUGQd0vPk/Y3iD7AMraZ8AnOc9s4H -Rb3V1hG8EpBx1potoTy2GLbVDh8/S+Mb9mngfCcCgYEAlL/oEGR6j7K/29gF8KjQ -J6HfNxdisP0b14her/mX0NSSv8WNFX77XO6pjXmpiIQPsm4I1KlzeFEVBy9RetGb -5OKLQdfILNe8aDrYxMKcjbuA9zLqIzYep8siLdrYI1RDSxKt7ZUW6JejPthV0vr5 -408+uF8kwRJscoRPn7+x7Uk= +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCr3o0YTuz2UMy7 +qolwd4CWXet3SjJL0XawKKgp3ZRo78zER1Z/LQ5/rZqc6DISBPCycAoOCBk2EcBZ +IlPKQFxq1PMj10ccyswjjb+4mf4ZRVveeflBqT9I4khrzwk9gymSZiD7TbkDc4fp +oxoHCv2f7VMR5m/IwsnXEsm7QyuRSA6ETbhp2Faa2CIGI4l6r+gtuVYkUSGEN/0H +0dk3kfh6rcjwvroLSzWcGJnhyOLDYxnV1kTHZDUBzGj0UOqL0fdpAuyXxSikadxa +Ec4BZ4s3GHIlFR5N7itrwYN28IInLObxUQXJ3unEdnon/T/XhC5KsdjMp8D6Xb8/ +0dObgaG/AgMBAAECggEAR0xyZQCs/fwECuBS1uTEY9y/h4OwvNI+9hJPvNpurF+6 +pzxe7jBPYPkweGI9D0ucXLHtAegrKUqQ7Ik4kGFF1Y3U9evjVNbue9tciyTbDDnB +RFh+ZlZjagVxfMPtNeb5MoIKsaYLGBrv6aUfcYeGMre96+GYQwVHvWDOblCNvN0k +fBInSGlDcLkx9EJ9vFbCf8IErg4RWtiEdSvSG1s99IPc17j4cJn4qq9OyQrECpdz +y7AnkvsMOiOzQawO+C2I5SgA26eaxNJrz/3ph4r1x1iCdPvY7iD/awkQXCgid6kq +Z0R8ByxTbwqr4NYXtUyHVkwiL9QZSKjnvrOfdmm0iQKBgQDkQ5KRMftmDi9cJfOj +0xY/TwCymsWp+MdMQ6YKAd57oMM+2DzgZ//LFgo4rcYWjTGlErE8tXhT4fM5PEUQ +1CgwozmkPcVnRzLyiNWNpiMCzPVPsXDxzoEQFHL0qLA/Xj7npH5d+Eg6yunDc9JS +D/OlqSEZQ6W4lWFvAkz7Mh5M6QKBgQDAwMFzAMYVXwcRmH0gvzmqA46MqjdfaXyq +NzNADiVCaGgqYpNygeftiHGCBQiXr3QkVVp+XihbY/5KnzVYiS0ICKMCwqxizvRT +gR3N4+UyVaK9FTWLgtSu8VN4JM3E+nigBO/QfD4WI4VZ3tZEsOveV70j97NpfaPN +lhjr2J0wZwKBgG3pj7i0bY5dxDZF3ASFjw/a9cYKuqU2spipdlkZP00eQwWOz/lq +eoQKz88s9dZEFKSc1JUb+J3Djf+AYu1Qiy2oWwgX6mbppMGeW95CIlel43WFRpJY +0lKw1g2y8HMC6Z1W2rZa6ETPEjLYIWz8W2DoiJSGu1SfMXRnkjPelTKJAoGACvbs +Ci6xFeYh8M5Lz+EQ1qr9IONN4w+NF+Gr+KjVVcG6qy6QVKMvHkP0sQC4TGieorJ4 +Q1f307sMbBJCZpbnCN305+NLCxPasiVWHLAqCYL1juv178mxb4IqzVrKmbnlwrSF +L8bhgUDkBQi4B5BI2o0DJVihzA5pkvhG0qOvzWECgYAHITSiOJBF1Lr0hw7JnS5r +Hre8bQdbDeqSd5iRM/+0MJnBvkp7RDgvh0c01Wc90A0gjhhPqBS9z5UNptiJKhJQ +kYDP7iiftM7FsG45IGKf6RrulLk6pCyFEFb/roBlC1xy/6uNCqAHbd+6K6UjQAFB ++c9hCtSDDh3/irZrY34lDw== -----END PRIVATE KEY----- diff --git a/Extension/ServiceWrapper/scripts/baozhuangscript.js b/Extension/ServiceWrapper/scripts/baozhuangscript.js index d674a2e..f2105e0 100644 --- a/Extension/ServiceWrapper/scripts/baozhuangscript.js +++ b/Extension/ServiceWrapper/scripts/baozhuangscript.js @@ -1,6 +1,6 @@ //表现层的处理 -if (window.location.href.indexOf("183.129.170.180") >= 0) { +if (window.location.href.indexOf("backEndAddressServiceWrapper") >= 0) { throw "serviceGrid"; //如果是服务器网页页面,则不执行工具 } diff --git a/Extension/ServiceWrapper/scripts/contentscript.js b/Extension/ServiceWrapper/scripts/contentscript.js index abc3321..78a14a6 100644 --- a/Extension/ServiceWrapper/scripts/contentscript.js +++ b/Extension/ServiceWrapper/scripts/contentscript.js @@ -1,50 +1,50 @@ //表现逻辑层的处理 -if (window.location.href.indexOf("183.129.170.180") >= 0) { +if (window.location.href.indexOf("backEndAddressServiceWrapper") >= 0) { throw "serviceGrid"; //如果是服务器网页页面,则不执行工具 } -//Vue元素 +//Vueelement var app; generateToolkit(); //生成Toolkit function generateToolkit() { $(".tooltips").html(`
-
✍操作提示框(可点此拖动)
+
✍Operation Toolbox (Can drag)
-

特殊点选模式

+

Special click mode

-

● 鼠标移动到元素上后,请右键点击或者按F7键选中页面元素。

-

● 如果不小心左键点选了元素导致页面跳转,直接后退或者切换回标签页即可。

+

● When your mouse moves to the element, please use yourright mouse buttonorF7 on the keyboard to select it.

+

● You can click the back button to go back to the page

{{initial()}}
- ● 已选中{{numOfList()}}个{{tname()}},同时发现{{numOfReady()}}个同类元素,您可以: + ● Already selected {{numOfList()}} {{tname()}},, meanwhile we find {{numOfReady()}} element with the same type, you can:
@@ -53,21 +53,21 @@ function generateToolkit() {
- ● 已选择了以下元素,您可以: + ● 已select了以下element,您可以:
- ● 已选择了{{numOfList()}}个同类元素,另外发现{{numOfReady()}}个同类元素,您可以: + ● 已select了{{numOfList()}}个同类element,另外发现{{numOfReady()}}个同类element,您可以:
@@ -91,11 +91,11 @@ function generateToolkit() {
-
{{setWidth("230px")}}
+
{{setWidth("230px")}}
- +

{{lastElementXPath()}}

@@ -116,14 +116,14 @@ function generateToolkit() { data: { option: 0, list: { nl: nodeList, opp: outputParameters }, - valTable: [], // 用来存储转换后的参数列表 - special: false, //是否为特殊选择模式 - selectedDescendents: false, // 标记是否选中了子元素 - selectStatus: false, //标记单个元素是否点击了采集 + valTable: [], // 用来存储转换后的para列表 + special: false, //是否为特殊selection模式 + selectedDescendents: false, // 标记是否选中了子element + selectStatus: false, //标记单个element是否点击了采集 page: 0, //默认页面,1为输入文字页面 text: "", // 记录输入的文字 tNodeName: "", // 记录临时节点列表 - nowPath: "", //现在元素的xpath + nowPath: "", //现在element的xpath }, watch: { nowPath: { //变量发生变化的时候进行一些操作 @@ -133,34 +133,34 @@ function generateToolkit() { } }, methods: { - initial: function() { //每当元素是0的时候,执行值的初始化操作 + initial: function() { //每当element是0的时候,执行值的初始化操作 this.selectedDescendents = false; this.selectStatus = false; this.nowPath = ""; }, - confirmCollectSingle: function() { //单元素确认采集 + confirmCollectSingle: function() { //单element确认采集 collectSingle(); clearEl(); }, - confirmCollectMulti: function() { //无规律多元素确认采集 + confirmCollectMulti: function() { //无规律多element确认采集 collectMultiNoPattern(); clearEl(); }, - confirmCollectMultiAndDescendents: function() { //有规律多元素确认采集 + confirmCollectMultiAndDescendents: function() { //有规律多element确认采集 collectMultiWithPattern(); clearEl(); }, - deleteSingleLine: function(event) { //删除单行元素 + deleteSingleLine: function(event) { //删除单行element let at = new Date().getTime() - //流程图送元素的时候,默认的使用不固定循环列表,但是一旦有删除元素的操作发生,则按照固定元素列表采集元素 + //流程图送element的时候,默认的使用不固定循环列表,但是一旦有删除element的操作发生,则按照固定element列表采集element index = event.target.getAttribute("index"); - let tnode = nodeList.splice(index, 1)[0]; //删掉当前元素 + let tnode = nodeList.splice(index, 1)[0]; //删掉当前element tnode["node"].style.backgroundColor = tnode["bgColor"]; tnode["node"].style.boxShadow = tnode["boxShadow"]; if (nodeList.length > 1) { // 如果删到没有就没有其他的操作了 handleElement(); if (this.selectedDescendents) { - handleDescendents(); //如果之前有选中子元素,新加入的节点又则这里也需要重新选择子元素 + handleDescendents(); //如果之前有选中子element,新加入的节点又则这里也需要重新selection子element } } else { this.valTable = []; @@ -170,25 +170,25 @@ function generateToolkit() { let at2 = parseInt(new Date().getTime()); console.log("delete:", at2, at, at2 - at); }, - clickElement: function() { //点击元素操作 + clickElement: function() { //点击element操作 sendSingleClick(); //先发送数据 - nodeList[0]["node"].focus(); //获得元素焦点 - nodeList[0]["node"].click(); //点击元素 + nodeList[0]["node"].focus(); //获得element焦点 + nodeList[0]["node"].click(); //点击element clearEl(); }, - loopClickSingleElement: function() { //循环点击单个元素 - sendLoopClickSingle(this.tname()); //识别下一页,循环点击单个元素和点击多个元素 - if (this.tname() != "下一页元素") { //下一页元素不进行点击操作 - nodeList[0]["node"].focus(); //获得元素焦点 - nodeList[0]["node"].click(); //点击元素 + loopClickSingleElement: function() { //循环点击单个element + sendLoopClickSingle(this.tname()); //识别下一页,循环点击单个element和点击多个element + if (this.tname() != "Elements in next page") { //Elements in next page不进行点击操作 + nodeList[0]["node"].focus(); //获得element焦点 + nodeList[0]["node"].click(); //点击element } clearEl(); }, - loopClickEveryElement: function() { //循环点击每个元素 - sendLoopClickEvery(); //识别下一页,循环点击单个元素和点击多个元素 - nodeList[0]["node"].focus(); //获得元素焦点 - nodeList[0]["node"].click(); //点击元素 + loopClickEveryElement: function() { //循环点击每个element + sendLoopClickEvery(); //识别下一页,循环点击单个element和点击多个element + nodeList[0]["node"].focus(); //获得element焦点 + nodeList[0]["node"].click(); //点击element clearEl(); }, setInput: function() { //输入文字 @@ -199,7 +199,7 @@ function generateToolkit() { }, getInput: function() { //得到输入的文字 nodeList[0]["node"].focus(); //获得文字焦点 - // nodeList[0]["node"].setAttribute("value", this.text); // 设置输入框内容 + // nodeList[0]["node"].setAttribute("value", this.text); // 设置输入 box内容 input(this.text); // 设置输入 this.text = ""; clearEl(); @@ -207,7 +207,7 @@ function generateToolkit() { cancelInput: function() { this.page = 0; }, - setWidth: function(width) { //根据是否出现表格调整最外框宽度 + setWidth: function(width) { //根据是否出现表格调整最外 box宽度 $(".tooltips").css("width", width); return ""; }, @@ -216,7 +216,7 @@ function generateToolkit() { this.selectStatus = true; clearReady(); }, - getLink: function() { //采集链接地址 + getLink: function() { //采集linkAddress generateParameters(0, false, true); this.selectStatus = true; clearReady(); @@ -243,22 +243,22 @@ function generateToolkit() { return "null"; } else if ($(nodeList[0]["node"]).contents().filter(function() { return this.nodeType === 3; }).text().indexOf("下一页") >= 0) { this.setWidth("250px"); - return "下一页元素"; + return "Elements in next page"; } else if (tag == "A") { - return "链接"; + return "link"; } else if (tag == "IMG") { - return "图片"; + return "Image"; } else if (tag == "BUTTON" || (tag == "INPUT" && (inputType == "button" || inputType == "submit"))) { - return "按钮"; - } else if (tag == "TEXTAREA" || (tag == "INPUT" && (inputType != "checkbox" || inputType != "ratio"))) { //普通输入框 - return "文本框"; + return "Button"; + } else if (tag == "TEXTAREA" || (tag == "INPUT" && (inputType != "checkbox" || inputType != "ratio"))) { //普通输入 box + return "text box"; } else if (tag == "SELECT") { - return "选择框"; + return "selection box"; } else { - return "元素"; + return "element"; } }, - existDescendents: function() { //检测选中的元素是否存在子元素,已经选中了子元素也不要再出现了 + existDescendents: function() { //检测选中的element是否存在子element,已经选中了子element也不要再出现了 return nodeList.length > 0 && nodeList[0]["node"].children.length > 0 && !this.selectedDescendents; }, numOfReady: function() { @@ -267,11 +267,11 @@ function generateToolkit() { numOfList: function() { return nodeList.length; }, - lastElementXPath: function() { //用来显示元素的最大最后5个xpath路劲元素 + lastElementXPath: function() { //用来显示element的最大最后5个xpath路劲element path = nodeList[nodeList.length - 1]["xpath"]; path = path.split("/"); tp = ""; - if (path.length > 5) { //只保留最后五个元素 + if (path.length > 5) { //只保留最后五个element path = path.splice(path.length - 5, 5); tp = ".../" } @@ -285,7 +285,7 @@ function generateToolkit() { cancel: function() { clearEl(); }, - specialSelect: function() { //特殊选择模式 + specialSelect: function() { //特殊selection模式 if (mousemovebind) { tdiv.style.pointerEvents = "none"; this.special = false; @@ -294,10 +294,10 @@ function generateToolkit() { } mousemovebind = !mousemovebind; }, - enlarge: function() { // 扩大选区功能,总是扩大最后一个选中的元素的选区 + enlarge: function() { // 扩大选区功能,总是扩大最后一个选中的element的选区 if (nodeList[nodeList.length - 1]["node"].tagName != "BODY") { - nodeList[nodeList.length - 1]["node"].style.backgroundColor = nodeList[nodeList.length - 1]["bgColor"]; //之前元素恢复原来的背景颜色 - nodeList[nodeList.length - 1]["node"].style.boxShadow = nodeList[nodeList.length - 1]["boxShadow"]; //之前元素恢复原来的背景颜色 + nodeList[nodeList.length - 1]["node"].style.backgroundColor = nodeList[nodeList.length - 1]["bgColor"]; //之前element恢复原来的背景颜色 + nodeList[nodeList.length - 1]["node"].style.boxShadow = nodeList[nodeList.length - 1]["boxShadow"]; //之前element恢复原来的背景颜色 tNode = nodeList[nodeList.length - 1]["node"].parentNode; //向上走一层 if (tNode != NowNode) { //扩大选区之后背景颜色的判断,当前正好选中的颜色应该是不同的 sty = tNode.style.backgroundColor; @@ -307,7 +307,7 @@ function generateToolkit() { nodeList[nodeList.length - 1]["node"] = tNode; nodeList[nodeList.length - 1]["bgColor"] = sty; nodeList[nodeList.length - 1]["xpath"] = readXPath(tNode, 1); - //显示框 + //显示 box var pos = tNode.getBoundingClientRect(); div.style.display = "block"; div.style.height = tNode.offsetHeight + "px"; @@ -316,39 +316,39 @@ function generateToolkit() { div.style.top = pos.top + "px"; div.style.zIndex = 2147483645; div.style.pointerEvents = "none"; - handleElement(); //每次数组元素有变动,都需要重新处理下 + handleElement(); //每次数组element有变动,都需要重新处理下 oe = tNode; tNode.style.backgroundColor = "rgba(0,191,255,0.5)"; this.selectedDescendents = false; } }, - selectAll: function() { //选中全部元素 + selectAll: function() { //选中全部element step++; readyToList(step, false); handleElement(); if (this.selectedDescendents) { - handleDescendents(); //如果之前有选中子元素,新加入的节点又则这里也需要重新选择子元素 + handleDescendents(); //如果之前有选中子element,新加入的节点又则这里也需要重新selection子element } }, - revoke: function() { //撤销选择当前节点 + revoke: function() { //撤销selection当前节点 var tstep = step; step--; //步数-1 - while (tstep == nodeList[nodeList.length - 1]["step"]) //删掉所有当前步数的元素节点 + while (tstep == nodeList[nodeList.length - 1]["step"]) //删掉所有当前步数的element节点 { let node = nodeList.splice(nodeList.length - 1, 1)[0]; //删除数组最后一项 - node["node"].style.backgroundColor = node["bgColor"]; //还原原始属性和边框 + node["node"].style.backgroundColor = node["bgColor"]; //还原原始属性和边 box node["node"].style.boxShadow = node["boxShadow"]; if (NowNode == node["node"]) { style = node["bgColor"]; } - //处理已经有选中子元素的情况 + //处理已经有选中子element的情况 // if (this.selectedDescendents) { clearParameters(); //直接撤销重选 // } } - handleElement(); //每次数组元素有变动,都需要重新处理下 + handleElement(); //每次数组element有变动,都需要重新处理下 }, - selectDescendents: function() { //选择所有子元素操作 + selectDescendents: function() { //selection所有子element操作 handleDescendents(); } }, @@ -358,7 +358,7 @@ function generateToolkit() { if (difference > 0) { $(".tooldrag").css("cssText", "height:" + (26 + difference) + "px!important") } - timer = setInterval(function() { //时刻监测相应元素是否存在(防止出现如百度一样元素消失重写body的情况),如果不存在,添加进来 + timer = setInterval(function() { //时刻监测相应element是否存在(防止出现如百度一样element消失重写body的情况),如果不存在,添加进来 if (document.body != null && document.getElementById("wrapperToolkit") == null) { this.clearInterval(); //先取消原来的计时器,再设置新的计时器 document.body.append(div); //默认如果toolkit不存在则div和tdiv也不存在 @@ -381,10 +381,10 @@ function generateToolkit() { }, 3000); } -//每次对元素进行增删之后需要执行的操作 +//每次对element进行增删之后需要执行的操作 function handleElement() { - clearReady(); //预备元素每次处理都先处理掉 - if (nodeList.length > 1) { //选中了许多元素的情况 + clearReady(); //预备element每次处理都先处理掉 + if (nodeList.length > 1) { //选中了许多element的情况 app._data.option = relatedTest(); if (app._data.option == 100) { generateMultiParameters(); @@ -392,13 +392,13 @@ function handleElement() { generateParameters(0); } } else if (nodeList.length == 1) { - findRelated(); //寻找和元素相关的元素 + findRelated(); //寻找和element相关的element } } -function clearParameters(deal = true) //清空参数列表 +function clearParameters(deal = true) //清空para列表 { - if (deal) //是否取消对选中的子元素进行处理 + if (deal) //是否取消对选中的子element进行处理 { app._data.selectedDescendents = false; } @@ -406,20 +406,20 @@ function clearParameters(deal = true) //清空参数列表 o["node"].style.boxShadow = o["boxShadow"]; } outputParameterNodes.splice(0); - outputParameters.splice(0); //清空原来的参数列表 + outputParameters.splice(0); //清空原来的para列表 app._data.valTable = []; //清空展现数组 app._data.selectStatus = false; } -//根据nodelist列表内的元素生成参数列表 -//适合:nodelist中的元素为同类型元素 -//type:0为全部文本 1为节点内直接的文字 2为innerhtml 3为outerhtml +//根据nodelist列表内的element生成para列表 +//适合:nodelist中的element为同类型element +//type:0为全部text 1为节点内直接的文字 2为innerhtml 3为outerhtml //nodetype:0,对应全type0123 -//nodetype:1 链接,对应type0123 -//nodetype:2 链接地址 对应type0 -//nodetype:3 按钮和输入文本框 对应type -//nodetype:4 按钮和输入文本框 对应type +//nodetype:1 link,对应type0123 +//nodetype:2 linkAddress 对应type0 +//nodetype:3 按钮和输入text box 对应type +//nodetype:4 按钮和输入text box 对应type function generateParameters(type, linktext = true, linkhref = true) { clearParameters(false); @@ -432,23 +432,23 @@ function generateParameters(type, linktext = true, linkhref = true) { ndPath = nodeList[num]["xpath"]; outputParameterNodes.push({ "node": nd, "boxShadow": nd.style.boxShadow == "" || boxShadowColor ? "none" : nd.style.boxShadow }); nd.style.boxShadow = boxShadowColor; - let pname = "文本"; + let pname = "text"; let ndText = ""; if (type == 0) { ndText = $(nd).text(); - pname = "文本"; + pname = "text"; if (nd.tagName == "IMG") { ndText = nd.getAttribute("src") == null ? "" : $(nd).prop("src"); - pname = "地址"; + pname = "Address"; } else if (nd.tagName == "INPUT") { ndText = nd.getAttribute("value") == null ? "" : nd.getAttribute("value"); } } else if (type == 1) { ndText = $(nd).contents().filter(function() { return this.nodeType === 3; }).text().replace(/\s+/g, ''); - pname = "文本"; + pname = "text"; if (nd.tagName == "IMG") { ndText = nd.getAttribute("src") == null ? "" : $(nd).prop("src"); - pname = "地址"; + pname = "Address"; } else if (nd.tagName == "INPUT") { ndText = nd.getAttribute("value") == null ? "" : nd.getAttribute("value"); } @@ -460,24 +460,24 @@ function generateParameters(type, linktext = true, linkhref = true) { pname = "outerHTML"; } if (num == 0) { //第一个节点新建,后面的增加即可 - if (nd.tagName == "IMG") { //如果元素是图片 + if (nd.tagName == "IMG") { //如果element是Image outputParameters.push({ "nodeType": 4, //节点类型 "contentType": type, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_图片" + pname, - "desc": "", //参数描述 + "name": "para" + (n++) + "_Image" + pname, + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? "" : ndPath, "exampleValues": [{ "num": num, "value": ndText }] }); - } else if (nd.tagName == "A") { //如果元素是超链接 + } else if (nd.tagName == "A") { //如果element是超link if (linktext) { outputParameters.push({ "nodeType": 1, "contentType": type, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接" + pname, - "desc": "", //参数描述 + "name": "para" + (n++) + "_link" + pname, + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? "" : ndPath, "exampleValues": [{ "num": num, "value": ndText }] }); @@ -487,19 +487,19 @@ function generateParameters(type, linktext = true, linkhref = true) { "nodeType": 2, "contentType": type, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接地址", - "desc": "", //参数描述 + "name": "para" + (n++) + "_linkAddress", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? "" : ndPath, "exampleValues": [{ "num": num, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }] }); } - } else if (nd.tagName == "INPUT") { //如果元素是输入项 + } else if (nd.tagName == "INPUT") { //如果element是输入项 outputParameters.push({ "nodeType": 3, "contentType": type, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_" + pname, - "desc": "", //参数描述 + "name": "para" + (n++) + "_" + pname, + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? "" : ndPath, "exampleValues": [{ "num": num, "value": ndText }] }); @@ -508,19 +508,19 @@ function generateParameters(type, linktext = true, linkhref = true) { "nodeType": 0, "contentType": type, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_" + pname, - "desc": "", //参数描述 + "name": "para" + (n++) + "_" + pname, + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? "" : ndPath, "exampleValues": [{ "num": num, "value": ndText }] }); } - } else { //如果元素节点已经存在,则只需要插入值就可以了 - if (nd.tagName == "IMG") { //如果元素是图片 + } else { //如果element节点已经存在,则只需要插入值就可以了 + if (nd.tagName == "IMG") { //如果element是Image outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText }); - } else if (nd.tagName == "A") { //如果元素是超链接 + } else if (nd.tagName == "A") { //如果element是超link outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText }); outputParameters[1]["exampleValues"].push({ "num": num, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }); - } else if (nd.tagName == "INPUT") { //如果元素是输入项 + } else if (nd.tagName == "INPUT") { //如果element是输入项 outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText }); } else { //其他所有情况 outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText }); @@ -536,8 +536,8 @@ function generateParameters(type, linktext = true, linkhref = true) { } -//根据nodelist列表内的元素生成参数列表 -//适合:nodelist中的元素为不同类型元素 +//根据nodelist列表内的element生成para列表 +//适合:nodelist中的element为不同类型element function generateMultiParameters() { clearParameters(false); let n = 1; @@ -550,23 +550,23 @@ function generateMultiParameters() { outputParameterNodes.push({ "node": nd, "boxShadow": nd.style.boxShadow == "" || boxShadowColor ? "none" : nd.style.boxShadow }); nd.style.boxShadow = boxShadowColor; ndText = $(nd).text(); - if (nd.tagName == "IMG") { //如果元素是图片 + if (nd.tagName == "IMG") { //如果element是Image outputParameters.push({ "nodeType": 4, //节点类型 "contentType": 0, // 内容类型 "relative": false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_图片地址", - "desc": "", //参数描述 + "name": "para" + (n++) + "_imageAddress", + "desc": "", //para描述 "relativeXpath": ndPath, "exampleValues": [{ "num": 0, "value": nd.getAttribute("src") == null ? "" : $(nd).prop("src") }] }); - } else if (nd.tagName == "A") { //如果元素是超链接 + } else if (nd.tagName == "A") { //如果element是超link outputParameters.push({ "nodeType": 1, "contentType": 0, // 内容类型 "relative": false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_linktext", + "desc": "", //para描述 "relativeXpath": ndPath, "exampleValues": [{ "num": 0, "value": ndText }] }); @@ -574,18 +574,18 @@ function generateMultiParameters() { "nodeType": 2, "contentType": 0, // 内容类型 "relative": false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接地址", - "desc": "", //参数描述 + "name": "para" + (n++) + "_linkAddress", + "desc": "", //para描述 "relativeXpath": ndPath, "exampleValues": [{ "num": 0, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }] }); - } else if (nd.tagName == "INPUT") { //如果元素是输入项 + } else if (nd.tagName == "INPUT") { //如果element是输入项 outputParameters.push({ "nodeType": 3, "contentType": 0, // 内容类型 "relative": false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_text", + "desc": "", //para描述 "relativeXpath": ndPath, "exampleValues": [{ "num": 0, "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") }] }); @@ -594,8 +594,8 @@ function generateMultiParameters() { "nodeType": 0, "contentType": 0, // 内容类型 "relative": false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_text", + "desc": "", //para描述 "relativeXpath": ndPath, "exampleValues": [{ "num": 0, "value": ndText }] }); @@ -610,22 +610,22 @@ function generateMultiParameters() { } -//处理子元素,对于每个块中多出的特殊元素,需要特殊处理 +//处理子element,对于每个块中多出的特殊element,需要特殊处理 function handleDescendents() { let n = 1; chrome.storage.local.get({ parameterNum: 1 }, function(items) { let at = parseInt(new Date().getTime()); n = items.parameterNum; - clearParameters(); //清除原来的参数列表 + clearParameters(); //清除原来的para列表 app._data.selectedDescendents = true; for (let num = 0; num < nodeList.length; num++) { let tnode = nodeList[num]["node"]; - let stack = new Array(); //深度优先搜索遍历元素 + let stack = new Array(); //深度优先搜索遍历element stack.push(tnode); //从此节点开始 while (stack.length > 0) { - let nd = stack.pop(); // 挨个取出元素 + let nd = stack.pop(); // 挨个取出element if (nd.parentNode.tagName == "A" && nd.tagName == "SPAN") { - continue; //对A标签内的SPAN元素不进行处理,剪枝,此时子元素根本不加入stack,即实现了此功能 + continue; //对A标签内的SPANelement不进行处理,剪枝,此时子element根本不加入stack,即实现了此功能 } ndPath = readXPath(nd, 1, tnode); let index = -1; @@ -643,29 +643,29 @@ function handleDescendents() { ndText = $(nd).contents().filter(function() { return this.nodeType === 3; }).text().replace(/\s+/g, ''); - if (index == -1) { //从第二个节点开始,只插入那些不在参数列表中的元素,根据xpath进行寻址 - //如果当前节点除了子元素外仍然有其他文字或者该元素是图片/表单项,加入子元素节点 + if (index == -1) { //从第二个节点开始,只插入那些不在para列表中的element,根据xpath进行寻址 + //如果当前节点除了子element外仍然有其他文字或者该element是Image/表单项,加入子element节点 if (ndText != "" || nd.tagName == "IMG" || nd.tagName == "INPUT" || nd.tagName == "A") { - if (nd.tagName == "IMG") { //如果元素是图片 + if (nd.tagName == "IMG") { //如果element是Image outputParameters.push({ "nodeType": 4, //节点类型 "contentType": 1, // 内容类型 - "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径,注意当只选择了子元素没有选中全部的时候,需要判断 - "name": "参数" + (n++) + "_图片地址", - "desc": "", //参数描述 + "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径,注意当只selection了子element没有选中全部的时候,需要判断 + "name": "para" + (n++) + "_imageAddress", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), //同理需要判断 "exampleValues": [{ "num": num, "value": nd.getAttribute("src") == null ? "" : $(nd).prop("src") }] }); - } else if (nd.tagName == "A") { //如果元素是超链接 + } else if (nd.tagName == "A") { //如果element是超link outputParameters.push({ "nodeType": 1, "contentType": 0, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_linktext", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), "exampleValues": [{ "num": num, "value": $(nd).text() }] //注意这里的ndtext是整个a的文字!!! }); @@ -673,21 +673,21 @@ function handleDescendents() { "nodeType": 2, "contentType": 0, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接地址", - "desc": "", //参数描述 + "name": "para" + (n++) + "_linkAddress", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), "exampleValues": [{ "num": num, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }] }); - } else if (nd.tagName == "INPUT") { //如果元素是输入项 + } else if (nd.tagName == "INPUT") { //如果element是输入项 outputParameters.push({ "nodeType": 3, "contentType": 1, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_text", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), "exampleValues": [{ "num": num, @@ -699,27 +699,27 @@ function handleDescendents() { "nodeType": 0, "contentType": 1, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_text", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), "exampleValues": [{ "num": num, "value": ndText }] }); } } - } else //如果元素节点已经存在,则只需要插入值就可以了 + } else //如果element节点已经存在,则只需要插入值就可以了 { - if (nd.tagName == "IMG") { //如果元素是图片 + if (nd.tagName == "IMG") { //如果element是Image outputParameters[index]["exampleValues"].push({ "num": num, "value": nd.getAttribute("src") == null ? "" : $(nd).prop("src") }); - } else if (nd.tagName == "A") { //如果元素是超链接 + } else if (nd.tagName == "A") { //如果element是超link outputParameters[index]["exampleValues"].push({ "num": num, "value": $(nd).text() }); outputParameters[index + 1]["exampleValues"].push({ "num": num, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }); - } else if (nd.tagName == "INPUT") { //如果元素是输入项 + } else if (nd.tagName == "INPUT") { //如果element是输入项 outputParameters[index]["exampleValues"].push({ "num": num, "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") @@ -734,14 +734,14 @@ function handleDescendents() { } } let at2 = parseInt(new Date().getTime()); - console.log("选中子元素", at2, at, at2 - at); + console.log("选中子element", at2, at, at2 - at); generateValTable(); }); } -//根据参数列表生成可视化参数界面 +//根据para列表生成可视化para界面 function generateValTable(multiline = true) { let paravalues = []; for (let i = 0; i < outputParameters.length; i++) { @@ -764,7 +764,7 @@ function generateValTable(multiline = true) { // 选中第一个节点,自动寻找同类节点 // 方法:/div[1]/div[2]/div[2]/a[1] -// 从倒数第一个节点开始找,看去掉方括号之后是否元素数目变多,如上面的变成/div[1]/div[2]/div[2]/a +// 从倒数第一个节点开始找,看去掉方括号之后是否element数目变多,如上面的变成/div[1]/div[2]/div[2]/a // 如果没有,则恢复原状,然后试试倒数第二个:/div[1]/div[2]/div/a[1] // 直到找到第一个变多的节点或者追溯到根节点为止 function findRelated() { @@ -789,9 +789,9 @@ function findRelated() { tempIndexList[i] = -1; //删除索引值 tempPath = combineXpath(nodeNameList, tempIndexList); //生成新的xpath var result = document.evaluate(tempPath, document, null, XPathResult.ANY_TYPE, null); - result.iterateNext(); //枚举第一个元素 - if (result.iterateNext() != null) { //如果能枚举到第二个元素,说明存在同类元素,选中同类元素,结束循环 - app.$data.nowPath = tempPath; //标记此元素xpath + result.iterateNext(); //枚举第一个element + if (result.iterateNext() != null) { //如果能枚举到第二个element,说明存在同类element,选中同类element,结束循环 + app.$data.nowPath = tempPath; //标记此elementxpath pushToReadyList(tempPath); break; } @@ -801,11 +801,11 @@ function findRelated() { } -//根据path将元素放入readylist中 +//根据path将element放入readylist中 function pushToReadyList(path) { result = document.evaluate(path, document, null, XPathResult.ANY_TYPE, null); - var node = result.iterateNext(); //枚举第一个元素 - while (node) { //只添加不在已选中列表内的元素 + var node = result.iterateNext(); //枚举第一个element + while (node) { //只添加不在已选中列表内的element let exist = false; for (o of nodeList) { if (o["node"] == node) { @@ -817,19 +817,19 @@ function pushToReadyList(path) { readyList.push({ "node": node, "bgColor": node.style.backgroundColor, "boxShadow": node.style.boxShadow == "" || boxShadowColor ? "none" : node.style.boxShadow }); } node.style.boxShadow = boxShadowColor; - node = result.iterateNext(); //枚举下一个元素 + node = result.iterateNext(); //枚举下一个element } } -//将readyList中的元素放入选中节点中 +//将readyList中的element放入选中节点中 function readyToList(step, dealparameters = true) { for (o of readyList) { nodeList.push({ node: o["node"], "step": step, bgColor: o["bgColor"], "boxShadow": o["boxShadow"], xpath: readXPath(o["node"], 1) }); o["node"].style.backgroundColor = selectedColor; } clearReady(); - if (dealparameters) { //防止出现先选中子元素再选中全部失效的问题 - generateParameters(0); //根据nodelist列表内的元素生成参数列表,0代表纯文本 + if (dealparameters) { //防止出现先选中子element再选中全部失效的问题 + generateParameters(0); //根据nodelist列表内的element生成para列表,0代表纯text } } @@ -847,7 +847,7 @@ function combineXpath(nameList, indexList) { return finalPath; } -//专门测试已经选中的这些元素之间有没有相关性 +//专门测试已经选中的这些element之间有没有相关性 // 举例: // /html/body/div[3]/div[1]/div[1]/div[1]/div[3]/div[1]/div[3]/a[22] // /html/body/div[3]/div[1]/div[1]/div[1]/div[3]/div[2]/div[3]/a[25] @@ -859,7 +859,7 @@ function relatedTest() { var testpath = ""; for (i = 0; i < nodeList.length; i++) { var testnumList = []; //用于比较节点索引号不同 - var tpath = nodeList[i]["xpath"].split("/").splice(1); //清理第一个空元素 + var tpath = nodeList[i]["xpath"].split("/").splice(1); //清理第一个空element for (j = 0; j < tpath.length; j++) { if (tpath[j].indexOf("[") >= 0) { //如果存在索引值 testnumList.push(parseInt(tpath[j].split("[")[1].replace("]", ""))); //只留下数字 @@ -869,16 +869,16 @@ function relatedTest() { tpath[j] = tpath[j].split("[")[0]; } tp = tpath.join("/"); - if (i > 0 && testpath != tp) { //如果去除括号后元素内存在不一致情况,直接返回默认情况代码100 - app.$data.nowPath = ""; //标记此元素xpath + if (i > 0 && testpath != tp) { //如果去除括号后element内存在不一致情况,直接返回默认情况代码100 + app.$data.nowPath = ""; //标记此elementxpath return 100; } testpath = tp; testList.push(testnumList); } - testpath = testpath.split("/"); //清理第一个空元素 + testpath = testpath.split("/"); //清理第一个空element var indexList = []; //记录新生成的xpath - //如果选中的元素属于同样的序列,则计算出序列的最佳xpath表达式 + //如果选中的element属于同样的序列,则计算出序列的最佳xpath表达式 for (j = 0; j < testList[0].length; j++) { indexList.push(testList[0][j]); for (i = 1; i < testList.length; i++) { @@ -889,14 +889,14 @@ function relatedTest() { } } var finalPath = combineXpath(testpath, indexList); - app.$data.nowPath = finalPath; //标记此元素xpath + app.$data.nowPath = finalPath; //标记此elementxpath pushToReadyList(finalPath); let at2 = parseInt(new Date().getTime()); console.log("手动:", at2, at, at2 - at); return 50; //先返回给默认码 } -//实现提示框拖拽功能 +//实现提示 box拖拽功能 $('.tooldrag').mousedown(function(e) { // e.pageX var positionDiv = $(this).offset(); diff --git a/Extension/ServiceWrapper/scripts/messageInteraction.js b/Extension/ServiceWrapper/scripts/messageInteraction.js index 8cd6e86..7e3b0c6 100644 --- a/Extension/ServiceWrapper/scripts/messageInteraction.js +++ b/Extension/ServiceWrapper/scripts/messageInteraction.js @@ -1,6 +1,6 @@ //实现与后台和流程图部分的交互 -if (window.location.href.indexOf("183.129.170.180") >= 0) { +if (window.location.href.indexOf("backEndAddressServiceWrapper") >= 0) { throw "serviceGrid"; //如果是服务器网页页面,则不执行工具 } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Readme.md b/Readme.md index f214dc2..4f9eed5 100644 --- a/Readme.md +++ b/Readme.md @@ -1,31 +1,155 @@ +# 请您Star Please Star + +如果你觉得此工具不错,请轻轻点击此页面右上角**Star**按钮增加项目曝光度,谢谢! + +If you think this tool is good, please gently click the **Star** button in the upper right corner at this page to increase the project exposure, thank you! + +# 无代码服务可视化Web数据采集爬虫器 Code-Free Visual Web Data Crawler/Spider (Service Wrapper) + +一个可以可视化无代码设计和执行的面向服务架构的爬虫软件(服务包装器)。 +A service oriented architecture GUI visual code-free web crawler/spider (service wrapper). + # 发布版本 -## Windows版本可执行程序: -打开压缩包内的ServiceWrapper.exe即可在Windows10系统执行,无需配置环境。 +## Windows版本可执行程序: +打开压缩包内的ServiceWrapper.exe即可在Windows10/11或以上系统执行,无需配置环境(其余Windows系统需手动安装.net Framework 4.5)。 数据存储后放在Data/文件夹内 -## 视频教程: +## 中文视频教程: -# 服务包装手动版程序结构 -## Chrome插件部分 + + + + +# 目录 +注:文档待完善 +- [请您Star Please Star](#请您star-please-star) +- [无代码服务可视化Web数据采集爬虫器 Code-Free Visual Web Data Crawler/Spider (Service Wrapper)](#无代码服务可视化web数据采集爬虫器-code-free-visual-web-data-crawlerspider-service-wrapper) +- [发布版本](#发布版本) + - [Windows版本可执行程序:](#windows版本可执行程序httpsgithubcomnaibowangservicewrapperreleasesdownloadv050servicewrapper7z) + - [中文视频教程:](#中文视频教程httpsgithubcomnaibowangservicewrapperreleasesdownloadv050tutorial_cnmp4) +- [目录](#目录) + - [界面截图](#界面截图) + - [软件界面示例](#软件界面示例) + - [块和子块及表单定义](#块和子块及表单定义) + - [已选中和待选择示例](#已选中和待选择示例) + - [京东商品块选择示例:](#京东商品块选择示例) + - [京东商品标题自动匹配选择示例](#京东商品标题自动匹配选择示例) + - [分块选择所有子元素示例](#分块选择所有子元素示例) + - [同类型元素自动和手动匹配示例](#同类型元素自动和手动匹配示例) + - [四种选择方式示例](#四种选择方式示例) + - [输入文字示例](#输入文字示例) + - [循环点击58同城房屋标题以进入详情页采集示例](#循环点击58同城房屋标题以进入详情页采集示例) + - [采集元素文本示例](#采集元素文本示例) + - [流程图界面介绍](#流程图界面介绍) + - [循环选项示例](#循环选项示例) + - [循环点击下一页示例](#循环点击下一页示例) + - [条件分支示例](#条件分支示例) + - [完整采集流程图示例](#完整采集流程图示例) + - [完整采集流程图转换为常规流程图示例](#完整采集流程图转换为常规流程图示例) + - [服务信息示例](#服务信息示例) + - [服务调用示例](#服务调用示例) + - [58 同城房源信息采集服务部分采集结果展示](#58-同城房源信息采集服务部分采集结果展示) + - [服务包装手动版程序结构](#服务包装手动版程序结构) + - [Chrome插件部分](#chrome插件部分) + - [后台流程图部分](#后台流程图部分) + - [服务展示部分](#服务展示部分) + - [C#部分](#c部分) + - [后台服务页面](#后台服务页面) + - [服务执行](#服务执行) + + + +## 界面截图 + +#### 软件界面示例 + +![pic](media/Picture.png) +#### 块和子块及表单定义 + +![pic](media/Picture2.png) +#### 已选中和待选择示例 + +![pic](media/Picture7.png) +#### 京东商品块选择示例: + +![pic](media/Picture1.png) + + +#### 京东商品标题自动匹配选择示例 + +![pic](media/Picture5.png) +#### 分块选择所有子元素示例 + +![pic](media/Picture6.png) + +#### 同类型元素自动和手动匹配示例 + +![pic](media/Picture8.png) +#### 四种选择方式示例 + +![pic](media/Picture90.png) +#### 输入文字示例 + +![pic](media/Picture10.png) +#### 循环点击58同城房屋标题以进入详情页采集示例 + +![pic](media/Picture12.png) +#### 采集元素文本示例 + +![pic](media/Picture14.png) +#### 流程图界面介绍 + +![pic](media/Picture4.png) +#### 循环选项示例 + +![pic](media/Picture9.png) + +#### 循环点击下一页示例 + +![pic](media/Picture11.png) + +#### 条件分支示例 + +![pic](media/Picture13.png) +#### 完整采集流程图示例 + +![pic](media/Picture16.png) +#### 完整采集流程图转换为常规流程图示例 + +![pic](media/Picture91.png) +#### 服务信息示例 + +![pic](media/Picture15.png) + +#### 服务调用示例 + +![pic](media/Picture17.png) + + +#### 58 同城房源信息采集服务部分采集结果展示 +![pic](media/Picture18.png) + +## 服务包装手动版程序结构 +### Chrome插件部分 * Extension/app内的文件 -## 后台流程图部分 +### 后台流程图部分 * ServiceGrid/frontEnd/FlowChart.html * ServiceGrid/frontEnd/FlowChart.js * ServiceGrid/frontEnd/FlowChart.css * ServiceGrid/frontEnd/logic.css -## 服务展示部分 +### 服务展示部分 * 服务列表:ServiceGrid/frontEnd/serviceList.html * 服务信息:ServiceGrid/frontEnd/serviceInfo.html * 新服务:ServiceGrid/frontEnd/newService.html * 调用服务:ServiceGrid/frontEnd/invokeService.html -## C#部分 +### C#部分 * C#/内的文件 -## 后台服务页面 +### 后台服务页面 * Django后台:ServiceGrid/backEnd/* -## 服务执行 +### 服务执行 * ExcuteStage/ServiceWrapper_ExcuteStage.py diff --git a/ServiceGrid/backEnd/.gitignore b/ServiceGrid/backEnd/.gitignore new file mode 100644 index 0000000..b6a8c43 --- /dev/null +++ b/ServiceGrid/backEnd/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +nohup.out +*.pid \ No newline at end of file diff --git a/ServiceGrid/backEnd/backEnd/.gitignore b/ServiceGrid/backEnd/backEnd/.gitignore new file mode 100644 index 0000000..d1fa904 --- /dev/null +++ b/ServiceGrid/backEnd/backEnd/.gitignore @@ -0,0 +1 @@ +dbconfig.py \ No newline at end of file diff --git a/ServiceGrid/backEnd/backEnd/settings.py b/ServiceGrid/backEnd/backEnd/settings.py index 59fc86b..ac21151 100644 --- a/ServiceGrid/backEnd/backEnd/settings.py +++ b/ServiceGrid/backEnd/backEnd/settings.py @@ -38,6 +38,7 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'corsheaders', 'django.contrib.staticfiles', + 'sslserver', ] MIDDLEWARE = [ diff --git a/ServiceGrid/backEnd/backEnd/urls.py b/ServiceGrid/backEnd/backEnd/urls.py index 1b968ef..d904512 100644 --- a/ServiceGrid/backEnd/backEnd/urls.py +++ b/ServiceGrid/backEnd/backEnd/urls.py @@ -27,6 +27,4 @@ urlpatterns = [ path('backEnd/invokeService',view.invokeService), #调用服务接口 path('backEnd/queryTask',view.queryTask), #调用服务接口 path('backEnd/queryTasks',view.queryTasks), #调用服务接口 - path('backEnd/dongcang/insertInfo',view.insertInfo), - path('backEnd/dongcang/queryInfos',view.queryInfos), ] diff --git a/ServiceGrid/backEnd/backEnd/view.py b/ServiceGrid/backEnd/backEnd/view.py index 1b75baf..076714f 100644 --- a/ServiceGrid/backEnd/backEnd/view.py +++ b/ServiceGrid/backEnd/backEnd/view.py @@ -3,49 +3,49 @@ from django.http import HttpResponse import pymongo import json from bson import json_util +from dbconfig import myclient, mycol, taskcol +""" +uncomment this to deploy database +""" +# myclient = pymongo.MongoClient('mongodb://username:password@localhost:27017/') +# mydb = myclient['service'] +# mycol = mydb["services"] +# taskcol = mydb["tasks"] # 生成新任务并返回ID -""" -高分服务器地址:192.168.14.113 -用户名:root -密码:zju.edu.cn -ftp用户:naibowang -密码:qq -程序位置:/root/servicewrapper -""" def hello(request): return HttpResponse("Hello world ! ") -myclient = pymongo.MongoClient('mongodb://localhost:27017/') -mydb = myclient['service'] -mycol = mydb["services"] -taskcol = mydb["tasks"] #生成新任务并返回ID - def queryServices(request): - result = mycol.find({"id" : { "$ne" : -2 }},{ "name": 1, "id": 1, "url": 1, "_id": 0 }) #查询id不为-2的元素 + result = mycol.find({"id": {"$ne": -2}}, {"name": 1, + "id": 1, "url": 1, "_id": 0}) # 查询id不为-2的元素 return HttpResponse(json.dumps(list(result)), content_type="application/json") + def queryTasks(request): - result = taskcol.find({"id" : { "$ne" : -2 }},{ "name": 1, "id": 1, "url": 1, "_id": 0 }) #查询id不为-2的元素 + result = taskcol.find( + {"id": {"$ne": -2}}, {"name": 1, "id": 1, "url": 1, "_id": 0}) # 查询id不为-2的元素 return HttpResponse(json.dumps(list(result)), content_type="application/json") + def queryService(request): if 'id' in request.GET: tid = request.GET['id'] else: tid = "0" - result = mycol.find({"id":int(tid)},{"_id":0}) + result = mycol.find({"id": int(tid)}, {"_id": 0}) r = list(result)[0] return HttpResponse(json.dumps(r), content_type="application/json") + def queryTask(request): if 'id' in request.GET: tid = request.GET['id'] else: tid = "0" - - result = taskcol.find({"id":int(tid)},{"_id":0}) + + result = taskcol.find({"id": int(tid)}, {"_id": 0}) r = list(result)[0] return HttpResponse(json.dumps(r), content_type="application/json") @@ -54,55 +54,68 @@ def manageService(request): data = request.POST['paras'] data = json.loads(data) if int(data["id"]) == -1: - count = mycol.find({}).count() - data["id"] = count #修改id - mycol.insert_one(data) + count = mycol.count_documents({}) + data["id"] = count # 修改id + mycol.insert_one(data) else: - mycol.delete_one({"id":int(data["id"])}) + mycol.delete_one({"id": int(data["id"])}) mycol.insert_one(data) return HttpResponse(data["id"]) + def deleteService(request): if 'id' in request.GET: tid = request.GET['id'] - myquery = { "id": int(tid) } - newvalues = { "$set": { "id": -2 } } #删除就是将服务id变成-2,并没有真正删除 + myquery = {"id": int(tid)} + newvalues = {"$set": {"id": -2}} # 删除就是将服务id变成-2,并没有真正删除 mycol.update_one(myquery, newvalues) return HttpResponse("Done!") # 调用服务 + + def invokeService(request): tid = request.POST['id'] data = json.loads(request.POST['paras']) - result = mycol.find({"id":int(tid)},{"_id":0}) + result = mycol.find({"id": int(tid)}, {"_id": 0}) service = list(result)[0] - for key,value in data.items(): + try: + service["links"] = data["urlList_0"] + except: + pass + for key, value in data.items(): for i in range(len(service["inputParameters"])): - if key == service["inputParameters"][i]["name"]: #能调用 + if key == service["inputParameters"][i]["name"]: # 能调用 nodeId = int(service["inputParameters"][i]["nodeId"]) node = service["graph"][nodeId] if node["option"] == 1: node["parameters"]["links"] = value elif node["option"] == 4: node["parameters"]["value"] = value + elif node["option"] == 8 and node["parameters"]["loopType"] == 0: + # print("loopType 0", value) + node["parameters"]["exitCount"] = int(value) + # print(node) elif node["option"] == 8: node["parameters"]["textList"] = value break - count = taskcol.find({}).count() - service["id"] = count #修改id - taskcol.insert_one(service) + count = taskcol.count_documents({}) + service["id"] = count # 修改id + taskcol.insert_one(service) return HttpResponse(count) + def insertInfo(request): request.GET = request.GET.copy() data = request.GET dbd = myclient['dongcang'] cold = dbd["redirect"] - cold.insert_one(data) + cold.insert_one(data) return HttpResponse("200") + def queryInfos(request): dbd = myclient['dongcang'] cold = dbd["redirect"] result = cold.find() - return HttpResponse(json_util.dumps(result).encode('utf-8').decode('unicode_escape'), content_type="application/json") \ No newline at end of file + return HttpResponse(json_util.dumps(result).encode('utf-8').decode('unicode_escape'), content_type="application/json") diff --git a/ServiceGrid/backEnd/db.sqlite3 b/ServiceGrid/backEnd/db.sqlite3 index e69de29..cfb0b47 100644 Binary files a/ServiceGrid/backEnd/db.sqlite3 and b/ServiceGrid/backEnd/db.sqlite3 differ diff --git a/ServiceGrid/backEnd/server.conf b/ServiceGrid/backEnd/server.conf new file mode 100644 index 0000000..588dfdb --- /dev/null +++ b/ServiceGrid/backEnd/server.conf @@ -0,0 +1,40 @@ +server { + listen 443 ssl; + + server_name servicewrapper.naibo.wang; + + #ssl_certificate cert/5640170_naibo.wang.pem; + #ssl_certificate_key cert/5640170_naibo.wang.key; + ssl_certificate /etc/letsencrypt/live/servicewrapper.naibo.wang/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/servicewrapper.naibo.wang/privkey.pem; + + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #表示使用的TLS协议的类型。 + ssl_prefer_server_ciphers on; + + root /home/naibo/server/ServiceWrapper/ServiceGrid/frontEnd; + index serviceList.html; + + + location /backEnd { + include uwsgi_params; + uwsgi_connect_timeout 30; + uwsgi_pass 127.0.0.1:8072; + } + + location /static { + autoindex on; + autoindex_localtime on; + alias /home/naibo/static; + try_files $uri $uri/ =404; + } +} + + +server { + listen 80; + listen [::]:80; + server_name xtra3090.d2.comp.nus.edu.sg naibo.wang gui.naibo.wang servicewrapper.naibo.wang; #需要将yourdomain.com替换成证书绑定的域名。 + return 301 https://$host$request_uri; +} diff --git a/ServiceGrid/frontEnd/FlowChart.html b/ServiceGrid/frontEnd/FlowChart.html index 254be5b..bea4938 100644 --- a/ServiceGrid/frontEnd/FlowChart.html +++ b/ServiceGrid/frontEnd/FlowChart.html @@ -16,29 +16,29 @@
-
工具箱
- +
Toolbox
+ - - - - - - - - + + + + + + + +
-----------------
- - - - - -
提示:点击上方操作按钮后点击要放置元素的位置处的箭头,可按取消操作按钮取消。
+ + + + + +
@@ -54,47 +54,47 @@
-->
- +
-

使用循环内的链接

+

Use link inside the Loop

- +
- + - +
-

使用循环内的元素

+

Use element inside the Loop

- + - +
@@ -103,9 +103,9 @@
- - - + + +
字段名示例值操作Field NameExample ValueOperations
@@ -115,22 +115,37 @@
{{paras.parameters[i-1]["exampleValues"][0]["value"]}} - 修改 - 删除 - 上移 - 下移 + Modify + Delete + Up + Down
- -

使用相对循环内的XPATH

+ +

Use relative XPATH

- + + + + + - +
@@ -140,10 +155,10 @@
-

使用循环内的文本

+

Use text inside the Loop

- +
@@ -165,31 +180,40 @@
- +
- +
- +
- + - + + + + + +
@@ -197,22 +221,22 @@
- + - +
- +
- +
@@ -225,20 +249,20 @@ `; @@ -199,7 +199,7 @@ function branchMouseDown(e) { parentId: 0, type: 3, option: 10, - title: "条件分支", + title: "Condition", sequence: [], isInLoop: false, }; @@ -232,7 +232,7 @@ function branchClick(e) { parentId: 0, type: 3, option: 10, - title: "条件分支", + title: "Condition", sequence: [], isInLoop: false, }; @@ -269,7 +269,7 @@ function arrowClick(e) { function addElement(op, para) { option = op; if (option == 1) { //打开网页选项 - title = "打开网页"; + title = "Open Page"; } else { title = $(".options")[option - 2].innerHTML; //获取新增操作名称 } @@ -287,7 +287,7 @@ function toolBoxKernel(e, para = null) { if (nowNode == null) { e.stopPropagation(); //防止冒泡 } else if (nowNode.getAttribute("dataType") > 0) { - alert("循环和判断、条件分支不可复制!"); + alert("Cannot copy loop, if and condition!"); e.stopPropagation(); //防止冒泡 } else { let position = parseInt(nowNode.getAttribute('position')); @@ -309,7 +309,7 @@ function toolBoxKernel(e, para = null) { if (nowNode == null) { e.stopPropagation(); //防止冒泡 } else if ($(nowNode).is(".branch")) { - alert("判断分支不可移动!"); + alert("Cannot move condition branch!"); e.stopPropagation(); //防止冒泡 } else { let position = parseInt(nowNode.getAttribute('position')); @@ -341,7 +341,7 @@ function toolBoxKernel(e, para = null) { app._data.nowArrow = { "position": nodeList[element[0]]["position"], "pId": nodeList[element[0]]["parentId"], "num": 0 }; $("#" + nodeList[element[0]]["id"]).click(); } else { - alert("自己不能移动到自己的节点里!"); + alert("Cannot move inside self!"); } e.stopPropagation(); //防止冒泡 } @@ -371,7 +371,7 @@ function toolBoxKernel(e, para = null) { index: l + 1, type: 3, option: 10, - title: "条件分支", + title: "Condition", sequence: [], isInLoop: false, }; @@ -381,7 +381,7 @@ function toolBoxKernel(e, para = null) { index: l + 2, type: 3, option: 10, - title: "条件分支", + title: "Condition", sequence: [], isInLoop: false, }; @@ -426,7 +426,7 @@ $(".options").mousedown(function() { option = parseInt(this.getAttribute("data")); title = this.innerHTML; if (option >= 10 && option <= 12 && (nowNode == null || nowNode.getAttribute("id") == 0)) { - alert("目前未选中元素"); + alert("No element is selected now!"); } else if (option == 12) { deleteElement(); $(".options")[12].click(); @@ -501,13 +501,13 @@ function refresh(nowArrowReset = true) { function deleteElement() { if (nowNode.getAttribute("id") == 0) { - alert("当前未选中元素!"); //root - return; - } - if (nodeList[actionSequence[nowNode.getAttribute("data")]]["option"] == 1) { - alert("打开网页操作不可删除!"); + alert("No element is selected now!"); //root return; } + // if (nodeList[actionSequence[nowNode.getAttribute("data")]]["option"] == 1) { + // alert("Cannot delete the element of Open Page!"); + // return; + // } let position = parseInt(nowNode.getAttribute('position')); let pId = nowNode.getAttribute('pId'); let tnode = nodeList[actionSequence[pId]]["sequence"].splice(position, 1); //在相应位置删除元素 @@ -539,7 +539,7 @@ document.oncontextmenu = function() { //删除元素 document.onkeydown = function(e) { if (nowNode != null && e.keyCode == 46) { - if (confirm("确定要删除元素吗?")) { + if (confirm("Do you really want to delete the selected element?")) { deleteElement(); } } else { //ctrl+s保存服务 diff --git a/ServiceGrid/frontEnd/FlowChart_CN.html b/ServiceGrid/frontEnd/FlowChart_CN.html new file mode 100644 index 0000000..5655d43 --- /dev/null +++ b/ServiceGrid/frontEnd/FlowChart_CN.html @@ -0,0 +1,277 @@ + + + + + + + + + + + + + 设计流程 + + + + + +
+
+
+
工具箱
+ + + + + + + + + + +
-----------------
+ + + + + +
提示:点击上方操作按钮后点击要放置元素的位置处的箭头,可按取消操作按钮取消。
+
+
+
+
+
+
+

+
+
+
+ +
+
+ + +
+ +
+
+ +

使用循环内的链接

+
+
+ + + + +
+ + + + +
+ +
+
+ +

使用循环内的元素

+
+
+ + +
+ + + + + +
+ +
+
+ + + + + + +
字段名示例值操作
+ + + + + + + +
+ {{paras.parameters[i-1]["exampleValues"][0]["value"]}} + 修改 + 删除 + 上移 + 下移 +
+
+
+ +

使用相对循环内的XPATH

+ + + + + + + + + + +
+ +
+ + +
+
+ +

使用循环内的文本

+
+
+ + +
+ + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + +
+ + +
+
+ + +
+
+ + +
+ + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+
+ + +
+ + +
+ + +
+ +
+ + + + + + + + \ No newline at end of file diff --git a/ServiceGrid/frontEnd/FlowChart_CN.js b/ServiceGrid/frontEnd/FlowChart_CN.js new file mode 100644 index 0000000..cabcb0d --- /dev/null +++ b/ServiceGrid/frontEnd/FlowChart_CN.js @@ -0,0 +1,561 @@ +//处理表现层 +var nodeList = Array(); //所有新生成的节点全部存储在这里,并且有唯一索引号,所有的定位均通过index进行,即将图保存下来了 +var root = { + index: 0, //在nodeList中的索引号 + id: 0, + parentId: 0, + type: -1, + option: 0, + title: "root", + sequence: [], + parameters: { + history: 1, + tabIndex: 0, + useLoop: false, //是否使用循环中的元素 + xpath: "", //xpath + wait: 0, + }, + isInLoop: false, //是否处于循环内 +}; +nodeList.push(root); +var queue = new Array(); +var actionSequence = new Array(); //存储图结构,每个元素为在nodelist里面的索引值,下面的id和pid根据此数组进行索引,然后再在nodelist里找 +var nowNode = null; //存储现在所在的节点 +var vueData = { nowNodeIndex: 0 }; //存储目前所在节点的索引号,不能直接使用变量而需要用对象包起来 +var option = 0; //工具箱选项 +var title = ""; +var parameterNum = 1; //记录目前的参数个数 + +//处理逻辑层 +var app = new Vue({ + el: '#app', + data: { + list: { nl: nodeList }, + index: vueData, + nodeType: 0, // 当前元素的类型 + nowNode: null, // 用来临时存储元素的节点 + loopType: -1, //点击循环时候用来循环选项 + useLoop: false, //记录是否使用循环内元素 + nowArrow: { "position": -1, "pId": 0, "num": 0 }, + paras: { "parameters": [] }, //提取数据的参数列表 + TClass: -1, //条件分支的条件类别 + paraIndex: 0, //当前参数的index + }, + watch: { + nowArrow: { //变量发生变化的时候进行一些操作 + deep: true, + handler: function(newVal, oldVal) { + let arrlist = document.getElementsByClassName("arrow"); + if (oldVal != null) { + for (let i = 0; i < arrlist.length; i++) { + if (arrlist[i].getAttribute("position") == oldVal["position"] && + arrlist[i].getAttribute("pid") == oldVal["pId"]) { + arrlist[i].style.backgroundColor = ""; // 时刻指示现在应该插入的节点的位置 + break; + } + } + } + for (let i = 0; i < arrlist.length; i++) { + if (arrlist[i].getAttribute("position") == newVal["position"] && + arrlist[i].getAttribute("pid") == newVal["pId"]) { + arrlist[i].style.backgroundColor = "lavender"; // 时刻指示现在应该插入的节点的位置 + break; + } + } + } + }, + loopType: { + handler: function(newVal, oldVal) { + this.nowNode["parameters"]["loopType"] = newVal; + } + }, + TClass: { + handler: function(newVal, oldVal) { + this.nowNode["parameters"]["class"] = newVal; + } + }, + useLoop: { + handler: function(newVal, oldVal) { + this.nowNode["parameters"]["useLoop"] = newVal; + } + }, + paras: { + handler: function(newVal, oldVal) { + this.nowNode["parameters"]["paras"] = newVal["parameters"]; + } + }, + }, + methods: { + modifyParas: function(i) { //修改第i个参数 + this.paraIndex = i; + }, + deleteParas: function(i) { //删除第i个参数 + this.nowNode["parameters"]["paras"].splice(i, 1); + //如果参数删除完了,就把提取数据也删掉 + if (this.nowNode["parameters"]["paras"].length == 0) { + deleteElement(); + } + }, + upParas: function(i) { //上移第i个参数 + if (i != 0) { + let t = this.nowNode["parameters"]["paras"].splice(i, 1)[0]; + this.nowNode["parameters"]["paras"].splice(i - 1, 0, t); + } + }, + downParas: function(i) { //下移第i个参数 + if (i != this.nowNode["parameters"]["paras"].length - 1) { + let t = this.nowNode["parameters"]["paras"].splice(i, 1)[0]; + this.nowNode["parameters"]["paras"].splice(i + 1, 0, t); + } + }, + + getType: function(nodeType, contentType) { //根据类型得到字段名称 + if (contentType == 2) { + return "InnerHTML"; + } else if (contentType == 3) { + return "OuterHTML"; + } + if (nodeType == 2) { + return "链接地址"; + } else if (nodeType == 1) { + return "链接文本"; + } else if (nodeType == 4) { + return "图片地址"; + } else { + return "文本"; + } + } + } +}) + +//深复制 +function DeepClone(obj) { + if (obj === null || typeof obj !== 'object') return obj; + var cpObj = obj instanceof Array ? [] : {}; + for (var key in obj) cpObj[key] = DeepClone(obj[key]); + return cpObj; +} + +// 根据元素类型返回不同元素的样式 +function newNode(node) { + id = node["id"]; + title = node["title"]; + type = node["type"]; + if (type == 0) //顺序 + { + return `
+
+

${title}

+
+
+

`; + } else if (type == 1) //循环 + { + return `
+

${title}

+

+
+

`; + } else if (type == 2) //判断 + { + return `
+

${title}

+

点击此处在最左边增加条件分支

+
+
+

`; + } else //判断分支 + { + return `
+

${title}

+

+
+
`; + } +} + +function elementMousedown(e) { + if (e.button == 2) //右键点击 + { + if (nowNode != null) { + nowNode.style.borderColor = "skyblue"; + } + nowNode = this; + vueData.nowNodeIndex = actionSequence[this.getAttribute("data")]; + this.style.borderColor = "blue"; + handleElement(); //处理元素 + } + e.stopPropagation(); //防止冒泡 +} + +function branchMouseDown(e) { + if (e.button == 2) //右键点击 + { + let judgeId = this.getAttribute('data'); + var l = nodeList.length; + var t = { + index: l, + id: 0, + parentId: 0, + type: 3, + option: 10, + title: "条件分支", + sequence: [], + isInLoop: false, + }; + addParameters(t) + nodeList.push(t); + nodeList[actionSequence[judgeId]]["sequence"].splice(0, 0, t.index); + refresh(); + app._data.nowArrow = { "position": -1, "pId": t["id"], "num": 0 }; + $("#" + t["id"]).click(); + } + e.stopPropagation(); //防止冒泡 +} + +function arrowMouseDown(e) { + if (e.button == 2) //右键点击 + { + if (option != 0) { + app._data.nowArrow = { "position": this.getAttribute('position'), "pId": this.getAttribute('pId'), "num": 0 }; + } + toolBoxKernel(e); + } +} +//增加分支点击事件 +function branchClick(e) { + let judgeId = this.getAttribute('data'); + var l = nodeList.length; + var t = { + index: l, + id: 0, + parentId: 0, + type: 3, + option: 10, + title: "条件分支", + sequence: [], + isInLoop: false, + }; + addParameters(t); + nodeList.push(t); + nodeList[actionSequence[judgeId]]["sequence"].splice(0, 0, t.index); + refresh(); + app._data.nowArrow = { "position": -1, "pId": t["id"], "num": 0 }; + $("#" + t["id"]).click(); + e.stopPropagation(); //防止冒泡 +} + +//元素点击事件 +function elementClick(e) { + if (nowNode != null) { + nowNode.style.borderColor = "skyblue"; + } + nowNode = this; + vueData.nowNodeIndex = actionSequence[this.getAttribute("data")]; + this.style.borderColor = "blue"; + handleElement(); //处理元素 + e.stopPropagation(); //防止冒泡 +} + +//箭头点击事件 +function arrowClick(e) { + if (option != 0) { + app._data.nowArrow = { "position": this.getAttribute('position'), "pId": this.getAttribute('pId'), "num": 0 }; + } + toolBoxKernel(e); +} + +//增加元素函数 +function addElement(op, para) { + option = op; + if (option == 1) { //打开网页选项 + title = "打开网页"; + } else { + title = $(".options")[option - 2].innerHTML; //获取新增操作名称 + } + + toolBoxKernel(null, para); +} + +// 工具箱操作函数 +function toolBoxKernel(e, para = null) { + if (option == 13) { //调整锚点 + // let tarrow = DeepClone(app.$data.nowArrow); + // refresh(); + // app._data.nowArrow =tarrow; + } else if (option == 11) { //复制操作 + if (nowNode == null) { + e.stopPropagation(); //防止冒泡 + } else if (nowNode.getAttribute("dataType") > 0) { + alert("循环和判断、条件分支不可复制!"); + e.stopPropagation(); //防止冒泡 + } else { + let position = parseInt(nowNode.getAttribute('position')); + let pId = nowNode.getAttribute('pId'); + var tt = nodeList[nodeList[actionSequence[pId]]["sequence"][position]]; //在相应位置添加新元素 + t = DeepClone(tt); //浅复制元素 + var l = nodeList.length; + t.index = l; + nodeList.push(t); + var position2 = parseInt(app._data.nowArrow['position']); + var pId2 = app._data.nowArrow['pId']; + nodeList[actionSequence[pId2]]["sequence"].splice(position2 + 1, 0, t.index); //在相应位置添加新元素 + refresh(); //重新渲染页面 + app._data.nowArrow = { "position": t["position"], "pId": t["parentId"], "num": 0 }; + $("#" + t["id"]).click(); //复制后点击复制后的元素 + e.stopPropagation(); //防止冒泡 + } + } else if (option == 10) { //剪切操作 + if (nowNode == null) { + e.stopPropagation(); //防止冒泡 + } else if ($(nowNode).is(".branch")) { + alert("判断分支不可移动!"); + e.stopPropagation(); //防止冒泡 + } else { + let position = parseInt(nowNode.getAttribute('position')); + let pId = nowNode.getAttribute('pId'); + var position2 = parseInt(app._data.nowArrow['position']); + var pId2 = app._data.nowArrow['pId']; + var id = nowNode.getAttribute('data'); + var pidt = pId2; + var move = true; + console.log(pidt, id); + while (pidt != 0) { + if (pidt == id) { + move = false; + break; + } + pidt = nodeList[actionSequence[pidt]]["parentId"]; + } + if (move) //如果自己要移动到自己节点里就不允许移动 + { + let element = nodeList[actionSequence[pId]]["sequence"].splice(position, 1); //在相应位置删除元素 + if (pId == pId2 && position < position2) //如果要移动的位置属于同一层并且是从前往后移动,注意需要控制数组插入位置向前错位 + { + position2--; + } + console.log(element); + nodeList[actionSequence[pId2]]["sequence"].splice(position2 + 1, 0, element[0]); //在相应位置添加新元素 + refresh(); //重新渲染页面 + console.log(nodeList[element[0]]); + app._data.nowArrow = { "position": nodeList[element[0]]["position"], "pId": nodeList[element[0]]["parentId"], "num": 0 }; + $("#" + nodeList[element[0]]["id"]).click(); + } else { + alert("自己不能移动到自己的节点里!"); + } + e.stopPropagation(); //防止冒泡 + } + } else if (option > 0) { //新增操作 + var l = nodeList.length; + var t = { + id: 0, + index: l, + parentId: 0, + type: 0, + option: option, + title: title, + sequence: [], + isInLoop: false, + }; + nodeList.push(t); + if (option == 8) //循环 + { + t["type"] = 1; + } else if (option == 9) //判断 + { + t["type"] = 2; + // 增加两个分支 + var nt = { + id: 0, + parentId: 0, + index: l + 1, + type: 3, + option: 10, + title: "条件分支", + sequence: [], + isInLoop: false, + }; + var nt2 = { + id: 0, + parentId: 0, + index: l + 2, + type: 3, + option: 10, + title: "条件分支", + sequence: [], + isInLoop: false, + }; + t["sequence"].push(nt.index); + t["sequence"].push(nt2.index); + nodeList.push(nt) + nodeList.push(nt2); + addParameters(nt); //增加选项的默认参数 + addParameters(nt2); //增加选项的默认参数 + } + let position = parseInt(app._data.nowArrow['position']); + let pId = app._data.nowArrow['pId']; + nodeList[actionSequence[pId]]["sequence"].splice(position + 1, 0, t.index); //在相应位置添加新元素 + refresh(); //重新渲染页面 + //下面是确定添加元素之后下一个要插入的节点的位置 + app._data.nowArrow = { "position": t["position"], "pId": t["parentId"], "num": 0 }; + addParameters(t); //增加选项的默认参数 + if (para != null) { + modifyParameters(t, para); + } + if (option == 8) //循环情况下应插入在循环里面 + { + app._data.nowArrow = { "position": -1, "pId": t["id"], "num": 0 }; + $("#" + t["id"]).click(); + } else if (option == 9) //判断插入到第一个判断条件中 + { + app._data.nowArrow = { "position": -1, "pId": nt["id"], "num": 0 }; + $("#" + nt["id"]).click(); + } else { + $("#" + t["id"]).click(); + } + + if (e != null) + e.stopPropagation(); //防止冒泡 + option = 0; + return t; + } + option = 0; +} + +$(".options").mousedown(function() { + option = parseInt(this.getAttribute("data")); + title = this.innerHTML; + if (option >= 10 && option <= 12 && (nowNode == null || nowNode.getAttribute("id") == 0)) { + alert("目前未选中元素"); + } else if (option == 12) { + deleteElement(); + $(".options")[12].click(); + } +}); + +function bindEvents() { + // 清空原来的listener然后再添加新的listener + //以下绑定了左右键的行为 + let rect = document.getElementsByClassName('clk'); + for (let i = 0, rule; rule = rect[i++];) { + rule.removeEventListener('mousedown', elementMousedown); + rule.addEventListener('mousedown', elementMousedown); + rule.removeEventListener('click', elementClick); + rule.addEventListener('click', elementClick); + } + let arr = document.getElementsByClassName('arrow'); + for (let i = 0, rule; rule = arr[i++];) { + rule.removeEventListener('click', arrowClick); + rule.addEventListener('click', arrowClick); + rule.removeEventListener('mousedown', arrowMouseDown); + rule.addEventListener('mousedown', arrowMouseDown); + } + let branch = document.getElementsByClassName('branchAdd'); + for (let i = 0, rule; rule = branch[i++];) { + rule.removeEventListener('click', branchClick); + rule.addEventListener('click', branchClick); + rule.removeEventListener('mousedown', branchMouseDown); + rule.addEventListener('mousedown', branchMouseDown); + } +} + +//重新画图 +function refresh(nowArrowReset = true) { + $("#0").empty(); + $("#0").append(`
+

+
+

`); + actionSequence.splice(0); + queue.splice(0); + var idd = 1; + queue.push(0); + actionSequence.push(0); + while (queue.length != 0) { + var nd = queue.shift(); //取出父元素并建立对子元素的链接 + for (i = 0; i < nodeList[nd].sequence.length; i++) { + nodeList[nodeList[nd].sequence[i]].parentId = nodeList[nd].id; + nodeList[nodeList[nd].sequence[i]]["position"] = i; + nodeList[nodeList[nd].sequence[i]].id = idd++; + //检测元素是否位于循环内 + if (nodeList[nd].option == 8 || nodeList[nd].isInLoop) { + nodeList[nodeList[nd].sequence[i]].isInLoop = true; + } else { + nodeList[nodeList[nd].sequence[i]].isInLoop = false; + } + queue.push(nodeList[nd].sequence[i]); + actionSequence.push(nodeList[nd].sequence[i]); + } + } + if (nowArrowReset) //如果要重置锚点位置 + { + app._data.nowArrow = { "position": -1, "pId": 0, "num": 0 }; //设置默认要添加的位置是元素流程最开头处 + } + //第一个元素不渲染 + for (i = 1; i < actionSequence.length; i++) { + parentId = nodeList[actionSequence[i]]["parentId"]; + $("#" + parentId).append(newNode(nodeList[actionSequence[i]])); + } + bindEvents(); +} + +function deleteElement() { + if (nowNode.getAttribute("id") == 0) { + alert("当前未选中元素!"); //root + return; + } + // if (nodeList[actionSequence[nowNode.getAttribute("data")]]["option"] == 1) { + // alert("打开网页操作不可删除!"); + // return; + // } + let position = parseInt(nowNode.getAttribute('position')); + let pId = nowNode.getAttribute('pId'); + let tnode = nodeList[actionSequence[pId]]["sequence"].splice(position, 1); //在相应位置删除元素 + //循环的标记已经被删除的元素,因为删除循环后,循环内的元素也会 + let queue = new Array(); + queue.push(tnode[0]); + while (queue.length > 0) { + let index = queue.shift(); + nodeList[index]["id"] = -1; //标记服务已被删除 + for (let i = 0; i < nodeList[index]["sequence"].length; i++) { + queue.push(nodeList[index]["sequence"][i]); + } + } + app._data["nowNode"] = null; + app._data["nodeType"] = 0; + vueData.nowNodeIndex = 0; + if (nowNode.getAttribute("datatype") == 3) { //如果删掉的是条件分支的话 + pId = nowNode.parentNode.parentNode.getAttribute('pId'); + position = nowNode.parentNode.parentNode.getAttribute('position'); + } + app.$data.nowArrow = { position: position - 1, "pId": pId, "num": 0 }; //删除元素后锚点跳转到当前元素的上一个节点 + refresh(false); //重新渲染页面 + nowNode = null; //取消选择 +} + +document.oncontextmenu = function() { + return false; + } //屏蔽右键菜单 + //删除元素 +document.onkeydown = function(e) { + if (nowNode != null && e.keyCode == 46) { + if (confirm("确定要删除元素吗?")) { + deleteElement(); + } + } else { //ctrl+s保存服务 + var currKey = 0, + e = e || event || window.event; + currKey = e.keyCode || e.which || e.charCode; + if (currKey == 83 && (e.ctrlKey || e.metaKey)) { + $('#save').click(); + return false; + } + + } +} + +function inputDelete(e) { + if (e.keyCode == 46) { + e.stopPropagation(); //输入框按delete应该正常运行 + } +} \ No newline at end of file diff --git a/ServiceGrid/frontEnd/invokeService.html b/ServiceGrid/frontEnd/invokeService.html index ab31b5b..2a34ce6 100644 --- a/ServiceGrid/frontEnd/invokeService.html +++ b/ServiceGrid/frontEnd/invokeService.html @@ -8,7 +8,7 @@ - 服务调用 + Service Invoke