Kaynağa Gözat

English Version

pull/115/head
naibo 1 yıl önce
ebeveyn
işleme
b73a2a9954
18 değiştirilmiş dosya ile 360 ekleme ve 1326 silme
  1. +2
    -1
      .temp_to_pub/.gitignore
  2. +6
    -0
      .temp_to_pub/compress.py
  3. +3
    -3
      ElectronJS/change_version.py
  4. +7
    -0
      ElectronJS/detect_chinese.py
  5. +4
    -4
      ElectronJS/main.js
  6. +4
    -4
      ElectronJS/src/index.html
  7. +93
    -30
      ElectronJS/src/taskGrid/FlowChart.html
  8. +71
    -40
      ElectronJS/src/taskGrid/FlowChart.js
  9. +7
    -9
      ElectronJS/src/taskGrid/FlowChart_CN.html
  10. +0
    -627
      ElectronJS/src/taskGrid/FlowChart_CN.js
  11. +31
    -1
      ElectronJS/src/taskGrid/global.js
  12. +125
    -94
      ElectronJS/src/taskGrid/logic.js
  13. +0
    -511
      ElectronJS/src/taskGrid/logic_CN.js
  14. +3
    -0
      ElectronJS/src/taskGrid/taskInfo.html
  15. +1
    -0
      ElectronJS/tasks/155.json
  16. +1
    -0
      ElectronJS/tasks/156.json
  17. +1
    -1
      ElectronJS/tasks/34.json
  18. +1
    -1
      media/readme_back.md

+ 2
- 1
.temp_to_pub/.gitignore Dosyayı Görüntüle

@ -4,4 +4,5 @@ EasySpider.app/
EasySpider_windows_x64/user_data
*.tmp
*.7z*
config.json
config.json
mysql_config.json

+ 6
- 0
.temp_to_pub/compress.py Dosyayı Görüntüle

@ -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)

+ 3
- 3
ElectronJS/change_version.py Dosyayı Görüntüle

@ -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
- 0
ElectronJS/detect_chinese.py Dosyayı Görüntüle

@ -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)

+ 4
- 4
ElectronJS/main.js Dosyayı Görüntüle

@ -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) {

+ 4
- 4
ElectronJS/src/index.html Dosyayı Görüntüle

@ -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>

+ 93
- 30
ElectronJS/src/taskGrid/FlowChart.html Dosyayı Görüntüle

@ -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">&times;</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">&times;</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">&times;</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">&times;</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({

+ 71
- 40
ElectronJS/src/taskGrid/FlowChart.js Dosyayı Görüntüle

@ -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,执行后会卡死输入框,所以最好不要用
}
}
}

+ 7
- 9
ElectronJS/src/taskGrid/FlowChart_CN.html Dosyayı Görüntüle

@ -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">&times;</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">&times;</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">&times;</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({

+ 0
- 627
ElectronJS/src/taskGrid/FlowChart_CN.js Dosyayı Görüntüle

@ -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,执行后会卡死输入框,所以最好不要用
}
}

+ 31
- 1
ElectronJS/src/taskGrid/global.js Dosyayı Görüntüle

@ -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);
}

+ 125
- 94
ElectronJS/src/taskGrid/logic.js Dosyayı Görüntüle

@ -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) {
}
}
function showError(msg, time=4000) {
$("#error_message").text(msg);
$("#tipError").slideDown(); //提示框
let fadeout = setTimeout(function() {
$("#tipError").slideUp();
}, time);
}
//点击确定按钮时的处理
$("#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;
}
}
});
$("#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 "";
}
//点击保存任务按钮时的处理
$("#saveButton").mousedown(function() {
saveService(0);
});
//点击另存为任务按钮时的处理
$("#saveAsButton").mousedown(function() {
saveService(1);
});
var sId = getUrlParam('id');
var backEndAddressServiceWrapper = getUrlParam("backEndAddressServiceWrapper");
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;
}
}

+ 0
- 511
ElectronJS/src/taskGrid/logic_CN.js Dosyayı Görüntüle

@ -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(); //新增任务
}

+ 3
- 0
ElectronJS/src/taskGrid/taskInfo.html Dosyayı Görüntüle

@ -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
- 0
ElectronJS/tasks/155.json Dosyayı Görüntüle

@ -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
- 0
ElectronJS/tasks/156.json
Dosya farkı çok büyük olduğundan ihmal edildi
Dosyayı Görüntüle


+ 1
- 1
ElectronJS/tasks/34.json
Dosya farkı çok büyük olduğundan ihmal edildi
Dosyayı Görüntüle


+ 1
- 1
media/readme_back.md Dosyayı Görüntüle

@ -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

Yükleniyor…
İptal
Kaydet