mirror of
https://github.com/NaiboWang/EasySpider.git
synced 2025-04-12 03:27:08 +08:00
English Version
This commit is contained in:
parent
08dc87d8f0
commit
b73a2a9954
3
.temp_to_pub/.gitignore
vendored
3
.temp_to_pub/.gitignore
vendored
@ -4,4 +4,5 @@ EasySpider.app/
|
||||
EasySpider_windows_x64/user_data
|
||||
*.tmp
|
||||
*.7z*
|
||||
config.json
|
||||
config.json
|
||||
mysql_config.json
|
@ -48,6 +48,8 @@ if __name__ == "__main__":
|
||||
if os.path.exists("./EasySpider_windows_x64/user_data"):
|
||||
shutil.rmtree("./EasySpider_windows_x64/user_data")
|
||||
shutil.rmtree("./EasySpider_windows_x64/Data")
|
||||
shutil.rmtree("./EasySpider_windows_x64/config.json")
|
||||
shutil.rmtree("./EasySpider_windows_x64/mysql_config.json")
|
||||
shutil.rmtree("./EasySpider_windows_x64/execution_instances")
|
||||
os.mkdir("./EasySpider_windows_x64/Data")
|
||||
os.mkdir("./EasySpider_windows_x64/execution_instances")
|
||||
@ -61,6 +63,8 @@ if __name__ == "__main__":
|
||||
shutil.rmtree("./EasySpider_windows_x86/user_data")
|
||||
shutil.rmtree("./EasySpider_windows_x86/Data")
|
||||
shutil.rmtree("./EasySpider_windows_x86/execution_instances")
|
||||
shutil.rmtree("./EasySpider_windows_x86/config.json")
|
||||
shutil.rmtree("./EasySpider_windows_x86/mysql_config.json")
|
||||
os.mkdir("./EasySpider_windows_x86/Data")
|
||||
os.mkdir("./EasySpider_windows_x86/execution_instances")
|
||||
compress_folder_to_7z("./EasySpider_windows_x64", file_name)
|
||||
@ -71,6 +75,8 @@ if __name__ == "__main__":
|
||||
shutil.rmtree("./EasySpider_Linux_x64/user_data")
|
||||
shutil.rmtree("./EasySpider_Linux_x64/Data")
|
||||
shutil.rmtree("./EasySpider_Linux_x64/execution_instances")
|
||||
shutil.rmtree("./EasySpider_Linux_x64/config.json")
|
||||
shutil.rmtree("./EasySpider_Linux_x64/mysql_config.json")
|
||||
os.mkdir("./EasySpider_Linux_x64/Data")
|
||||
os.mkdir("./EasySpider_Linux_x64/execution_instances")
|
||||
# compress_folder_to_7z("./EasySpider_Linux_x64", file_name)
|
||||
|
@ -39,11 +39,11 @@ if __name__ == "__main__":
|
||||
file_path = "../.temp_to_pub/compress.py"
|
||||
update_file_version(file_path, version, key='easyspider_version = "')
|
||||
|
||||
file_path = "./src/taskGrid/logic.js"
|
||||
file_path = "./src/taskGrid/logic_deprecated.js"
|
||||
update_file_version(file_path, version, key='"version": "')
|
||||
|
||||
file_path = "./src/taskGrid/logic_CN.js"
|
||||
update_file_version(file_path, version, key='"version": "')
|
||||
# file_path = "./src/taskGrid/logic.js"
|
||||
# update_file_version(file_path, version, key='"version": "')
|
||||
|
||||
file_path = "../ExecuteStage/easyspider_executestage.py"
|
||||
update_file_version(file_path, version, key='"version": "')
|
||||
|
7
ElectronJS/detect_chinese.py
Normal file
7
ElectronJS/detect_chinese.py
Normal file
@ -0,0 +1,7 @@
|
||||
import re
|
||||
|
||||
with open('src/taskGrid/FlowChart.js', 'r', encoding='utf-8') as file:
|
||||
for line in file:
|
||||
line = re.split('//', line)[0]
|
||||
if re.search('[\u4e00-\u9fff]', line):
|
||||
print(line)
|
@ -93,7 +93,7 @@ function createWindow() {
|
||||
|
||||
// and load the index.html of the app.
|
||||
// mainWindow.loadFile('src/index.html');
|
||||
mainWindow.loadURL(server_address + '/index.html?user_data_folder=' + config.user_data_folder);
|
||||
mainWindow.loadURL(server_address + '/index.html?user_data_folder=' + config.user_data_folder, { extraHeaders: 'pragma: no-cache\n' });
|
||||
// 隐藏菜单栏
|
||||
const {Menu} = require('electron');
|
||||
Menu.setApplicationMenu(null);
|
||||
@ -118,7 +118,7 @@ async function beginInvoke(msg, ws) {
|
||||
url = server_address + `/taskGrid/FlowChart.html?id=${msg.message.id}&wsport=${websocket_port}&backEndAddressServiceWrapper=` + server_address;
|
||||
}
|
||||
console.log(url);
|
||||
flowchart_window.loadURL(url);
|
||||
flowchart_window.loadURL(url, { extraHeaders: 'pragma: no-cache\n' });
|
||||
}
|
||||
mainWindow.hide();
|
||||
// Prints the currently focused window bounds.
|
||||
@ -475,7 +475,7 @@ function handleOpenBrowser(event, lang = "en", user_data_folder = "", mobile = f
|
||||
url = server_address + `/taskGrid/FlowChart_CN.html?id=${id}&wsport=${websocket_port}&backEndAddressServiceWrapper=` + server_address+ "&mobile=" + mobile.toString();
|
||||
}
|
||||
// and load the index.html of the app.
|
||||
flowchart_window.loadURL(url);
|
||||
flowchart_window.loadURL(url, { extraHeaders: 'pragma: no-cache\n' });
|
||||
if(process.platform != "darwin"){
|
||||
flowchart_window.hide();
|
||||
}
|
||||
@ -495,7 +495,7 @@ function handleOpenInvoke(event, lang = "en") {
|
||||
url = server_address + `/taskGrid/taskList.html?type=1&wsport=${websocket_port}&backEndAddressServiceWrapper=` + server_address + "&lang=zh";
|
||||
}
|
||||
// and load the index.html of the app.
|
||||
window.loadURL(url);
|
||||
window.loadURL(url, { extraHeaders: 'pragma: no-cache\n' });
|
||||
window.maximize();
|
||||
mainWindow.hide();
|
||||
window.on('close', function (event) {
|
||||
|
@ -95,7 +95,7 @@
|
||||
style="margin-top: 15px; width: 300px;height:60px;padding-top:12px;color:white">Start Data Mode</a>
|
||||
</p>
|
||||
|
||||
<a @click="step = 0" class="btn btn-outline-primary btn-lg"style="margin-top: 10px; width: 322px;height:45px;padding-top:5px">Go to Home Page</a>
|
||||
<a @click="step = 0" class="btn btn-outline-primary btn-lg"style="margin-top: 10px; width: 302px;height:45px;padding-top:5px">Go to Home Page</a>
|
||||
|
||||
</div>
|
||||
<div v-else-if="step == 2">
|
||||
@ -111,13 +111,13 @@
|
||||
</div>
|
||||
<p><a @click="startDesign('en', true)"
|
||||
class="btn btn-primary btn-lg"
|
||||
style="margin-top: 15px; width: 320px;height:60px;padding-top:12px;color:white">Start Design</a></p>
|
||||
style="margin-top: 15px; width: 300px;height:60px;padding-top:12px;color:white">Start Design</a></p>
|
||||
<p>
|
||||
<p><a @click="startDesign('en', true, true)"
|
||||
class="btn btn-primary btn-lg"
|
||||
style="margin-top: 15px; width: 320px;height:60px;padding-top:12px;color:white">Start Design (Mobile)</a></p>
|
||||
style="margin-top: 15px; width: 300px;height:60px;padding-top:12px;color:white">Start Design (Mobile)</a></p>
|
||||
<p>
|
||||
<a @click="step = 0" class="btn btn-outline-primary btn-lg"style="margin-top: 10px; width: 322px;height:45px;padding-top:5px">Go to Home Page</a>
|
||||
<a @click="step = 0" class="btn btn-outline-primary btn-lg"style="margin-top: 10px; width: 302px;height:45px;padding-top:5px">Go to Home Page</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,8 +18,8 @@
|
||||
<div id="tip" class="alert alert-success alert-dismissible fade show" style="position: fixed; width:100%;display: none;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button> Hint: Save Successfully!
|
||||
</div>
|
||||
<div id="tip2" class="alert alert-danger alert-dismissible fade show" style="position: fixed; width:100%;display: none;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button> Tip: Save failed, if you have multiple custom actions, their option names must be set to different names!
|
||||
<div id="tipError" class="alert alert-danger alert-dismissible fade show" style="position: fixed; width:100%;display: none;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button> <span id="error_message">提示:保存名不符合MySQL表名规范,请重试!</span>
|
||||
</div>
|
||||
<div id="navigator">
|
||||
<nav aria-label="breadcrumb" v-if="type==1">
|
||||
@ -115,6 +115,21 @@
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
|
||||
<label>Wait time after scrolling (in seconds): </label>
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollWaitTime']" type="number" required></input>
|
||||
<p style="margin-top: 10px">
|
||||
<a class="btn btn-primary" data-toggle="collapse" href="#collapseOpenPage" role="button" aria-expanded="false" aria-controls="collapseExample">
|
||||
Click here to expand/collapse advanced operations
|
||||
</a>
|
||||
</p>
|
||||
<div :class="{collapse: true, 'show': nowNode['parameters']['cookies'].length!=0}" id="collapseOpenPage">
|
||||
<div>
|
||||
<label>Set Cookies after page loaded: </label>
|
||||
<p style="margin-bottom: 20px;color:white"><a class="btn btn-primary" @click="getCookies">
|
||||
Click here to get cookies of current page
|
||||
</a></p>
|
||||
<textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
|
||||
placeholder='key=value, one pair per line' v-model='nowNode["parameters"]["cookies"]' id="pageCookies" style="font-size: 14px!important;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="elements" v-if="nodeType==2">
|
||||
@ -221,6 +236,19 @@
|
||||
<input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='paras.parameters[paraIndex]["afterJSWaitTime"]'></input>
|
||||
</div>
|
||||
</div>
|
||||
<label>Parameter type conversion (for Excel and Database):</label>
|
||||
<select v-model='paras.parameters[paraIndex]["paraType"]' class="form-control">
|
||||
<option value = "text">Text (for single values estimated to exceed 10,000 in length, please choose Large Text)</option>
|
||||
<option value = "int">Integer (up to 9 digits)</option>
|
||||
<option value = "double">Floating Number (Decimal)</option>
|
||||
<option value = "mediumText">Large Text (single value length exceeding 10,000 but less than 1,000,000)</option>
|
||||
<option value = "datetime">Date Time</option>
|
||||
<option value = "date">Date</option>
|
||||
<option value = "time">Time</option>
|
||||
<option value = "varchar">Small Text (single value length less than 50)</option>
|
||||
<option value = "longText">Extra Large Text (single value length exceeding 1,000,000)</option>
|
||||
<option value = "bigInt">Large Integer (more than 9 digits)</option>
|
||||
</select>
|
||||
<label>Extract Type</label>
|
||||
<select v-model='paras.parameters[paraIndex]["contentType"]' class="form-control">
|
||||
<option :value = 0>Text (include child element)</option>
|
||||
@ -232,7 +260,8 @@
|
||||
<option :value = 6>Webpage Title</option>
|
||||
<option :value = 7>Element Screenshot</option>
|
||||
<option :value = 8>OCR Results</option>
|
||||
<option :value = 9>The return value after executing JavaScript script on this element (start with 'return ')</option>
|
||||
<option :value = 9>Return value of JavaScript code (for this element), starting with 'return')</option>
|
||||
<option :value = 12>System command return value</option>
|
||||
<option :value = 10>Selected value of the current select box</option>
|
||||
<option :value = 11>Selected text of the current select box</option>
|
||||
</select>
|
||||
@ -263,6 +292,11 @@
|
||||
<!-- <option :value = 0>普通提取</option>-->
|
||||
<!-- <option :value = 1>OCR提取</option>-->
|
||||
<!-- </select>-->
|
||||
<label style="margin-top: 15px">Whether to save this field: (Choose 'No' if you only want to treat this field as a variable and not save it):</label>
|
||||
<select v-model='paras.parameters[paraIndex]["recordASField"]' class="form-control">
|
||||
<option :value = 1>Yes</option>
|
||||
<option :value = 0>No</option>
|
||||
</select>
|
||||
<label>Parameter Description:</label>
|
||||
<textarea onkeydown="inputDelete(event)" class="form-control" style="min-height: 60px" v-model='paras.parameters[paraIndex]["desc"]'></textarea>
|
||||
<label>Default value when cannot find this element:</label>
|
||||
@ -310,13 +344,15 @@
|
||||
<div class="elements" v-if="nodeType==5">
|
||||
<p><input onkeydown="inputDelete(event)" type="checkbox" v-model='nowNode["parameters"]["iframe"]'></input>Action is inside iframe</p>
|
||||
<label>Custom Action Mode</label>
|
||||
<select v-model='nowNode["parameters"]["codeMode"]' class="form-control">
|
||||
<select v-model='codeMode' class="form-control">
|
||||
<option value = 0>Execute JavaScript script</option>
|
||||
<option value = 1>Execute operating system-level command</option>
|
||||
<option v-if="nowNode['isInLoop']" value = 2>Execute JavaScript script for the current element inside the loop</option>
|
||||
<option v-if="nowNode['isInLoop']" value = 3>Exit Current Loop (the "Break" operation)</option>
|
||||
|
||||
</select>
|
||||
|
||||
<div>
|
||||
<div v-if='nowNode["parameters"]["codeMode"] < 3'>
|
||||
<label>Code (Use Field["FieldName"] to input the last extracted value of a field): </label>
|
||||
<textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["code"]' placeholder="Please input a JavaScript command or a system command. For example, document.body.innerText = '1' is an example of a JavaScript command, and python D:/test.py is an example of a system command. If you choose to execute a JavaScript script for the current iteration, you can represent the element of the current iteration using arguments[0]. For instance, arguments[0].style.color = 'blue' sets the color of the element in the current iteration to blue."></textarea>
|
||||
<p style="margin-top: 15px">Whether to record the output/return value of the execution as a field: </p>
|
||||
@ -324,6 +360,20 @@
|
||||
<option value = 0>No</option>
|
||||
<option value = 1>Yes</option>
|
||||
</select></p>
|
||||
<p><label>Convert parameter type to:</label>
|
||||
<select v-model='paras.parameters[paraIndex]["paraType"]' class="form-control">
|
||||
<option value = "text">Text (for single values estimated to exceed 10,000 in length, please choose Large Text)</option>
|
||||
<option value = "int">Integer (up to 9 digits)</option>
|
||||
<option value = "double">Floating Number (Decimal)</option>
|
||||
<option value = "mediumText">Large Text (single value length exceeding 10,000 but less than 1,000,000)</option>
|
||||
<option value = "datetime">Date Time</option>
|
||||
<option value = "date">Date</option>
|
||||
<option value = "time">Time</option>
|
||||
<option value = "varchar">Small Text (single value length less than 50)</option>
|
||||
<option value = "longText">Extra Large Text (single value length exceeding 1,000,000)</option>
|
||||
<option value = "bigInt">Large Integer (more than 9 digits)</option>
|
||||
</select>
|
||||
</p>
|
||||
<label>Maximum wait time for script execution (0 represents unlimited wait time): </label>
|
||||
<input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["waitTime"]'></input>
|
||||
</div>
|
||||
@ -403,19 +453,6 @@
|
||||
<label v-if='parseInt(loopType) == 0'>Max Loop time(0 means infinite):</label>
|
||||
<input onkeydown="inputDelete(event)" required v-if='parseInt(loopType) == 0' class="form-control" type="number" v-model.number='nowNode["parameters"]["exitCount"]'></input>
|
||||
|
||||
|
||||
<label>Waiting time in seconds after a history record rollback: </label>
|
||||
<input onkeydown="inputDelete(event)" required type="number" class="form-control" v-model.number='list.nl[index.nowNodeIndex]["parameters"]["historyWait"]'></input>
|
||||
<label>After executed, whether scroll down:</label>
|
||||
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
|
||||
<option value = 0>No Scrolling</option>
|
||||
<option value = 1>Scroll one screen</option>
|
||||
<option value = 2>Scroll to the end</option>
|
||||
</select>
|
||||
<label>Scroll Times:</label>
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
|
||||
<label>Wait time after scrolling (in seconds):</label>
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollWaitTime']" type="number" required></input>
|
||||
<div id="breakAdvanced" v-if='nowNode["parameters"]["loopType"] < 5'>
|
||||
<div>
|
||||
<p><label>(Advanced Operation) Define loop exit condition using code/script:</label></p>
|
||||
@ -432,6 +469,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label>Waiting time in seconds after a history record rollback: </label>
|
||||
<input onkeydown="inputDelete(event)" required type="number" class="form-control" v-model.number='list.nl[index.nowNodeIndex]["parameters"]["historyWait"]'></input>
|
||||
<label>After executed, whether scroll down:</label>
|
||||
<select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
|
||||
<option value = 0>No Scrolling</option>
|
||||
<option value = 1>Scroll one screen</option>
|
||||
<option value = 2>Scroll to the end</option>
|
||||
</select>
|
||||
<label>Scroll Times:</label>
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
|
||||
<label>Wait time after scrolling (in seconds):</label>
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollWaitTime']" type="number" required></input>
|
||||
</div>
|
||||
|
||||
<div class="elements" v-if="nodeType==9">
|
||||
@ -452,6 +501,7 @@
|
||||
<option value = 6>Return value of system command</option>
|
||||
<option v-if="nowNode['isInLoop']" value = 7>Return value of JavaScript command for the current loop item</option>
|
||||
</select>
|
||||
|
||||
<div v-if='TClass > 0 && TClass < 5'>
|
||||
<label>Text/Element XPath to Include: <span style="font-size: 30px!important;" title="Relative XPath syntax: starts with /, e.g., if the XPath of the loop item is /html/body/div[1], and you input /*[@id='tab-customer'], the final XPath will be: /html/body/div[1]/*[@id='tab-customer']">☺</span></label>
|
||||
<textarea onkeydown="inputDelete(event)" required placeholder="If the current loop contains elements, input the xpath of the relative element (such as '/div[2]/div[1]/img', if written in relative path, it should be written as '/*//img', which means checking whether there exists an 'img' tag among all the descendant elements of the current loop item.)." class="form-control" rows="3" v-model='nowNode["parameters"]["value"]'></textarea>
|
||||
@ -468,7 +518,6 @@
|
||||
<label>Maximum wait time for script execution (0 represents unlimited wait time): </label>
|
||||
<input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["waitTime"]'></input>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="margin-top:5px">
|
||||
<label>Seconds <b>after executed</b> (Can be set to a decimal, such as 0.5):</label>
|
||||
@ -495,25 +544,38 @@
|
||||
<h4 class="modal-title" id="myModalLabel">Save Task</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body" style="height:400px;overflow: auto">
|
||||
<input onkeydown="inputDelete(event)" id="serviceId" type="hidden" name="serviceId" value="-1"></input>
|
||||
<input onkeydown="inputDelete(event)" id="url" type="hidden" name="url" value="about:blank"></input>
|
||||
<input id="create_time" type="hidden"></input>
|
||||
<label>Task Name:</label>
|
||||
<input onkeydown="inputDelete(event)" required name="serviceName" value="New Crawler Task" id="serviceName" class="form-control"></input>
|
||||
<input onkeydown="inputDelete(event)" required name="serviceName" value="New Web Scraping Task" id="serviceName" class="form-control"></input>
|
||||
<label>Task Description:</label>
|
||||
<input onkeydown="inputDelete(event)" id="serviceDescription" name="serviceDescription" class="form-control"></input>
|
||||
<label>How many data to save each time (the larger the value, the faster the collection speed, but there is a risk of data loss):</label>
|
||||
<input onkeydown="inputDelete(event)" type="number" value="10" id="saveThreshold" name="saveThreshold" class="form-control"></input>
|
||||
<label>Is the page an extreme anti-crawler website such as Cloudflare:</label>
|
||||
<label>Export Data Format (Excel/CSV/TXT/Database):</label>
|
||||
<select id="outputFormat" class="form-control">
|
||||
<option value = "xlsx">XLSX (EXCEL)</option>
|
||||
<option value = "csv">CSV</option>
|
||||
<option value = "txt">TXT</option>
|
||||
<option value = "mysql">MySQL Database</option>
|
||||
</select>
|
||||
<label>Export File Name/Database Table Name (The keyword "current_time" will be replaced with the timestamp when the task is executed):</label>
|
||||
<input onkeydown="inputDelete(event)" value="current_time" id="saveName" class="form-control"></input>
|
||||
<label>Is it an extreme anti-scraping website like Cloudflare?</label>
|
||||
<select id="cloudflare" name="cloudflare" class="form-control">
|
||||
<option value = 0>No</option>
|
||||
<option value = 1>Yes</option>
|
||||
<option value=0>No</option>
|
||||
<option value=1>Yes</option>
|
||||
</select>
|
||||
<label>Browser simulation type:</label>
|
||||
<label>Browser Emulation Type:</label>
|
||||
<select id="environment" name="environment" class="form-control">
|
||||
<option value = 0>Personal Computer</option>
|
||||
<option value = 1>Mobile (not support on Cloudflare mode)</option>
|
||||
<option value=0>Desktop</option>
|
||||
<option value=1>Mobile (Not supported under Cloudflare mode)</option>
|
||||
</select>
|
||||
<label>Save Data Every N Rows (The larger the value, the faster the scraping speed, but there is a risk of data loss if unexpectedly exited):</label>
|
||||
<input onkeydown="inputDelete(event)" type="number" value="10" id="saveThreshold" name="saveThreshold" class="form-control"></input>
|
||||
<label>Maximum Display Length of Data in Console Preview:</label>
|
||||
<input onkeydown="inputDelete(event)" type="number" value="15" id="maxViewLength" class="form-control"></input>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" id="saveAsButton" class="btn btn-outline-primary">Save as</button>
|
||||
@ -527,9 +589,10 @@
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
<script src="FlowChart.js"></script>
|
||||
<script src="logic.js"></script>
|
||||
<script src="global.js"></script>
|
||||
<script src="logic.js"></script>
|
||||
|
||||
<script>
|
||||
var navigator = new Vue({
|
||||
|
@ -14,6 +14,7 @@ let root = {
|
||||
useLoop: false, //是否使用循环中的元素
|
||||
xpath: "", //xpath
|
||||
wait: 0,
|
||||
waitType: 0,
|
||||
},
|
||||
isInLoop: false, //是否处于循环内
|
||||
};
|
||||
@ -26,6 +27,8 @@ let option = 0; //工具箱选项
|
||||
let title = "";
|
||||
let parameterNum = 1; //记录目前的参数个数
|
||||
|
||||
// window.resizeTo( screen.availWidth, screen.availHeight );
|
||||
|
||||
//处理逻辑层
|
||||
let app = new Vue({
|
||||
el: '#app',
|
||||
@ -34,13 +37,14 @@ let app = new Vue({
|
||||
index: vueData,
|
||||
nodeType: 0, // 当前元素的类型
|
||||
nowNode: null, // 用来临时存储元素的节点
|
||||
codeMode: 0, //代码模式
|
||||
loopType: -1, //点击循环时候用来循环选项
|
||||
useLoop: false, //记录是否使用循环内元素
|
||||
nowArrow: { "position": -1, "pId": 0, "num": 0 },
|
||||
paras: { "parameters": [] }, //提取数据的参数列表
|
||||
TClass: -1, //条件分支的条件类别
|
||||
paraIndex: 0, //当前参数的index
|
||||
XPaths: "",
|
||||
XPaths: "", //xpath列表
|
||||
},
|
||||
watch: {
|
||||
nowArrow: { //变量发生变化的时候进行一些操作
|
||||
@ -65,7 +69,7 @@ let app = new Vue({
|
||||
}
|
||||
}
|
||||
},
|
||||
loopType: {
|
||||
loopType: { //循环类型发生变化的时候更新参数值
|
||||
handler: function(newVal, oldVal) {
|
||||
this.nowNode["parameters"]["loopType"] = newVal;
|
||||
}
|
||||
@ -85,8 +89,22 @@ let app = new Vue({
|
||||
this.nowNode["parameters"]["paras"] = newVal["parameters"];
|
||||
}
|
||||
},
|
||||
codeMode: {
|
||||
handler: function(newVal, oldVal) {
|
||||
this.nowNode["parameters"]["codeMode"] = newVal;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCookies: function() { //获取cookies
|
||||
let command = new WebSocket("ws://localhost:"+getUrlParam("wsport"))
|
||||
command.onopen = function() {
|
||||
let message = {
|
||||
type: 7, //消息类型,0代表连接操作
|
||||
};
|
||||
this.send(JSON.stringify(message));
|
||||
};
|
||||
},
|
||||
changeXPaths: function (XPaths){
|
||||
let result = "";
|
||||
for (let i = 0; i < XPaths.length; i++) {
|
||||
@ -99,30 +117,35 @@ let app = new Vue({
|
||||
"nodeType": 0,
|
||||
"contentType": 0,
|
||||
"relative": false,
|
||||
"name": "Custom_Field_" + this.nowNode["parameters"]["paras"].length.toString(),
|
||||
"name": LANG("自定义参数_" + this.nowNode["parameters"]["paras"].length.toString(),"Custom_Field_" + this.nowNode["parameters"]["paras"].length.toString()),
|
||||
"desc": "",
|
||||
"extractType": 0,
|
||||
"relativeXPath": "",
|
||||
"relativeXPath": "//body",
|
||||
"recordASField": 1,
|
||||
"allXPaths": [],
|
||||
"exampleValues": [
|
||||
{
|
||||
"num": 0,
|
||||
"value": "Custom_Value_0"
|
||||
"value": LANG("自定义值", "Custom_Value")
|
||||
}
|
||||
],
|
||||
"default": "",
|
||||
"beforeJS": "",
|
||||
"beforeJSWaitTime": 0,
|
||||
"JS": "",
|
||||
"paraType": "text",
|
||||
"JSWaitTime": 0,
|
||||
"afterJS": "",
|
||||
"afterJSWaitTime": 0,
|
||||
"downloadPic": 0
|
||||
});
|
||||
this.paraIndex = this.nowNode["parameters"]["paras"].length - 1;
|
||||
setTimeout(function(){$("#app > div.elements > div.toolkitcontain > table.toolkittb4 > tbody > tr:last-child")[0].scrollIntoView(false); //滚动到底部
|
||||
}, 200);
|
||||
},
|
||||
modifyParas: function(i) { //修改第i个参数
|
||||
this.paraIndex = i;
|
||||
console.log(this.paras);
|
||||
},
|
||||
deleteParas: function(i) { //删除第i个参数
|
||||
this.nowNode["parameters"]["paras"].splice(i, 1);
|
||||
@ -151,13 +174,13 @@ let app = new Vue({
|
||||
return "OuterHTML";
|
||||
}
|
||||
if (nodeType == 2) {
|
||||
return "Link Address";
|
||||
return LANG("链接地址", "Link Address");
|
||||
} else if (nodeType == 1) {
|
||||
return "Link Text";
|
||||
return LANG("链接文本","Link Text");
|
||||
} else if (nodeType == 4) {
|
||||
return "Image Address";
|
||||
return LANG("图片地址","Image Address");
|
||||
} else {
|
||||
return "Text";
|
||||
return LANG("文本","Text");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,12 +216,18 @@ function newNode(node) {
|
||||
<p class="arrow" data = "${id}" position=${node["position"]} pId=${node["parentId"]}>↓</p></div>`;
|
||||
} else if (type == 2) //判断
|
||||
{
|
||||
return `<div class="loop clk" dataType=${type} data="${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
return LANG(`<div class="loop clk" dataType=${type} data="${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
<p style="background:#d6d6d6;text-align:left;padding:2px">${title}</p>
|
||||
<p class="branchAdd" data="${id}">点击此处在最左边增加条件分支</p>
|
||||
<div class="judge" id = "${id}">
|
||||
</div></div>
|
||||
<p class="arrow" data = "${id}" position=${node["position"]} pId=${node["parentId"]}>↓</p></div>`,
|
||||
`<div class="loop clk" dataType=${type} data="${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
<p style="background:#d6d6d6;text-align:left;padding:2px">${title}</p>
|
||||
<p class="branchAdd" data="${id}">Click here to add a new condition to the left most</p>
|
||||
<div class="judge" id = "${id}">
|
||||
</div></div>
|
||||
<p class="arrow" data = "${id}" position=${node["position"]} pId=${node["parentId"]}>↓</p></div>`;
|
||||
<p class="arrow" data = "${id}" position=${node["position"]} pId=${node["parentId"]}>↓</p></div>`);
|
||||
} else //判断分支
|
||||
{
|
||||
return `<div class="branch clk" dataType=${type} data="${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
@ -209,19 +238,7 @@ function newNode(node) {
|
||||
}
|
||||
}
|
||||
|
||||
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) //右键点击
|
||||
@ -234,7 +251,7 @@ function branchMouseDown(e) {
|
||||
parentId: 0,
|
||||
type: 3,
|
||||
option: 10,
|
||||
title: "Condition",
|
||||
title: LANG("条件分支", "Condition"),
|
||||
sequence: [],
|
||||
isInLoop: false,
|
||||
};
|
||||
@ -267,7 +284,7 @@ function branchClick(e) {
|
||||
parentId: 0,
|
||||
type: 3,
|
||||
option: 10,
|
||||
title: "Condition",
|
||||
title: LANG("条件分支", "Condition"),
|
||||
sequence: [],
|
||||
isInLoop: false,
|
||||
};
|
||||
@ -280,6 +297,20 @@ function branchClick(e) {
|
||||
e.stopPropagation(); //防止冒泡
|
||||
}
|
||||
|
||||
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 elementClick(e) {
|
||||
if (nowNode != null) {
|
||||
@ -304,9 +335,8 @@ function arrowClick(e) {
|
||||
function addElement(op, para) {
|
||||
option = op;
|
||||
if (option == 1) { //打开网页选项
|
||||
title = "Open Page";
|
||||
}
|
||||
else {
|
||||
title = LANG("打开网页", "Open Page");
|
||||
} else {
|
||||
title = $(".options")[option - 1].innerHTML; //获取新增操作名称
|
||||
}
|
||||
|
||||
@ -323,7 +353,7 @@ function toolBoxKernel(e, para = null) {
|
||||
if (nowNode == null) {
|
||||
e.stopPropagation(); //防止冒泡
|
||||
} else if (nowNode.getAttribute("dataType") > 0) {
|
||||
alert("Cannot copy loop, if and condition!");
|
||||
showError(LANG("循环和判断、条件分支不可复制!", "Cannot copy loop, if and condition!"));
|
||||
e.stopPropagation(); //防止冒泡
|
||||
} else {
|
||||
let position = parseInt(nowNode.getAttribute('position'));
|
||||
@ -345,7 +375,7 @@ function toolBoxKernel(e, para = null) {
|
||||
if (nowNode == null) {
|
||||
e.stopPropagation(); //防止冒泡
|
||||
} else if ($(nowNode).is(".branch")) {
|
||||
alert("Cannot move condition branch!");
|
||||
showError(LANG("判断分支不可移动!", "Cannot move condition branch!"));
|
||||
e.stopPropagation(); //防止冒泡
|
||||
} else {
|
||||
let position = parseInt(nowNode.getAttribute('position'));
|
||||
@ -377,7 +407,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("Cannot move inside self!");
|
||||
showError(LANG("自己不能移动到自己的节点里!", "Cannot move inside self!"));
|
||||
}
|
||||
e.stopPropagation(); //防止冒泡
|
||||
}
|
||||
@ -407,7 +437,7 @@ function toolBoxKernel(e, para = null) {
|
||||
index: l + 1,
|
||||
type: 3,
|
||||
option: 10,
|
||||
title: "Condition",
|
||||
title: LANG("条件分支", "Condition"),
|
||||
sequence: [],
|
||||
isInLoop: false,
|
||||
};
|
||||
@ -417,7 +447,7 @@ function toolBoxKernel(e, para = null) {
|
||||
index: l + 2,
|
||||
type: 3,
|
||||
option: 10,
|
||||
title: "Condition",
|
||||
title: LANG("条件分支", "Condition"),
|
||||
sequence: [],
|
||||
isInLoop: false,
|
||||
};
|
||||
@ -462,7 +492,7 @@ $(".options").mousedown(function() {
|
||||
option = parseInt(this.getAttribute("data"));
|
||||
title = this.innerHTML;
|
||||
if (option >= 10 && option <= 12 && (nowNode == null || nowNode.getAttribute("id") == 0)) {
|
||||
alert("No element is selected now!");
|
||||
showError(LANG("目前未选中元素。", "No element selected。"));
|
||||
} else if (option == 12) {
|
||||
deleteElement();
|
||||
$(".options")[12].click();
|
||||
@ -537,11 +567,11 @@ function refresh(nowArrowReset = true) {
|
||||
|
||||
function deleteElement() {
|
||||
if (nowNode.getAttribute("id") == 0) {
|
||||
alert("No element is selected now!"); //root
|
||||
showError(LANG("当前未选中元素!", "No element is selected now!"));
|
||||
return;
|
||||
}
|
||||
// if (nodeList[actionSequence[nowNode.getAttribute("data")]]["option"] == 1) {
|
||||
// alert("Cannot delete the element of Open Page!");
|
||||
// showError("打开网页操作不可删除!");
|
||||
// return;
|
||||
// }
|
||||
let position = parseInt(nowNode.getAttribute('position'));
|
||||
@ -575,7 +605,7 @@ document.oncontextmenu = function() {
|
||||
//删除元素
|
||||
document.onkeydown = function(e) {
|
||||
if (nowNode != null && e.keyCode == 46) {
|
||||
// if (confirm("Do you really want to delete the selected operation?")) {
|
||||
// if (confirm("确定要删除元素吗?")) {
|
||||
deleteElement();
|
||||
// }
|
||||
} else { //ctrl+s保存服务
|
||||
@ -588,7 +618,7 @@ document.onkeydown = function(e) {
|
||||
location.reload();
|
||||
} else if (currKey == 123) {
|
||||
console.log("打开devtools")
|
||||
let command = new WebSocket("ws://localhost:8084")
|
||||
let command = new WebSocket("ws://localhost:"+getUrlParam("wsport"))
|
||||
command.onopen = function() {
|
||||
let message = {
|
||||
type: 6, //消息类型,0代表连接操作
|
||||
@ -602,5 +632,6 @@ document.onkeydown = function(e) {
|
||||
function inputDelete(e) {
|
||||
if (e.keyCode == 46) {
|
||||
e.stopPropagation(); //输入框按delete应该正常运行
|
||||
//Electron中如果有showError或者confirm,执行后会卡死输入框,所以最好不要用
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,9 @@
|
||||
<div id="tip" class="alert alert-success alert-dismissible fade show" style="position: fixed; width:100%;display: none;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button> 提示:保存成功!
|
||||
</div>
|
||||
<div id="tipMySQL" class="alert alert-danger alert-dismissible fade show" style="position: fixed; width:100%;display: none;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button> 提示:保存名不符合MySQL表名规范,请重试!
|
||||
<div id="tipError" class="alert alert-danger alert-dismissible fade show" style="position: fixed; width:100%;display: none;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button> <span id="error_message">提示:保存名不符合MySQL表名规范,请重试!</span>
|
||||
</div>
|
||||
|
||||
<div id="navigator">
|
||||
<nav aria-label="breadcrumb" v-if="type==1">
|
||||
<ol class="breadcrumb" style="padding-left:23px;padding-bottom: 0;margin-bottom:0;background-color: white">
|
||||
@ -37,7 +36,6 @@
|
||||
<div style="width: 200px;float:left;overflow: auto">
|
||||
<div class="toolbox" style="text-align:center;margin: 20px;border-radius:10px;border:navy solid;background-color:rgb(242,243,245);z-index: 9999;">
|
||||
<div style="padding: 10px;border-radius:10px;font-size: 20px;">工具箱</div>
|
||||
<!-- window.resizeTo( screen.availWidth, screen.availHeight );-->
|
||||
<button type="button" id="save" data-toggle="modal" data-target="#myModal" onmousedown="$('#myModal').modal('show');" class="btn btn-primary">保存任务</button>
|
||||
<button type="button" data=1 class="btn btn-outline-primary options">打开网页</button>
|
||||
<button type="button" data=2 class="btn btn-outline-primary options">点击元素</button>
|
||||
@ -117,7 +115,6 @@
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollCount']" type="number" required></input>
|
||||
<label>滚动后等待时间(秒):</label>
|
||||
<input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['scrollWaitTime']" type="number" required></input>
|
||||
|
||||
<p style="margin-top: 10px">
|
||||
<a class="btn btn-primary" data-toggle="collapse" href="#collapseOpenPage" role="button" aria-expanded="false" aria-controls="collapseExample">
|
||||
点此展开/折叠高级操作
|
||||
@ -133,7 +130,6 @@
|
||||
placeholder='key=value形式,每行一个键值对' v-model='nowNode["parameters"]["cookies"]' id="pageCookies" style="font-size: 14px!important;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="elements" v-if="nodeType==2">
|
||||
@ -506,7 +502,7 @@
|
||||
<option v-if="nowNode['isInLoop']" value = 7>针对当前循环项的JavaScript命令返回值(需以return 开头)</option>
|
||||
</select>
|
||||
|
||||
<div v-if='TClass>0 && TClass <5'>
|
||||
<div v-if='TClass > 0 && TClass < 5'>
|
||||
<label>包含的文字/元素XPATH: <span style="font-size: 30px!important;" title="相对XPATH写法:以/开头,如循环项XPATH为/html/body/div[1],您的输入为/*[@id='tab-customer'],则最终寻址的xpath为:/html/body/div[1]/*[@id='tab-customer']">☺</span></label>
|
||||
<textarea onkeydown="inputDelete(event)" required placeholder="如果是当前循环包含元素,则输入相对元素的xpath(如/div[2]/div[1]/img,如果写相对路径,需要写成/*//img,即检测当前循环项所有的子孙元素是否存在img标签)。" class="form-control" rows="3" v-model='nowNode["parameters"]["value"]'></textarea>
|
||||
</div>
|
||||
@ -551,6 +547,7 @@
|
||||
<div class="modal-body" style="height:400px;overflow: auto">
|
||||
<input onkeydown="inputDelete(event)" id="serviceId" type="hidden" name="serviceId" value="-1"></input>
|
||||
<input onkeydown="inputDelete(event)" id="url" type="hidden" name="url" value="about:blank"></input>
|
||||
<input id="create_time" type="hidden"></input>
|
||||
<label>任务名称:</label>
|
||||
<input onkeydown="inputDelete(event)" required name="serviceName" value="新web采集任务" id="serviceName" class="form-control"></input>
|
||||
<label>任务描述:</label>
|
||||
@ -592,9 +589,10 @@
|
||||
|
||||
|
||||
</body>
|
||||
<script src="FlowChart_CN.js"></script>
|
||||
<script src="logic_CN.js"></script>
|
||||
|
||||
<script src="FlowChart.js"></script>
|
||||
<script src="global.js"></script>
|
||||
<script src="logic.js"></script>
|
||||
|
||||
<script>
|
||||
var navigator = new Vue({
|
||||
|
@ -1,627 +0,0 @@
|
||||
//处理表现层
|
||||
let nodeList = Array(); //所有新生成的节点全部存储在这里,并且有唯一索引号,所有的定位均通过index进行,即将图保存下来了
|
||||
let 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,
|
||||
waitType: 0,
|
||||
},
|
||||
isInLoop: false, //是否处于循环内
|
||||
};
|
||||
nodeList.push(root);
|
||||
let queue = new Array();
|
||||
let actionSequence = new Array(); //存储图结构,每个元素为在nodelist里面的索引值,下面的id和pid根据此数组进行索引,然后再在nodelist里找
|
||||
let nowNode = null; //存储现在所在的节点
|
||||
let vueData = { nowNodeIndex: 0 }; //存储目前所在节点的索引号,不能直接使用变量而需要用对象包起来
|
||||
let option = 0; //工具箱选项
|
||||
let title = "";
|
||||
let parameterNum = 1; //记录目前的参数个数
|
||||
|
||||
//处理逻辑层
|
||||
let app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
list: { nl: nodeList },
|
||||
index: vueData,
|
||||
nodeType: 0, // 当前元素的类型
|
||||
nowNode: null, // 用来临时存储元素的节点
|
||||
codeMode: 0, //代码模式
|
||||
loopType: -1, //点击循环时候用来循环选项
|
||||
useLoop: false, //记录是否使用循环内元素
|
||||
nowArrow: { "position": -1, "pId": 0, "num": 0 },
|
||||
paras: { "parameters": [] }, //提取数据的参数列表
|
||||
TClass: -1, //条件分支的条件类别
|
||||
paraIndex: 0, //当前参数的index
|
||||
XPaths: "", //xpath列表
|
||||
},
|
||||
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"];
|
||||
}
|
||||
},
|
||||
codeMode: {
|
||||
handler: function(newVal, oldVal) {
|
||||
this.nowNode["parameters"]["codeMode"] = newVal;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCookies: function() { //获取cookies
|
||||
let command = new WebSocket("ws://localhost:"+getUrlParam("wsport"))
|
||||
command.onopen = function() {
|
||||
let message = {
|
||||
type: 7, //消息类型,0代表连接操作
|
||||
};
|
||||
this.send(JSON.stringify(message));
|
||||
};
|
||||
},
|
||||
changeXPaths: function (XPaths){
|
||||
let result = "";
|
||||
for (let i = 0; i < XPaths.length; i++) {
|
||||
result += XPaths[i] + "\n";
|
||||
}
|
||||
this.XPaths = result;
|
||||
},
|
||||
addPara: function() { //添加参数
|
||||
this.nowNode["parameters"]["paras"].push({
|
||||
"nodeType": 0,
|
||||
"contentType": 0,
|
||||
"relative": false,
|
||||
"name": "自定义参数_" + this.nowNode["parameters"]["paras"].length.toString(),
|
||||
"desc": "",
|
||||
"extractType": 0,
|
||||
"relativeXPath": "//body",
|
||||
"recordASField": 1,
|
||||
"allXPaths": [],
|
||||
"exampleValues": [
|
||||
{
|
||||
"num": 0,
|
||||
"value": "自定义字段"
|
||||
}
|
||||
],
|
||||
"default": "",
|
||||
"beforeJS": "",
|
||||
"beforeJSWaitTime": 0,
|
||||
"JS": "",
|
||||
"paraType": "text",
|
||||
"JSWaitTime": 0,
|
||||
"afterJS": "",
|
||||
"afterJSWaitTime": 0,
|
||||
"downloadPic": 0
|
||||
});
|
||||
this.paraIndex = this.nowNode["parameters"]["paras"].length - 1;
|
||||
},
|
||||
modifyParas: function(i) { //修改第i个参数
|
||||
this.paraIndex = i;
|
||||
console.log(this.paras);
|
||||
},
|
||||
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;
|
||||
let cpObj = obj instanceof Array ? [] : {};
|
||||
for (let 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 `<div class="sequence"><div class="node clk" data="${id}" dataType=${type} id = "${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
<div >
|
||||
<p>${title}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="arrow" position=${node["position"]} data = "${id}" pId=${node["parentId"]}>↓</p></div>`;
|
||||
} else if (type == 1) //循环
|
||||
{
|
||||
return `<div class="loop clk" data="${id}" dataType=${type} id = "${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
<p style="background:#d6d6d6;text-align:left;padding:2px">${title}</p>
|
||||
<p class="arrow" position=-1 data = "${id}" pId=${id}>↓</p>
|
||||
</div>
|
||||
<p class="arrow" data = "${id}" position=${node["position"]} pId=${node["parentId"]}>↓</p></div>`;
|
||||
} else if (type == 2) //判断
|
||||
{
|
||||
return `<div class="loop clk" dataType=${type} data="${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
<p style="background:#d6d6d6;text-align:left;padding:2px">${title}</p>
|
||||
<p class="branchAdd" data="${id}">点击此处在最左边增加条件分支</p>
|
||||
<div class="judge" id = "${id}">
|
||||
</div></div>
|
||||
<p class="arrow" data = "${id}" position=${node["position"]} pId=${node["parentId"]}>↓</p></div>`;
|
||||
} else //判断分支
|
||||
{
|
||||
return `<div class="branch clk" dataType=${type} data="${id}" position=${node["position"]} pId=${node["parentId"]}>
|
||||
<p style="background:#d6d6d6;text-align:left;padding:2px">${title}</p>
|
||||
<p data = "${id}" class="arrow" position=-1 pId=${id}>↓</p>
|
||||
<div id = "${id}">
|
||||
</div></div>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function branchMouseDown(e) {
|
||||
if (e.button == 2) //右键点击
|
||||
{
|
||||
let judgeId = this.getAttribute('data');
|
||||
let l = nodeList.length;
|
||||
let 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');
|
||||
let l = nodeList.length;
|
||||
let 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 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 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 - 1].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');
|
||||
let tt = nodeList[nodeList[actionSequence[pId]]["sequence"][position]]; //在相应位置添加新元素
|
||||
t = DeepClone(tt); //浅复制元素
|
||||
let l = nodeList.length;
|
||||
t.index = l;
|
||||
nodeList.push(t);
|
||||
let position2 = parseInt(app._data.nowArrow['position']);
|
||||
let 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');
|
||||
let position2 = parseInt(app._data.nowArrow['position']);
|
||||
let pId2 = app._data.nowArrow['pId'];
|
||||
let id = nowNode.getAttribute('data');
|
||||
let pidt = pId2;
|
||||
let 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) { //新增操作
|
||||
let l = nodeList.length;
|
||||
let 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;
|
||||
// 增加两个分支
|
||||
let nt = {
|
||||
id: 0,
|
||||
parentId: 0,
|
||||
index: l + 1,
|
||||
type: 3,
|
||||
option: 10,
|
||||
title: "条件分支",
|
||||
sequence: [],
|
||||
isInLoop: false,
|
||||
};
|
||||
let 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(`<div style="border-radius: 50%;width: 40px;height: 40px;border:solid;border-color:seagreen;margin:5px auto;background-color:lightcyan;margin-top:20px">
|
||||
<p style="font-size: 24px!important;text-align: center;margin-left: 6px;font-family:'Times New Roman'">▶</p>
|
||||
</div>
|
||||
<p id="firstArrow" class="arrow" position=-1 pId=0>↓</p>`);
|
||||
actionSequence.splice(0);
|
||||
queue.splice(0);
|
||||
let idd = 1;
|
||||
queue.push(0);
|
||||
actionSequence.push(0);
|
||||
while (queue.length != 0) {
|
||||
let nd = queue.shift(); //取出父元素并建立对子元素的链接
|
||||
for (let 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": nodeList[0].sequence.length - 1, "pId": 0, "num": 0 }; //设置默认要添加的位置是元素流程最开头处
|
||||
}
|
||||
//第一个元素不渲染
|
||||
for (let i = 1; i < actionSequence.length; i++) {
|
||||
let 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保存服务
|
||||
let currKey = 0;
|
||||
currKey = e.keyCode || e.which || e.charCode;
|
||||
if (currKey == 83 && (e.ctrlKey || e.metaKey)) {
|
||||
$('#save').click();
|
||||
return true;
|
||||
} else if (currKey == 116) {
|
||||
location.reload();
|
||||
} else if (currKey == 123) {
|
||||
console.log("打开devtools")
|
||||
let command = new WebSocket("ws://localhost:"+getUrlParam("wsport"))
|
||||
command.onopen = function() {
|
||||
let message = {
|
||||
type: 6, //消息类型,0代表连接操作
|
||||
};
|
||||
this.send(JSON.stringify(message));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function inputDelete(e) {
|
||||
if (e.keyCode == 46) {
|
||||
e.stopPropagation(); //输入框按delete应该正常运行
|
||||
//Electron中如果有alert或者confirm,执行后会卡死输入框,所以最好不要用
|
||||
}
|
||||
}
|
@ -1,3 +1,26 @@
|
||||
// function isExtract() { //检测当前锚点之前的元素是否为提取数据字段
|
||||
// if (app.$data.nowArrow["position"] == -1) {
|
||||
// return false;
|
||||
// } else if (nodeList[nodeList[app.$data.nowArrow["pId"]].sequence[app.$data.nowArrow["position"]]]["option"] == 3) {
|
||||
// return true;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
function DateFormat(datetime) {
|
||||
let date = new Date(datetime);
|
||||
|
||||
// Format the date and time
|
||||
let formatted = date.getFullYear() +
|
||||
'-' + String(date.getMonth() + 1).padStart(2, '0') +
|
||||
'-' + String(date.getDate()).padStart(2, '0') +
|
||||
' ' + String(date.getHours()).padStart(2, '0') +
|
||||
':' + String(date.getMinutes()).padStart(2, '0') +
|
||||
':' + String(date.getSeconds()).padStart(2, '0');
|
||||
return formatted;
|
||||
}
|
||||
|
||||
function getUrlParam(name) {
|
||||
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
|
||||
let r = window.location.search.substr(1).match(reg); //匹配目标参数
|
||||
@ -11,4 +34,11 @@ Vue.filter('lang', function (value) {
|
||||
} else {
|
||||
return value.split("~")[0];
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function isValidMySQLTableName(tableName) {
|
||||
// 正则表达式:以字母或汉字开头,后接字母、数字、下划线或汉字的字符串,长度为1到64字符
|
||||
const pattern = /^[\u4e00-\u9fa5a-zA-Z][\u4e00-\u9fa5a-zA-Z0-9_]{0,63}$/;
|
||||
return pattern.test(tableName);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
exampleMsg = { //示例消息
|
||||
let exampleMsg = { //示例消息
|
||||
"type": 0, //消息类型,1代表增加操作
|
||||
"data": {
|
||||
"option": 1, //增加选项
|
||||
@ -8,7 +8,7 @@ exampleMsg = { //示例消息
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify(exampleMsg));
|
||||
ws = new WebSocket("ws://localhost:"+getUrlParam("wsport"));
|
||||
let ws = new WebSocket("ws://localhost:"+getUrlParam("wsport"));
|
||||
ws.onopen = function() {
|
||||
// Web Socket 已连接上,使用 send() 方法发送数据
|
||||
console.log("已连接");
|
||||
@ -36,11 +36,12 @@ ws.onmessage = function(evt) {
|
||||
} else {
|
||||
handleAddElement(evt); //处理增加元素操作
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function changeGetDataParameters(msg, i) {
|
||||
msg["parameters"][i]["default"] = ""; //找不到元素时候的默认值
|
||||
msg["parameters"][i]["paraType"] = "text"; //参数类型
|
||||
msg["parameters"][i]["recordASField"] = 1; //是否记录为字段值
|
||||
msg["parameters"][i]["beforeJS"] = ""; //执行前执行的js
|
||||
msg["parameters"][i]["beforeJSWaitTime"] = 0; //执行前js等待时间
|
||||
msg["parameters"][i]["JS"] = ""; //如果是JS,需要执行的js
|
||||
@ -48,19 +49,9 @@ function changeGetDataParameters(msg, i) {
|
||||
msg["parameters"][i]["afterJS"] = ""; //执行后执行的js
|
||||
msg["parameters"][i]["afterJSWaitTime"] = 0; //执行后js等待时间
|
||||
msg["parameters"][i]["downloadPic"] = 0; //是否下载图片
|
||||
msg["parameters"][i]["iframe"] = false; //是否在iframe中
|
||||
}
|
||||
|
||||
|
||||
function extractTitle(html) {
|
||||
var match = html.match(/<title[^>]*>([^<]+)<\/title>/i);
|
||||
if (match && match[1]) {
|
||||
return "Collect" + match[1];
|
||||
} else {
|
||||
return "New Web Collection Task";
|
||||
}
|
||||
}
|
||||
|
||||
function handleAddElement(msg) {
|
||||
if (msg["type"] == "openPage") {
|
||||
addElement(1, msg);
|
||||
@ -83,7 +74,7 @@ function handleAddElement(msg) {
|
||||
addElement(8, msg);
|
||||
addElement(2, msg);
|
||||
} else if (msg["type"] == "singleCollect" || msg["type"] == "multiCollectNoPattern") {
|
||||
if (app._data.nowNode != null && app._data["nowNode"]["option"] == 3) { //如果当前点击的动作就是提取数据
|
||||
if (app._data.nowNode != null && app._data["nowNode"]["option"] == 3) { //如果现在节点就是提取数据节点,直接在此节点添加参数,而不是生成一个新的提取数据节点
|
||||
for (let i = 0; i < msg["parameters"].length; i++) {
|
||||
changeGetDataParameters(msg, i);
|
||||
app._data["nowNode"]["parameters"]["paras"].push(msg["parameters"][i]);
|
||||
@ -97,10 +88,17 @@ function handleAddElement(msg) {
|
||||
addElement(8, msg);
|
||||
addElement(3, msg);
|
||||
notifyParameterNum(msg["parameters"].length); //通知浏览器端参数的个数变化
|
||||
} else if(msg["type"] == "GetCookies"){
|
||||
for(let node of nodeList){
|
||||
if(node["option"] == 1){
|
||||
node["parameters"]["cookies"] = msg["message"];
|
||||
$("#pageCookies").val(msg["message"]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function notifyParameterNum(num) {
|
||||
parameterNum += num;
|
||||
let message = {
|
||||
@ -110,17 +108,9 @@ function notifyParameterNum(num) {
|
||||
};
|
||||
window.ws.send(JSON.stringify(message));
|
||||
}
|
||||
// function isExtract() { //检测当前锚点之前的元素是否为提取数据字段
|
||||
// if (app.$data.nowArrow["position"] == -1) {
|
||||
// return false;
|
||||
// } else if (nodeList[nodeList[app.$data.nowArrow["pId"]].sequence[app.$data.nowArrow["position"]]]["option"] == 3) {
|
||||
// return true;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// 流程图元素点击后的处理逻辑
|
||||
|
||||
// 流程图元素点击后的处理逻辑,注意在FlowChart_CN.js中watch的那些数据的加载都需要在这里执行!!!
|
||||
function handleElement() {
|
||||
app._data["nowNode"] = nodeList[vueData.nowNodeIndex];
|
||||
app._data["nodeType"] = app._data["nowNode"]["option"];
|
||||
@ -130,6 +120,8 @@ function handleElement() {
|
||||
} else if (app._data["nodeType"] == 3) {
|
||||
app._data.paraIndex = 0; //参数索引初始化
|
||||
app._data.paras.parameters = app._data["nowNode"]["parameters"]["paras"];
|
||||
} else if(app._data["nodeType"] == 5){
|
||||
app._data.codeMode = app._data["nowNode"]["parameters"]["codeMode"];
|
||||
} else if (app._data["nodeType"] == 10) {
|
||||
app._data.TClass = app._data["nowNode"]["parameters"]["class"];
|
||||
}
|
||||
@ -149,7 +141,6 @@ function addParameters(t) {
|
||||
beforeJSWaitTime: 0, //执行前js等待时间
|
||||
afterJS: "", //执行后执行的js
|
||||
afterJSWaitTime: 0, //执行后js等待时间
|
||||
iframe: false, //是否在iframe中
|
||||
}; //公共参数处理
|
||||
if (t.option == 1) {
|
||||
t["parameters"]["url"] = "about:blank";
|
||||
@ -158,6 +149,7 @@ function addParameters(t) {
|
||||
t["parameters"]["scrollType"] = 0; //滚动类型,0不滚动,1向下滚动1屏,2滚动到底部
|
||||
t["parameters"]["scrollCount"] = 1; //滚动次数
|
||||
t["parameters"]["scrollWaitTime"] = 1; //滚动后等待时间
|
||||
t["parameters"]["cookies"] = ""; //cookies
|
||||
} else if (t.option == 2) { //点击元素
|
||||
t["parameters"]["scrollType"] = 0; //滚动类型,0不滚动,1向下滚动1屏,2滚动到底部
|
||||
t["parameters"]["scrollCount"] = 1; //滚动次数
|
||||
@ -183,6 +175,7 @@ function addParameters(t) {
|
||||
t["parameters"]["code"] = "";
|
||||
t["parameters"]["waitTime"] = 0; //最长等待时间
|
||||
t["parameters"]["recordASField"] = 0; //是否记录脚本输出
|
||||
t["parameters"]["paraType"] = "text"; //记录脚本输出的字段索引
|
||||
} else if (t.option == 8) { //循环
|
||||
t["parameters"]["scrollType"] = 0; //滚动类型,0不滚动,1向下滚动1屏,2滚动到底部
|
||||
t["parameters"]["scrollCount"] = 1; //滚动次数
|
||||
@ -208,7 +201,23 @@ function addParameters(t) {
|
||||
}
|
||||
}
|
||||
|
||||
//修改元素参数
|
||||
|
||||
function updateUI() {
|
||||
refresh(false);
|
||||
app.$data.nowArrow["num"]++; //改变元素的值,通知画图,重新对锚点画图
|
||||
let tnodes = document.getElementsByClassName("clk");
|
||||
let position = nodeList[vueData.nowNodeIndex]["position"];
|
||||
let pid = nodeList[vueData.nowNodeIndex]["parentId"];
|
||||
for (let i = 0; i < tnodes.length; i++) {
|
||||
if (position == tnodes[i].getAttribute("position") && pid == tnodes[i].getAttribute("pId")) {
|
||||
tnodes[i].style.borderColor = "blue"; // 点击了确定按钮之后需要重新对选中的颜色画框
|
||||
nowNode = tnodes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//修改元素参数,注意所有socket传过来的参数都需要在这里赋值给操作
|
||||
function modifyParameters(t, para) {
|
||||
t["parameters"]["history"] = para["history"];
|
||||
t["parameters"]["tabIndex"] = para["tabIndex"];
|
||||
@ -240,9 +249,9 @@ function modifyParameters(t, para) {
|
||||
t["parameters"]["xpath"] = para["xpath"];
|
||||
t["parameters"]["allXPaths"] = para["allXPaths"];
|
||||
if (para["nextPage"]) { //循环点击下一页的情况下
|
||||
t["title"] = "Loop click next page"
|
||||
t["title"] = LANG("循环点击下一页", "Loop click next page");
|
||||
} else {
|
||||
t["title"] = "Loop"
|
||||
t["title"] = LANG("循环", "Loop");
|
||||
}
|
||||
if (para["loopType"] == 2) //如果是固定元素列表
|
||||
{
|
||||
@ -256,43 +265,41 @@ function modifyParameters(t, para) {
|
||||
}
|
||||
}
|
||||
|
||||
//点击确定按钮时的处理
|
||||
$("#confirm").mousedown(function() {
|
||||
refresh(false);
|
||||
app.$data.nowArrow["num"]++; //改变元素的值,通知画图,重新对锚点画图
|
||||
let tnodes = document.getElementsByClassName("clk");
|
||||
let position = nodeList[vueData.nowNodeIndex]["position"];
|
||||
let pid = nodeList[vueData.nowNodeIndex]["parentId"];
|
||||
for (let i = 0; i < tnodes.length; i++) {
|
||||
if (position == tnodes[i].getAttribute("position") && pid == tnodes[i].getAttribute("pId")) {
|
||||
tnodes[i].style.borderColor = "blue"; // 点击了确定按钮之后需要重新对选中的颜色画框
|
||||
nowNode = tnodes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//获取url中的参数
|
||||
function getUrlParam(name) {
|
||||
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
|
||||
var r = window.location.search.substr(1).match(reg); //匹配目标参数
|
||||
if (r != null) return unescape(r[2]);
|
||||
return "";
|
||||
function showError(msg, time=4000) {
|
||||
$("#error_message").text(msg);
|
||||
$("#tipError").slideDown(); //提示框
|
||||
let fadeout = setTimeout(function() {
|
||||
$("#tipError").slideUp();
|
||||
}, time);
|
||||
}
|
||||
|
||||
var sId = getUrlParam('id');
|
||||
var backEndAddressServiceWrapper = getUrlParam("backEndAddressServiceWrapper");
|
||||
//点击确定按钮时的处理
|
||||
$("#confirm").mousedown(updateUI);
|
||||
|
||||
//点击保存任务按钮时的处理
|
||||
$("#saveButton").mousedown(function() {
|
||||
saveService(0);
|
||||
});
|
||||
//点击另存为任务按钮时的处理
|
||||
$("#saveAsButton").mousedown(function() {
|
||||
saveService(1);
|
||||
});
|
||||
|
||||
let sId = getUrlParam('id');
|
||||
let backEndAddressServiceWrapper = getUrlParam("backEndAddressServiceWrapper");
|
||||
let mobile = getUrlParam("mobile");
|
||||
if (mobile == "true") {
|
||||
$("#environment").val(1);
|
||||
}
|
||||
|
||||
|
||||
function saveService(type) {
|
||||
let serviceId = $("#serviceId").val();
|
||||
let text = "Confirm to save this task (If cannot click, can press Enter)? ";
|
||||
let text = LANG("确认要保存任务吗(不能用鼠标点击时,请按键盘回车键)?", "Are you sure to save the task (if you can't use the mouse to click, please press the enter key)?");
|
||||
if (type == 1) { //任务另存为
|
||||
serviceId = -1;
|
||||
text = "Confirm to save as another task in the system (If cannot click, can press Enter)?";
|
||||
$("#create_time").val(new Date().toLocaleString());
|
||||
text = LANG("确认要另存为任务吗(不能用鼠标点击时,请按键盘回车键)?", "Are you sure to save the task as (if you can't use the mouse to click, please press the enter key)?");
|
||||
}
|
||||
// if (confirm(text)) {
|
||||
let serviceName = $("#serviceName").val();
|
||||
@ -320,8 +327,8 @@ function saveService(type) {
|
||||
nodeId: i, //记录操作位于的节点位置,重要!!!
|
||||
nodeName: nodeList[i]["title"],
|
||||
value: nodeList[i]["parameters"]["links"],
|
||||
desc: "List of URLs to be collected, separated by \\n for multiple lines",
|
||||
type: "string",
|
||||
desc: LANG("要采集的网址列表,多行以\\n分开","List of URLs to be collected, separated by \\n for multiple lines",),
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["links"]
|
||||
});
|
||||
links = nodeList[i]["parameters"]["links"];
|
||||
@ -335,8 +342,8 @@ function saveService(type) {
|
||||
name: "inputText_" + inputIndex++,
|
||||
nodeName: nodeList[i]["title"],
|
||||
nodeId: i,
|
||||
desc: "The text to be entered, such as 'computer' at eBay search box",
|
||||
type: "string",
|
||||
desc: LANG("要输入的文本,如京东搜索框输入:电脑","The text to be entered, such as 'computer' at eBay search box"),
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["value"],
|
||||
value: nodeList[i]["parameters"]["value"],
|
||||
});
|
||||
@ -349,8 +356,8 @@ function saveService(type) {
|
||||
name: "loopText_" + inputIndex++,
|
||||
nodeId: i,
|
||||
nodeName: nodeList[i]["title"],
|
||||
desc:"Text/URL to be entered, multiple lines should be separated by \\n",
|
||||
type: "string",
|
||||
desc: LANG("要输入的文本/网址,多行以\\n分开", "Text/URL to be entered, multiple lines should be separated by \\n"),
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["textList"],
|
||||
value: nodeList[i]["parameters"]["textList"],
|
||||
});
|
||||
@ -360,7 +367,7 @@ function saveService(type) {
|
||||
name: "loopTimes_" + nodeList[i]["title"] + "_" + inputIndex++,
|
||||
nodeId: i,
|
||||
nodeName: nodeList[i]["title"],
|
||||
desc: "Number of loop executions, 0 means unlimited loops (until element not found)",
|
||||
desc: LANG("循环" + nodeList[i]["title"] + "执行的次数(0代表无限循环)", "Number of loop executions for loop "+nodeList[i]["title"]+", 0 means unlimited loops (until element not found)"),
|
||||
type: "int",
|
||||
exampleValue: nodeList[i]["parameters"]["exitCount"],
|
||||
value: nodeList[i]["parameters"]["exitCount"],
|
||||
@ -375,33 +382,35 @@ function saveService(type) {
|
||||
id: outputIndex++,
|
||||
name: nodeList[i]["parameters"]["paras"][j]["name"],
|
||||
desc: nodeList[i]["parameters"]["paras"][j]["desc"],
|
||||
type: "string",
|
||||
type: nodeList[i]["parameters"]["paras"][j]["paraType"],
|
||||
recordASField: nodeList[i]["parameters"]["paras"][j]["recordASField"],
|
||||
exampleValue: nodeList[i]["parameters"]["paras"][j]["exampleValues"][0]["value"],
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 5) //自定义操作
|
||||
{
|
||||
if (nodeList[i]["parameters"]["recordASField"] == 1) {
|
||||
let id = outputIndex++;
|
||||
let title = nodeList[i]["title"];
|
||||
// if (outputNames.indexOf(title) >= 0) { //参数名称已经被添加
|
||||
// $('#myModal').modal('hide');
|
||||
// $("#tip2").slideDown(); //提示框
|
||||
// fadeout = setTimeout(function() {
|
||||
// $("#tip2").slideUp();
|
||||
// }, 5000);
|
||||
// return;
|
||||
// }
|
||||
outputNames.push(title);
|
||||
outputParameters.push({
|
||||
id: id,
|
||||
name: title,
|
||||
desc: "Output of custom action",
|
||||
type: "string",
|
||||
exampleValue: "",
|
||||
});
|
||||
}
|
||||
// if (nodeList[i]["parameters"]["recordASField"] == 1) {
|
||||
let id = outputIndex++;
|
||||
let title = nodeList[i]["title"];
|
||||
// if (outputNames.indexOf(title) >= 0) { //参数名称已经被添加
|
||||
// $('#myModal').modal('hide');
|
||||
// $("#tip2").slideDown(); //提示框
|
||||
// fadeout = setTimeout(function() {
|
||||
// $("#tip2").slideUp();
|
||||
// }, 5000);
|
||||
// return;
|
||||
// }
|
||||
outputNames.push(title);
|
||||
outputParameters.push({
|
||||
id: id,
|
||||
name: title,
|
||||
desc: LANG("自定义操作返回的数据","Output of custom action"),
|
||||
type: nodeList[i]["parameters"]["paraType"],
|
||||
recordASField: nodeList[i]["parameters"]["recordASField"],
|
||||
exampleValue: "",
|
||||
});
|
||||
// }
|
||||
} else if (nodeList[i]["option"] == 9) //条件判断
|
||||
{
|
||||
containJudge = true;
|
||||
@ -413,17 +422,28 @@ function saveService(type) {
|
||||
"name": serviceName,
|
||||
"url": url,
|
||||
"links": links,
|
||||
"create_time": new Date().toLocaleString(),
|
||||
"create_time": parseInt(serviceId) == -1 ? new Date().toLocaleString() : $("#create_time").val(),
|
||||
"update_time": new Date().toLocaleString(),
|
||||
"version": "0.3.5",
|
||||
"saveThreshold": saveThreshold,
|
||||
"cloudflare": cloudflare,
|
||||
"environment": environment,
|
||||
"maxViewLength": parseInt($("#maxViewLength").val()),
|
||||
"outputFormat": $("#outputFormat").val(),
|
||||
"saveName": $("#saveName").val(),
|
||||
"containJudge": containJudge,
|
||||
"desc": serviceDescription,
|
||||
"inputParameters": inputParameters,
|
||||
"outputParameters": outputParameters,
|
||||
"graph": nodeList, //图结构要存储下来
|
||||
};
|
||||
if(serviceInfo.outputFormat=="mysql"){
|
||||
if(!isValidMySQLTableName(serviceInfo.saveName)) {
|
||||
$('#myModal').modal('hide');
|
||||
showError(LANG("提示:保存名不符合MySQL表名规范,请重试!","The save name is not valid for MySQL table name!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
$.post(backEndAddressServiceWrapper + "/manageTask", { paras: JSON.stringify(serviceInfo) },
|
||||
function(result) { $("#serviceId").val(parseInt(result)) });
|
||||
// alert("保存成功!");
|
||||
@ -432,31 +452,42 @@ function saveService(type) {
|
||||
let fadeout = setTimeout(function() {
|
||||
$("#tip").slideUp();
|
||||
}, 2000);
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
//点击保存任务按钮时的处理
|
||||
$("#saveButton").mousedown(function() {
|
||||
saveService(0);
|
||||
});
|
||||
//点击另存为任务按钮时的处理
|
||||
$("#saveAsButton").mousedown(function() {
|
||||
saveService(1);
|
||||
});
|
||||
|
||||
|
||||
if (sId != null && sId != -1) //加载任务
|
||||
{
|
||||
$.get(backEndAddressServiceWrapper + "/queryTask?id=" + sId, function(result) {
|
||||
nodeList = result["graph"];
|
||||
app.$data.list.nl = nodeList;
|
||||
for(let node of nodeList){ //兼容旧版本
|
||||
if(node["option"] == 1){
|
||||
if(!("cookies" in node["parameters"])) {
|
||||
node["parameters"]["cookies"] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
$("#serviceName").val(result["name"]);
|
||||
$("#serviceId").val(result["id"]);
|
||||
$("#url").val(result["url"]);
|
||||
$("#serviceDescription").val(result["desc"]);
|
||||
for(let key of Object.keys(result)){
|
||||
try{
|
||||
$("#"+key).val(result[key]);
|
||||
} catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
});
|
||||
} else {
|
||||
refresh(); //新增任务
|
||||
}
|
||||
|
||||
function LANG(zh, en) {
|
||||
if(window.location.href.indexOf("_CN") != -1){
|
||||
return zh;
|
||||
} else {
|
||||
return en;
|
||||
}
|
||||
}
|
@ -1,511 +0,0 @@
|
||||
exampleMsg = { //示例消息
|
||||
"type": 0, //消息类型,1代表增加操作
|
||||
"data": {
|
||||
"option": 1, //增加选项
|
||||
"parameters": { //传入的参数
|
||||
"url": "https://www.baidu.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify(exampleMsg));
|
||||
ws = new WebSocket("ws://localhost:"+getUrlParam("wsport"));
|
||||
ws.onopen = function() {
|
||||
// Web Socket 已连接上,使用 send() 方法发送数据
|
||||
console.log("已连接");
|
||||
message = {
|
||||
type: 0, //消息类型,0代表链接操作
|
||||
message: {
|
||||
id: 2, //socket id
|
||||
}
|
||||
};
|
||||
this.send(JSON.stringify(message));
|
||||
};
|
||||
ws.onclose = function() {
|
||||
// 关闭 websocket
|
||||
console.log("连接已关闭...");
|
||||
};
|
||||
let old_title = "";
|
||||
ws.onmessage = function(evt) {
|
||||
evt = JSON.parse(evt.data);
|
||||
console.log(evt);
|
||||
if (evt["type"] == "title") { //如果不是特殊处理的话,默认全部是增加元素操作
|
||||
if (old_title == "New Task") { //只记录第一次的title
|
||||
$("#serviceName").val(evt.data.title);
|
||||
}
|
||||
old_title = evt.data.title;
|
||||
} else {
|
||||
handleAddElement(evt); //处理增加元素操作
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function changeGetDataParameters(msg, i) {
|
||||
msg["parameters"][i]["default"] = ""; //找不到元素时候的默认值
|
||||
msg["parameters"][i]["paraType"] = "text"; //参数类型
|
||||
msg["parameters"][i]["recordASField"] = 1; //是否记录为字段值
|
||||
msg["parameters"][i]["beforeJS"] = ""; //执行前执行的js
|
||||
msg["parameters"][i]["beforeJSWaitTime"] = 0; //执行前js等待时间
|
||||
msg["parameters"][i]["JS"] = ""; //如果是JS,需要执行的js
|
||||
msg["parameters"][i]["JSWaitTime"] = 0; //JS等待时间
|
||||
msg["parameters"][i]["afterJS"] = ""; //执行后执行的js
|
||||
msg["parameters"][i]["afterJSWaitTime"] = 0; //执行后js等待时间
|
||||
msg["parameters"][i]["downloadPic"] = 0; //是否下载图片
|
||||
}
|
||||
|
||||
|
||||
function extractTitle(html) {
|
||||
var match = html.match(/<title[^>]*>([^<]+)<\/title>/i);
|
||||
if (match && match[1]) {
|
||||
return "采集" + match[1];
|
||||
} else {
|
||||
return "采集新Web页面";
|
||||
}
|
||||
}
|
||||
|
||||
function handleAddElement(msg) {
|
||||
if (msg["type"] == "openPage") {
|
||||
addElement(1, msg);
|
||||
} else if (msg["type"] == "singleClick") {
|
||||
addElement(2, msg);
|
||||
} else if (msg["type"] == "inputText") {
|
||||
addElement(4, msg);
|
||||
} else if (msg["type"] == "changeOption"){
|
||||
addElement(6, msg);
|
||||
} else if (msg["type"] == "mouseMove") {
|
||||
addElement(7, msg);
|
||||
} else if (msg["type"] == "loopMouseMove") {
|
||||
addElement(8, msg);
|
||||
addElement(7, msg);
|
||||
} else if (msg["type"] == "loopClickSingle") {
|
||||
addElement(8, msg);
|
||||
addElement(2, msg);
|
||||
app._data.nowArrow["position"] = -1; //循环点击单个元素,下一个要插入的位置一般在元素上方
|
||||
} else if (msg["type"] == "loopClickEvery") {
|
||||
addElement(8, msg);
|
||||
addElement(2, msg);
|
||||
} else if (msg["type"] == "singleCollect" || msg["type"] == "multiCollectNoPattern") {
|
||||
if (app._data.nowNode != null && app._data["nowNode"]["option"] == 3) { //如果现在节点就是提取数据节点,直接在此节点添加参数,而不是生成一个新的提取数据节点
|
||||
for (let i = 0; i < msg["parameters"].length; i++) {
|
||||
changeGetDataParameters(msg, i);
|
||||
app._data["nowNode"]["parameters"]["paras"].push(msg["parameters"][i]);
|
||||
}
|
||||
app._data.paras.parameters = app._data["nowNode"]["parameters"]["paras"];
|
||||
} else {
|
||||
addElement(3, msg);
|
||||
}
|
||||
notifyParameterNum(msg["parameters"].length); //通知浏览器端参数的个数变化
|
||||
} else if (msg["type"] == "multiCollectWithPattern") {
|
||||
addElement(8, msg);
|
||||
addElement(3, msg);
|
||||
notifyParameterNum(msg["parameters"].length); //通知浏览器端参数的个数变化
|
||||
} else if(msg["type"] == "GetCookies"){
|
||||
for(let node of nodeList){
|
||||
if(node["option"] == 1){
|
||||
node["parameters"]["cookies"] = msg["message"];
|
||||
$("#pageCookies").val(msg["message"]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function notifyParameterNum(num) {
|
||||
parameterNum += num;
|
||||
let message = {
|
||||
type: 3, //消息类型,3代表元素增加事件
|
||||
from: 1, //0代表从浏览器到流程图,1代表从流程图到浏览器
|
||||
message: { "pipe": JSON.stringify({ "type": 0, "value": parameterNum }) } // {}全选{BS}退格
|
||||
};
|
||||
window.ws.send(JSON.stringify(message));
|
||||
}
|
||||
// function isExtract() { //检测当前锚点之前的元素是否为提取数据字段
|
||||
// if (app.$data.nowArrow["position"] == -1) {
|
||||
// return false;
|
||||
// } else if (nodeList[nodeList[app.$data.nowArrow["pId"]].sequence[app.$data.nowArrow["position"]]]["option"] == 3) {
|
||||
// return true;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// 流程图元素点击后的处理逻辑,注意在FlowChart_CN.js中watch的那些数据的加载都需要在这里执行!!!
|
||||
function handleElement() {
|
||||
app._data["nowNode"] = nodeList[vueData.nowNodeIndex];
|
||||
app._data["nodeType"] = app._data["nowNode"]["option"];
|
||||
app._data.useLoop = app._data["nowNode"]["parameters"]["useLoop"];
|
||||
if (app._data["nodeType"] == 8) {
|
||||
app._data.loopType = app._data["nowNode"]["parameters"]["loopType"];
|
||||
} else if (app._data["nodeType"] == 3) {
|
||||
app._data.paraIndex = 0; //参数索引初始化
|
||||
app._data.paras.parameters = app._data["nowNode"]["parameters"]["paras"];
|
||||
} else if(app._data["nodeType"] == 5){
|
||||
app._data.codeMode = app._data["nowNode"]["parameters"]["codeMode"];
|
||||
} else if (app._data["nodeType"] == 10) {
|
||||
app._data.TClass = app._data["nowNode"]["parameters"]["class"];
|
||||
}
|
||||
}
|
||||
|
||||
// 新增元素时的默认参数处理
|
||||
function addParameters(t) {
|
||||
t["parameters"] = {
|
||||
history: 1,
|
||||
tabIndex: 0,
|
||||
useLoop: false, //是否使用循环中的元素
|
||||
xpath: "", //xpath
|
||||
iframe: false, //是否在iframe中
|
||||
wait: 0, //执行后等待
|
||||
waitType: 0, //等待类型,0代表固定时间,1代表随机等待
|
||||
beforeJS: "", //执行前执行的js
|
||||
beforeJSWaitTime: 0, //执行前js等待时间
|
||||
afterJS: "", //执行后执行的js
|
||||
afterJSWaitTime: 0, //执行后js等待时间
|
||||
}; //公共参数处理
|
||||
if (t.option == 1) {
|
||||
t["parameters"]["url"] = "about:blank";
|
||||
t["parameters"]["links"] = "about:blank";
|
||||
t["parameters"]["maxWaitTime"] = 10; //最长等待时间
|
||||
t["parameters"]["scrollType"] = 0; //滚动类型,0不滚动,1向下滚动1屏,2滚动到底部
|
||||
t["parameters"]["scrollCount"] = 1; //滚动次数
|
||||
t["parameters"]["scrollWaitTime"] = 1; //滚动后等待时间
|
||||
t["parameters"]["cookies"] = ""; //cookies
|
||||
} else if (t.option == 2) { //点击元素
|
||||
t["parameters"]["scrollType"] = 0; //滚动类型,0不滚动,1向下滚动1屏,2滚动到底部
|
||||
t["parameters"]["scrollCount"] = 1; //滚动次数
|
||||
t["parameters"]["scrollWaitTime"] = 1; //滚动后等待时间
|
||||
t["parameters"]["clickWay"] = 0; //点击方式,0代表selenium点击,1代表js点击
|
||||
t["parameters"]["maxWaitTime"] = 10; //最长等待时间
|
||||
t["parameters"]["paras"] = []; //默认参数列表
|
||||
t["parameters"]["wait"] = 2; //点击后等待时间默认2s
|
||||
t["parameters"]["beforeJS"] = ""; //执行前执行的js
|
||||
t["parameters"]["beforeJSWaitTime"] = 0; //执行前js等待时间
|
||||
t["parameters"]["afterJS"] = ""; //执行后执行的js
|
||||
t["parameters"]["afterJSWaitTime"] = 0; //执行后js等待时间
|
||||
} else if (t.option == 3) { //提取数据
|
||||
t["parameters"]["paras"] = []; //默认参数列表
|
||||
} else if (t.option == 4) { //输入文字
|
||||
t["parameters"]["value"] = "";
|
||||
t["parameters"]["beforeJS"] = ""; //执行前执行的js
|
||||
t["parameters"]["beforeJSWaitTime"] = 0; //执行前js等待时间
|
||||
t["parameters"]["afterJS"] = ""; //执行后执行的js
|
||||
t["parameters"]["afterJSWaitTime"] = 0; //执行后js等待时间
|
||||
} else if(t.option == 5) { //自定义操作
|
||||
t["parameters"]["codeMode"] = 0; //代码模式,0代表JS, 2代表系统级别
|
||||
t["parameters"]["code"] = "";
|
||||
t["parameters"]["waitTime"] = 0; //最长等待时间
|
||||
t["parameters"]["recordASField"] = 0; //是否记录脚本输出
|
||||
t["parameters"]["paraType"] = "text"; //记录脚本输出的字段索引
|
||||
} else if (t.option == 8) { //循环
|
||||
t["parameters"]["scrollType"] = 0; //滚动类型,0不滚动,1向下滚动1屏,2滚动到底部
|
||||
t["parameters"]["scrollCount"] = 1; //滚动次数
|
||||
t["parameters"]["scrollWaitTime"] = 1; //滚动后等待时间
|
||||
t["parameters"]["loopType"] = 0; //默认循环类型
|
||||
t["parameters"]["xpath"] = "";
|
||||
t["parameters"]["pathList"] = "";
|
||||
t["parameters"]["textList"] = "";
|
||||
t["parameters"]["code"] = ""; //执行的代码
|
||||
t["parameters"]["waitTime"] = 0; //最长等待时间
|
||||
t["parameters"]["exitCount"] = 0; //执行多少次后退出循环,0代表不设置此条件
|
||||
t["parameters"]["historyWait"] = 2; //历史记录回退时间,用于循环点击每个链接的情况下点击链接后不打开新标签页的情况
|
||||
t["parameters"]["breakMode"] = 0; //break类型,0代表JS,2代表系统命令
|
||||
t["parameters"]["breakCode"] = ""; //break条件
|
||||
t["parameters"]["breakCodeWaitTime"] = 0; //break条件等待时间
|
||||
} else if (t.option == 9) { //条件
|
||||
|
||||
} else if (t.option == 10) { //条件分支
|
||||
t["parameters"]["class"] = 0; //0代表什么条件都没有,1代表当前页面包括文本,2代表当前页面包括元素,3代表当前循环包括文本,4代表当前循环包括元素
|
||||
t["parameters"]["value"] = ""; //相关值
|
||||
t["parameters"]["code"] = ""; //code
|
||||
t["parameters"]["waitTime"] = 0; //最长等待时间
|
||||
}
|
||||
}
|
||||
|
||||
//修改元素参数,注意所有socket传过来的参数都需要在这里赋值给操作
|
||||
function modifyParameters(t, para) {
|
||||
t["parameters"]["history"] = para["history"];
|
||||
t["parameters"]["tabIndex"] = para["tabIndex"];
|
||||
t["parameters"]["iframe"] = para["iframe"];
|
||||
if (t.option == 1) {
|
||||
t["parameters"]["url"] = para["url"];
|
||||
t["parameters"]["links"] = para["links"];
|
||||
$("#serviceDescription").val(para["url"]);
|
||||
$("#url").val(para["url"]);
|
||||
} else if (t.option == 2) { //鼠标点击事件
|
||||
t["parameters"]["xpath"] = para["xpath"];
|
||||
t["parameters"]["useLoop"] = para["useLoop"];
|
||||
t["parameters"]["allXPaths"] = para["allXPaths"];
|
||||
} else if (t.option == 4) { //输入文字事件
|
||||
t["parameters"]["value"] = para["value"];
|
||||
t["parameters"]["xpath"] = para["xpath"];
|
||||
t["parameters"]["allXPaths"] = para["allXPaths"];
|
||||
} else if(t.option == 6){
|
||||
t["parameters"]["xpath"] = para["xpath"];
|
||||
t["parameters"]["allXPaths"] = para["allXPaths"];
|
||||
t["parameters"]["optionMode"] = para["optionMode"];
|
||||
t["parameters"]["optionValue"] = para["optionValue"];
|
||||
} else if(t.option == 7){
|
||||
t["parameters"]["xpath"] = para["xpath"];
|
||||
t["parameters"]["useLoop"] = para["useLoop"];
|
||||
t["parameters"]["allXPaths"] = para["allXPaths"];
|
||||
} else if (t.option == 8) { //循环事件
|
||||
t["parameters"]["loopType"] = para["loopType"];
|
||||
t["parameters"]["xpath"] = para["xpath"];
|
||||
t["parameters"]["allXPaths"] = para["allXPaths"];
|
||||
if (para["nextPage"]) { //循环点击下一页的情况下
|
||||
t["title"] = "循环点击下一页"
|
||||
} else {
|
||||
t["title"] = "循环"
|
||||
}
|
||||
if (para["loopType"] == 2) //如果是固定元素列表
|
||||
{
|
||||
t["parameters"]["pathList"] = para["pathList"].join("\n");
|
||||
}
|
||||
} else if (t.option == 3) { //采集数据
|
||||
for (let i = 0; i < para["parameters"].length; i++) {
|
||||
changeGetDataParameters(para, i);
|
||||
}
|
||||
t["parameters"]["paras"] = para["parameters"];
|
||||
}
|
||||
}
|
||||
|
||||
function updateUI() {
|
||||
refresh(false);
|
||||
app.$data.nowArrow["num"]++; //改变元素的值,通知画图,重新对锚点画图
|
||||
let tnodes = document.getElementsByClassName("clk");
|
||||
let position = nodeList[vueData.nowNodeIndex]["position"];
|
||||
let pid = nodeList[vueData.nowNodeIndex]["parentId"];
|
||||
for (let i = 0; i < tnodes.length; i++) {
|
||||
if (position == tnodes[i].getAttribute("position") && pid == tnodes[i].getAttribute("pId")) {
|
||||
tnodes[i].style.borderColor = "blue"; // 点击了确定按钮之后需要重新对选中的颜色画框
|
||||
nowNode = tnodes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//点击确定按钮时的处理
|
||||
$("#confirm").mousedown(updateUI);
|
||||
|
||||
//获取url中的参数
|
||||
function getUrlParam(name) {
|
||||
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
|
||||
var r = window.location.search.substr(1).match(reg); //匹配目标参数
|
||||
if (r != null) return unescape(r[2]);
|
||||
return "";
|
||||
}
|
||||
|
||||
var sId = getUrlParam('id');
|
||||
var backEndAddressServiceWrapper = getUrlParam("backEndAddressServiceWrapper");
|
||||
let mobile = getUrlParam("mobile");
|
||||
if (mobile == "true") {
|
||||
$("#environment").val(1);
|
||||
}
|
||||
|
||||
|
||||
function isValidMySQLTableName(tableName) {
|
||||
// 正则表达式:以字母或汉字开头,后接字母、数字、下划线或汉字的字符串,长度为1到64字符
|
||||
const pattern = /^[\u4e00-\u9fa5a-zA-Z][\u4e00-\u9fa5a-zA-Z0-9_]{0,63}$/;
|
||||
return pattern.test(tableName);
|
||||
}
|
||||
function saveService(type) {
|
||||
let serviceId = $("#serviceId").val();
|
||||
let text = "确认要保存任务吗(不能用鼠标点击时,请按键盘回车键)?";
|
||||
if (type == 1) { //任务另存为
|
||||
serviceId = -1;
|
||||
text = "确认要另存为任务吗(不能用鼠标点击时,请按键盘回车键)?";
|
||||
}
|
||||
// if (confirm(text)) {
|
||||
let serviceName = $("#serviceName").val();
|
||||
let url = $("#url").val();
|
||||
let serviceDescription = $("#serviceDescription").val();
|
||||
let inputParameters = [];
|
||||
let outputParameters = [];
|
||||
let outputNames = [];
|
||||
let inputIndex = 0;
|
||||
let outputIndex = 0;
|
||||
let links = ""; //记录所有的link
|
||||
let containJudge = false; //是否含有判断语句
|
||||
let saveThreshold = parseInt($("#saveThreshold").val());
|
||||
let cloudflare = parseInt($("#cloudflare").val());
|
||||
let environment = parseInt($("#environment").val());
|
||||
for (let i = 1; i < nodeList.length; i++) {
|
||||
if (nodeList[i]["id"] != -1) { //已经被删除的节点不进行统计
|
||||
if (nodeList[i]["option"] == 1) //打开网页操作,统计输入框输入操作
|
||||
{
|
||||
if (!nodeList[i]["parameters"]["useLoop"]) //如果不是使用循环里的文本
|
||||
{
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "urlList_" + inputIndex++,
|
||||
nodeId: i, //记录操作位于的节点位置,重要!!!
|
||||
nodeName: nodeList[i]["title"],
|
||||
value: nodeList[i]["parameters"]["links"],
|
||||
desc: "要采集的网址列表,多行以\\n分开",
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["links"]
|
||||
});
|
||||
links = nodeList[i]["parameters"]["links"];
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 4) //输入文字操作
|
||||
{
|
||||
if (!nodeList[i]["parameters"]["useLoop"]) //如果不是使用循环里的文本
|
||||
{
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "inputText_" + inputIndex++,
|
||||
nodeName: nodeList[i]["title"],
|
||||
nodeId: i,
|
||||
desc: "要输入的文本,如京东搜索框输入:电脑",
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["value"],
|
||||
value: nodeList[i]["parameters"]["value"],
|
||||
});
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 8) //循环操作
|
||||
{
|
||||
if (parseInt(nodeList[i]["parameters"]["loopType"]) > 2 && parseInt(nodeList[i]["parameters"]["loopType"]) < 5) { //循环中的循环输入文本或循环输入网址
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "loopText_" + inputIndex++,
|
||||
nodeId: i,
|
||||
nodeName: nodeList[i]["title"],
|
||||
desc: "要输入的文本/网址,多行以\\n分开",
|
||||
type: "text",
|
||||
exampleValue: nodeList[i]["parameters"]["textList"],
|
||||
value: nodeList[i]["parameters"]["textList"],
|
||||
});
|
||||
} else if (parseInt(nodeList[i]["parameters"]["loopType"]) == 0) {
|
||||
inputParameters.push({
|
||||
id: inputIndex,
|
||||
name: "loopTimes_" + nodeList[i]["title"] + "_" + inputIndex++,
|
||||
nodeId: i,
|
||||
nodeName: nodeList[i]["title"],
|
||||
desc: "循环" + nodeList[i]["title"] + "执行的次数(0代表无限循环)",
|
||||
type: "int",
|
||||
exampleValue: nodeList[i]["parameters"]["exitCount"],
|
||||
value: nodeList[i]["parameters"]["exitCount"],
|
||||
});
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 3) //提取数据操作
|
||||
{
|
||||
for (let j = 0; j < nodeList[i]["parameters"]["paras"].length; j++) {
|
||||
if (outputNames.indexOf(nodeList[i]["parameters"]["paras"][j]["name"]) < 0) { //参数名称还未被添加
|
||||
outputNames.push(nodeList[i]["parameters"]["paras"][j]["name"]);
|
||||
outputParameters.push({
|
||||
id: outputIndex++,
|
||||
name: nodeList[i]["parameters"]["paras"][j]["name"],
|
||||
desc: nodeList[i]["parameters"]["paras"][j]["desc"],
|
||||
type: nodeList[i]["parameters"]["paras"][j]["paraType"],
|
||||
recordASField: nodeList[i]["parameters"]["paras"][j]["recordASField"],
|
||||
exampleValue: nodeList[i]["parameters"]["paras"][j]["exampleValues"][0]["value"],
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (nodeList[i]["option"] == 5) //自定义操作
|
||||
{
|
||||
// if (nodeList[i]["parameters"]["recordASField"] == 1) {
|
||||
let id = outputIndex++;
|
||||
let title = nodeList[i]["title"];
|
||||
// if (outputNames.indexOf(title) >= 0) { //参数名称已经被添加
|
||||
// $('#myModal').modal('hide');
|
||||
// $("#tip2").slideDown(); //提示框
|
||||
// fadeout = setTimeout(function() {
|
||||
// $("#tip2").slideUp();
|
||||
// }, 5000);
|
||||
// return;
|
||||
// }
|
||||
outputNames.push(title);
|
||||
outputParameters.push({
|
||||
id: id,
|
||||
name: title,
|
||||
desc: "自定义操作返回的数据",
|
||||
type: nodeList[i]["parameters"]["paraType"],
|
||||
recordASField: nodeList[i]["parameters"]["recordASField"],
|
||||
exampleValue: "",
|
||||
});
|
||||
// }
|
||||
} else if (nodeList[i]["option"] == 9) //条件判断
|
||||
{
|
||||
containJudge = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
let serviceInfo = {
|
||||
"id": parseInt(serviceId),
|
||||
"name": serviceName,
|
||||
"url": url,
|
||||
"links": links,
|
||||
"create_time": new Date().toLocaleString(),
|
||||
"version": "0.3.5",
|
||||
"saveThreshold": saveThreshold,
|
||||
"cloudflare": cloudflare,
|
||||
"environment": environment,
|
||||
"maxViewLength": parseInt($("#maxViewLength").val()),
|
||||
"outputFormat": $("#outputFormat").val(),
|
||||
"saveName": $("#saveName").val(),
|
||||
"containJudge": containJudge,
|
||||
"desc": serviceDescription,
|
||||
"inputParameters": inputParameters,
|
||||
"outputParameters": outputParameters,
|
||||
"graph": nodeList, //图结构要存储下来
|
||||
};
|
||||
if(serviceInfo.outputFormat=="mysql"){
|
||||
if(!isValidMySQLTableName(serviceInfo.saveName)) {
|
||||
$('#myModal').modal('hide');
|
||||
$("#tipMySQL").slideDown(); //提示框
|
||||
let fadeout = setTimeout(function() {
|
||||
$("#tipMySQL").slideUp();
|
||||
}, 4000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$.post(backEndAddressServiceWrapper + "/manageTask", { paras: JSON.stringify(serviceInfo) },
|
||||
function(result) { $("#serviceId").val(parseInt(result)) });
|
||||
// alert("保存成功!");
|
||||
$('#myModal').modal('hide');
|
||||
$("#tip").slideDown(); //提示框
|
||||
let fadeout = setTimeout(function() {
|
||||
$("#tip").slideUp();
|
||||
}, 2000);
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
//点击保存任务按钮时的处理
|
||||
$("#saveButton").mousedown(function() {
|
||||
saveService(0);
|
||||
});
|
||||
//点击另存为任务按钮时的处理
|
||||
$("#saveAsButton").mousedown(function() {
|
||||
saveService(1);
|
||||
});
|
||||
|
||||
|
||||
if (sId != null && sId != -1) //加载任务
|
||||
{
|
||||
$.get(backEndAddressServiceWrapper + "/queryTask?id=" + sId, function(result) {
|
||||
nodeList = result["graph"];
|
||||
app.$data.list.nl = nodeList;
|
||||
for(let node of nodeList){ //兼容旧版本
|
||||
if(node["option"] == 1){
|
||||
if(!("cookies" in node["parameters"])) {
|
||||
node["parameters"]["cookies"] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
$("#serviceName").val(result["name"]);
|
||||
$("#serviceId").val(result["id"]);
|
||||
$("#url").val(result["url"]);
|
||||
$("#serviceDescription").val(result["desc"]);
|
||||
for(let key of Object.keys(result)){
|
||||
try{
|
||||
$("#"+key).val(result[key]);
|
||||
} catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
});
|
||||
} else {
|
||||
refresh(); //新增任务
|
||||
}
|
@ -47,6 +47,8 @@
|
||||
<p>{{"Task Name:~任务名称:" | lang}} {{task["name"]}}</p>
|
||||
<p style="word-wrap: break-word;word-break: break-all;overflow: hidden;max-height: 100px;">{{"Task Description:~任务描述:" | lang}} {{task["desc"]}}</p>
|
||||
<p style="word-wrap: break-word;word-break: break-all;overflow: hidden;max-height: 100px;">{{"Example URL:~样例网址:" | lang}} {{task["url"]}}</p>
|
||||
<p style="word-wrap: break-word;word-break: break-all;overflow: hidden;max-height: 100px;">{{"Create Time:~创建时间:" | lang}} {{dateFormat(task["create_time"])}}</p>
|
||||
<p style="word-wrap: break-word;word-break: break-all;overflow: hidden;max-height: 100px;">{{"Update Time:~更新时间:" | lang}} {{dateFormat(task["update_time"])}}</p>
|
||||
<p>{{"Operations (Please close this window and select 'Design Task' button if you want to modify task with a browser)~操作(如要带浏览器修改任务流程请关闭此窗口并选择设计任务)" | lang}}</p>
|
||||
<p><a style="margin-top: 5px;" href="javascript:void(0)" v-on:click="modifyTask(task['id'],task['url'])" class="btn btn-primary">{{"Modify Task Workflow~修改任务流程" | lang}}</a>
|
||||
<a style="margin-top: 5px;" href="javascript:void(0)" v-on:click="invokeTask(task['id'],task['url'])" class="btn btn-primary">{{"Invoke Task~调用任务" | lang}}</a></p>
|
||||
@ -125,6 +127,7 @@
|
||||
backEndAddressServiceWrapper: getUrlParam("backEndAddressServiceWrapper"),
|
||||
},
|
||||
methods: {
|
||||
dateFormat: DateFormat,
|
||||
gotoHome:function(){
|
||||
let url = "";
|
||||
if(getUrlParam("lang")=="zh"){
|
||||
|
1
ElectronJS/tasks/155.json
Normal file
1
ElectronJS/tasks/155.json
Normal file
@ -0,0 +1 @@
|
||||
{"id":155,"name":"京东全球版-专业的综合网上购物商城","url":"https://www.jd.com","links":"https://www.jd.com","create_time":"7/8/2023, 4:15:43 AM","update_time":"7/8/2023, 4:25:42 AM","version":"0.3.5","saveThreshold":10,"cloudflare":0,"environment":0,"maxViewLength":15,"outputFormat":"mysql","saveName":"current_time","containJudge":false,"desc":"https://www.jd.com","inputParameters":[{"id":0,"name":"urlList_0","nodeId":1,"nodeName":"打开网页","value":"https://www.jd.com","desc":"要采集的网址列表,多行以\\n分开","type":"text","exampleValue":"https://www.jd.com"}],"outputParameters":[],"graph":[{"index":0,"id":0,"parentId":0,"type":-1,"option":0,"title":"root","sequence":[1,2],"parameters":{"history":1,"tabIndex":0,"useLoop":false,"xpath":"","wait":0,"waitType":0},"isInLoop":false},{"id":1,"index":1,"parentId":0,"type":0,"option":1,"title":"打开网页","sequence":[],"isInLoop":false,"position":0,"parameters":{"useLoop":false,"xpath":"","wait":0,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"url":"https://www.jd.com","links":"https://www.jd.com","maxWaitTime":10,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"cookies":""}},{"id":2,"index":2,"parentId":0,"type":0,"option":2,"title":"点击元素","sequence":[],"isInLoop":false,"position":1,"parameters":{"history":1,"tabIndex":0,"useLoop":false,"xpath":"","iframe":false,"wait":2,"waitType":0,"beforeJS":"","beforeJSWaitTime":0,"afterJS":"","afterJSWaitTime":0,"scrollType":0,"scrollCount":1,"scrollWaitTime":1,"clickWay":0,"maxWaitTime":10,"paras":[]}}]}
|
1
ElectronJS/tasks/156.json
Normal file
1
ElectronJS/tasks/156.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -136,7 +136,7 @@ Yin: Professor of College of Computer Science and Technology of Zhejiang Univers
|
||||
|
||||
### 后台流程图部分
|
||||
* ServiceGrid/frontEnd/FlowChart.html
|
||||
* ServiceGrid/frontEnd/FlowChart.js
|
||||
* ServiceGrid/frontEnd/FlowChart_Deprecated.js
|
||||
* ServiceGrid/frontEnd/FlowChart.css
|
||||
* ServiceGrid/frontEnd/logic.css
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user