Przeglądaj źródła

Translate to English

naibo 2 lat temu
rodzic
commit
f81619980e

+ 3 - 3
ElectronJS/src/index.html

@@ -19,7 +19,7 @@
         flex-direction: column;
         align-items: center;
         justify-content: center;
-        margin-top: 20px;
+        margin-top: 40px;
     }
 
     .img-container img {
@@ -44,7 +44,7 @@
 <!--        <p>如发现新版本更新,可从以下Github仓库下载最新版本使用/If a new version is found, you can download the latest version from the following Github repository:</p>-->
 <!--        <p></p>-->
         <div class="img-container">
-            <h6>出品方/Producer</h6>
+            <h5>出品方/Producer</h5>
             <a href="https://www.zju.edu.cn" target="_blank"><img src="img/zju.png"></a>
             <a href="https://www.nus.edu.sg" target="_blank"><img src="img/nuslogo.png"></a>
             <a href="https://www.xidian.edu.cn" target="_blank"><img src="img/xidian.png"></a>
@@ -70,7 +70,7 @@
             <div v-else-if="step == 1">
                 <h4 style="margin-top: 20px">Please select design mode</h4>
                 <p style="margin-top: 20px; text-align: justify; width:310px; margin-left: 18%">
-                    Clean Mode: Start with a clean browser with no cookie/user data</p>
+                    Clean Mode: Start with a clean browser with no cookie/user data.</p>
                 <p style="text-align: justify; width:310px; margin-left: 18%">
                     Data Mode: Start with a browser that stores user data such as website login information and cookies.</p>
                 <p><a @click="startDesign('en')"

+ 240 - 30
ElectronJS/src/taskGrid/FlowChart.html

@@ -18,6 +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> 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>
     <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">
@@ -30,7 +33,7 @@
         </nav>
     </div>
     <div style="display:flex">
-        <div style="width: 200px;float:left">
+        <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;">Toolkit</div>
                 <button type="button" id="save" data-toggle="modal" data-target="#myModal" onmousedown="$('#myModal').modal('show');" class="btn btn-primary">Save Task</button>
@@ -39,8 +42,8 @@
                 <button type="button" data=3 class="btn btn-outline-primary options">Collect Data</button>
                 <button type="button" data=4 class="btn btn-outline-primary options">Input Text</button>
                  <button type="button" data=5 class="btn btn-outline-primary options">Custom Action</button>
-                <!-- <button type="button" data=6 style="font-size: 14px!important;" class="btn btn-outline-primary options">Change Option</button> -->
-                <!-- <button type="button" data=7 class="btn btn-outline-primary options">-</button> -->
+                 <button type="button" data=6 style="font-size: 14px!important;" class="btn btn-outline-primary options">Change Option</button>
+                 <button type="button" data=7 class="btn btn-outline-primary options">Mouse Move</button>
                 <button type="button" data=8 class="btn btn-outline-primary options">Loop</button>
                 <button type="button" data=9 class="btn btn-outline-primary options">If Condition</button>
                 <div>-----------------</div>
@@ -49,7 +52,7 @@
                 <button type="button" data=11 class="btn btn-outline-primary options">Copy Element</button>
                 <button type="button" data=12 class="btn btn-outline-primary options">Del Element</button>
                 <button type="button" data=0 class="btn btn-outline-primary2 options">Cancel</button>
-                <div style="text-align: left;margin: 10px;font-size:15px!important"></div>
+                <div style="text-align: left;margin: 10px;font-size:15px!important">Click button above and then click the flowchart to insert.</div>
             </div>
         </div>
         <div style="margin-top:20px;border: solid;height:850px;overflow: auto;width:58%;float:right">
@@ -59,11 +62,31 @@
                 <p style="font-size: 22px!important;text-align: center;margin-left: 1px;">■</p>
             </div>
         </div>
-        <div style="margin-top:20px;border: solid navy;height:850px;overflow: auto;margin-left:10px;width:30%;float:right;min-width:300px">
+        <div style="margin-top:20px;border: solid navy;height:850px;overflow: auto;margin-left:10px;margin-right:10px;width:30%;float:right;min-width:300px">
             <!-- <div style="display: inline-block;border-bottom:solid 2px;width:100%">
                 <input style="float:left;margin-left:10px;margin-top:10px;margin-bottom:10px" type="button" class="btn-primary" value="保存服务"></input>
             </div> -->
             <div class="Modify" id="app">
+                <div class="modal fade" id="myModal_XPath" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+                    <div class="modal-dialog">
+                        <div class="modal-content">
+                            <div class="modal-header">
+                                <h4 class="modal-title">Equivalent XPath</h4>
+                                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                            </div>
+                            <div class="modal-body">
+                                <label>The following are alternative XPath expressions, in addition to the default generated XPath, that can all locate the same element (although not entirely accurate, they may also locate other elements besides the intended one, so they are provided here for reference only). </label>
+                                <label>Each line contains an XPath expression (you can use the pre-installed <i>XPath Helper</i> extension for debugging):</label>
+                                <textarea id="otherXPaths" onkeydown="inputDelete(event)" class="form-control" rows="4">{{XPaths}}</textarea>
+                                <div>
+                                    <img src="../img/XPather_helper.png" style="width:50%;height:50%; margin: 10px auto"></div>
+                            </div>
+                        </div>
+                        <!-- /.modal-content -->
+                    </div>
+                    <!-- /.modal -->
+                </div>
+
                 <div>
                     <label>Option Name:</label>
                     <input onkeydown="inputDelete(event)" class="form-control" v-model='list.nl[index.nowNodeIndex]["title"]'></input>
@@ -75,19 +98,23 @@
                         <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='useLoop'></input>Use link inside the Loop</p>
                     </div>
                     <div v-if='!useLoop'>
-                        <label>url:</label>
-                        <input onkeydown="inputDelete(event)" class="form-control" v-model='nowNode["parameters"]["url"]'></input>
-                        <label>All links filled:</label>
+<!--                        <label>url:</label>-->
+<!--                        <input onkeydown="inputDelete(event)" class="form-control" v-model='nowNode["parameters"]["url"]'></input>-->
+                        <label>Links (one link per line, the entire workflow will be executed as many times as there are lines of links):</label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["links"]'></textarea>
                     </div>
+                    <label>Maximum wait time for page load (in seconds): </label>
+                    <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['maxWaitTime']" type="number" required></input>
                     <label>After executed, whether scroll down:</label>
                     <select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
-                        <option value = 0>No</option>
+                        <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>
+                    <label>Scroll Times (the wait time after scrolling <b>ineffective</b> when the scrolling type is set to <b>no scrolling</b>):</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==2">
@@ -96,18 +123,42 @@
                         <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='useLoop'></input>Use element inside the Loop</p>
                     </div>
                     <div v-if='!useLoop'>
-                        <!-- <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> -->
                         <label>XPath: <span style="font-size: 30px!important;" title="Relative XPATH writing: start with /, e.g. the loop item XPATH is /html/body/div[1], your input is /*[@id='tab-customer'], then the final addressed xpath is: /html/body/div[1]/*[@id='tab-customer']">☺</span></label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
+                        <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode['parameters']['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">Click here to view other equivalent XPath expressions</button></p>
                     </div>
-                    <label>After executed, whether scroll down:</label>
+                    <label>Maximum wait time for page load after clicking (in seconds):</label>
+                    <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['maxWaitTime']" type="number" required></input>
+                    <label>Whether to scroll down after clicking:</label>
                     <select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
-                        <option value = 0>No</option>
+                        <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>
+                    <p style="margin-top: 10px">
+                        <a class="btn btn-primary" data-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false" aria-controls="collapseExample">
+                            Click here to expand/collapse advanced operations
+                        </a>
+                    </p>
+                    <div :class="{collapse: true, 'show': nowNode['parameters']['beforeJS'].length!=0 || nowNode['parameters']['afterJS'].length!=0}" id="collapseExample">
+                        <div>
+                            <label>Execute a JavaScript script <strong>before</strong> clicking on this element:</label>
+                            <textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
+                                      placeholder='The element should be represented by arguments[0]. Here is an example JavaScript code: arguments[0].innerText = arguments[0].innerText.replace("United States","US"). This code replaces occurrences of "United States" with "US" in the text of the element. Subsequently, when extracting data, you will obtain the replaced value.' v-model='nowNode["parameters"]["beforeJS"]'></textarea>
+                            <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"]["beforeJSWaitTime"]'></input>
+                            <label>Execute a JavaScript script <strong>after</strong> clicking on this element: </label>
+                            <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" placeholder='The element should be represented by arguments[0]. Here is an example JavaScript code: arguments[0].click(). This code simulates a click on the element.' v-model='nowNode["parameters"]["afterJS"]'></textarea>
+                            <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"]["afterJSWaitTime"]'></input>
+                        </div>
+                    </div>
+
+
 
                 </div>
 
@@ -136,17 +187,52 @@
                         </table>
                     </div>
                     <div style="font-size: 13px;" v-if="paraIndex<paras.parameters.length">
-                        <label>Current parameter name: <strong>{{paras.parameters[paraIndex]["name"]}}</strong></label>
-                        <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='paras.parameters[paraIndex]["relative"]'></input>Use relative XPATH</p>
-                        <label>XPATH: <span style="font-size: 30px!important;" title="Relative XPATH writing: start with /, e.g. the loop item XPATH is /html/body/div[1], your input is /*[@id='tab-customer'], then the final addressed xpath is: /html/body/div[1]/*[@id='tab-customer']">☺</span></label>
+                        <p>Current parameter name (Clicking on the "Modify" option of the field toggles the parameters)</p>
+                        <label><strong>{{paras.parameters[paraIndex]["name"]}}</strong></label>
+                        <p v-if="nowNode['isInLoop']"><input onkeydown="inputDelete(event)" type="checkbox" v-model='paras.parameters[paraIndex]["relative"]'></input>Use relative XPath</p>
+                        <p>XPATH: <span style="font-size: 30px!important;" title="Relative XPATH writing: start with /, e.g. the loop item XPATH is /html/body/div[1], your input is /*[@id='tab-customer'], then the final addressed xpath is: /html/body/div[1]/*[@id='tab-customer']">☺</span></p>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='paras.parameters[paraIndex]["relativeXPath"]'></textarea>
-                        <label>Extraction Type</label>
+                        <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(paras.parameters[paraIndex]['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">Click here to view other equivalent XPath expressions</button></p>
+                        <p style="margin-top: 10px">
+                            <a class="btn btn-primary" data-toggle="collapse" href="#elementAdvanced" role="button" aria-expanded="false" aria-controls="collapseExample">
+                                Click here to expand/collapse advanced operations
+                            </a>
+                        </p>
+                        <div :class="{collapse: true, 'show': paras.parameters[paraIndex]['beforeJS'].length!=0 || paras.parameters[paraIndex]['afterJS'].length!=0}" id="elementAdvanced">
+                            <div>
+                                <label>Execute a JavaScript script <strong>before</strong> extracting data from this element: </label>
+                                <textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
+                                          placeholder='The element should be represented by arguments[0]. Here is an example JavaScript code: arguments[0].innerText = arguments[0].innerText.replace("United States","US"). This code replaces occurrences of "United States" with "US" in the text of the element. Subsequently, when extracting data, you will obtain the replaced value.' v-model='paras.parameters[paraIndex]["beforeJS"]'></textarea>
+                                <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='paras.parameters[paraIndex]["beforeJSWaitTime"]'></input>
+                                <label>Execute a JavaScript script <strong>after</strong> extracting data from this element: </label>
+                                <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" placeholder='The element should be represented by arguments[0]. Here is an example JavaScript code: arguments[0].click(). This code simulates a click on the element.' v-model='paras.parameters[paraIndex]["afterJS"]'></textarea>
+                                <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='paras.parameters[paraIndex]["afterJSWaitTime"]'></input>
+                            </div>
+                        </div>
+                        <label>Extract Type</label>
                         <select v-model='paras.parameters[paraIndex]["contentType"]' class="form-control">
                             <option :value = 0>Text (include child element)</option>
                             <option :value = 1>Text (exclude child element)</option>
                             <option :value = 2>innerHTML</option>
                             <option :value = 3>outerHTML</option>
+                            <option :value = 4>Background Image Address</option>
+                            <option :value = 5>Webpage URL</option>
+                            <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</option>
+                            <option :value = 10>Selected value of the current select box</option>
+                            <option :value = 11>Selected text of the current select box</option>
                         </select>
+                        <div v-if='paras.parameters[paraIndex]["contentType"] == 9'>
+                            <label>JavaScript Code: </label>
+                            <textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
+                                      placeholder='The element can be represented by arguments[0]. Here is an example: return arguments[0].innerText + "US Dollar". This code extracts the innerText of the element and appends "US Dollar"  to it.' v-model='paras.parameters[paraIndex]["JS"]'></textarea>
+                            <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='paras.parameters[paraIndex]["JSWaitTime"]'></input>
+                        </div>
                         <label>Node Type</label>
                         <select v-model='paras.parameters[paraIndex]["nodeType"]' class="form-control">
                             <option :value = 0>Ordinary Node</option>
@@ -155,10 +241,23 @@
                             <option :value = 3>Form Value</option>
                             <option :value = 4>Image Address</option>
                         </select>
+                        <div v-if='paras.parameters[paraIndex]["nodeType"] == 4'>
+                            <label>Whether to <b>download image</b> after extracting the image address</label>
+                            <select v-model='paras.parameters[paraIndex]["downloadPic"]' class="form-control">
+                                <option :value = 0>No</option>
+                                <option :value = 1>Yes</option>
+                            </select>
+                        </div>
+<!--                        <label>提取方式</label>-->
+<!--                        <select v-model='paras.parameters[paraIndex]["extractType"]' class="form-control">-->
+<!--                            <option :value = 0>普通提取</option>-->
+<!--                            <option :value = 1>OCR提取</option>-->
+<!--                        </select>-->
                         <label>Parameter Description:</label>
-                        <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='paras.parameters[paraIndex]["desc"]'></textarea>
-                        <label>Absent value when cannot find the element:</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>
                         <input onkeydown="inputDelete(event)" class="form-control" v-model='paras.parameters[paraIndex]["default"]'></textarea>
+
                     </div>
 
                 </div>
@@ -167,7 +266,7 @@
                 <div class="elements" v-if="nodeType==4">
                     <div v-if="nowNode['isInLoop']">
                         <!-- 如果在循环内且循环内是固定文本才显示此行元素 -->
-                        <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='useLoop'></input>Use text inside the Loop</p>
+                        <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='useLoop'></input>Use text inside the Loop (If not checked, the text entered each time will be the text inside the "Input Value" textbox below. If checked, the text set within the loop will be used)</p>
                     </div>
                     <div v-if='!useLoop'>
                         <label>Input Value:</label>
@@ -176,17 +275,81 @@
 
                     <label>XPath: <span style="font-size: 30px!important;" title="Relative XPATH writing: start with /, e.g. the loop item XPATH is /html/body/div[1], your input is /*[@id='tab-customer'], then the final addressed xpath is: /html/body/div[1]/*[@id='tab-customer']">☺</span></label>
                     <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
+                    <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode.parameters['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">Click here to view other equivalent XPath expressions</button></p>
+                    <p style="margin-top: 10px">
+                        <a class="btn btn-primary" data-toggle="collapse" href="#inputAdvanced" role="button" aria-expanded="false" aria-controls="collapseExample">
+                            Click here to expand/collapse advanced operations
+                        </a>
+                    </p>
+                    <div :class="{collapse: true, 'show': nowNode['parameters']['beforeJS'].length!=0 || nowNode['parameters']['afterJS'].length!=0}" id="inputAdvanced">
+                        <div>
+                            <label>Execute JavaScript script on this element <b>before</b> entering text into it: </label>
+                            <textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
+                                      placeholder='The element should be represented by arguments[0]. Here is an example JavaScript code: arguments[0].innerText = arguments[0].innerText.replace("United States","US"). This code replaces occurrences of "United States" with "US" in the text of the element. Subsequently, when extracting data, you will obtain the replaced value.' v-model='nowNode["parameters"]["beforeJS"]'></textarea>
+                            <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"]["beforeJSWaitTime"]'></input>
+                            <label>Execute JavaScript script on this element <b>after</b> entering text into it: </label>
+                            <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" placeholder='The element should be represented by arguments[0]. Here is an example JavaScript code: arguments[0].click(). This code simulates a click on the element.' v-model='nowNode["parameters"]["afterJS"]'></textarea>
+                            <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"]["afterJSWaitTime"]'></input>
+                        </div>
+                    </div>
                 </div>
 
                 <div class="elements" v-if="nodeType==5">
+                    <label>Custom Action Mode</label>
+                    <select v-model='nowNode["parameters"]["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>
+                    </select>
+
+                    <div>
+                        <label>Code: </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>
+                        <p><select v-model='nowNode["parameters"]["recordASField"]' class="form-control">
+                            <option value = 0>No</option>
+                            <option value = 1>Yes</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>
+
+
+
+
 
                 </div>
 
                 <div class="elements" v-if="nodeType==6">
+                    <label>XPath: </label>
+                    <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
+                    <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode['parameters']['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">Click here to view other equivalent XPath expressions</button></p>
+
+                    <p>Option switch Mode</p>
 
+                    <select class="form-control" v-model='nowNode["parameters"]["optionMode"]'>
+                        <option value=0>Switch to the next option</option>
+                        <option value=1>Switch options by index value</option>
+                        <option value=2>Switch options by option value</option>
+                        <option value=3>Switch options by option text</option>
+                    </select>
+                    <p>Set value (not applicable for "Switch to the next option" mode)</p>
+                    <input class="form-control" id="selectValue" v-model='nowNode["parameters"]["optionValue"]' autoFocus="autofocus" type="text"></input>
                 </div>
 
                 <div class="elements" v-if="nodeType==7">
+                    <div v-if="nowNode['isInLoop']">
+                        <!-- 如果在循环内才显示此行元素 -->
+                        <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='useLoop'></input>Use element inside the loop</p>
+                    </div>
+                    <div v-if='!useLoop'>
+                        <label>XPath: </label>
+                        <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
+                        <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode['parameters']['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">Click here to view other equivalent XPath expressions</button></p>
+                    </div>
+
 
                 </div>
 
@@ -199,33 +362,61 @@
                         <option value = 2>Fixed Element List</option>
                         <option value = 3>Text List</option>
                         <option value = 4>Weblink List</option>
+                        <option value = 5>Return value of JavaScript command</option>
+                        <option value = 6>Return value of system command</option>
                       </select>
                     <div v-if='parseInt(loopType) < 2'>
                         <label>XPath: <span style="font-size: 30px!important;" title="Relative XPATH writing: start with /, e.g. the loop item XPATH is /html/body/div[1], your input is /*[@id='tab-customer'], then the final addressed xpath is: /html/body/div[1]/*[@id='tab-customer']">☺</span></label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
+                        <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode.parameters['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">(Testing feature) Click here to view other possible XPath expressions</button></p>
                     </div>
                     <div v-if='parseInt(loopType) == 2'>
                         <label>XPath List:</label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" v-model='nowNode["parameters"]["pathList"]'></textarea>
                     </div>
-                    <div v-if='parseInt(loopType) > 2'>
+                    <div v-else-if='parseInt(loopType) < 5'>
                         <label>Content List:</label>
-                        <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" v-model='nowNode["parameters"]["textList"]'></textarea>
+                        <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" placeholder="One text/URL per line" v-model='nowNode["parameters"]["textList"]'></textarea>
+                    </div>
+                    <div v-else-if='parseInt(loopType) < 7'>
+                        <label>Code:</label>
+                        <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" v-model='nowNode["parameters"]["code"]' placeholder="Continue the loop if the command return value is greater than 0 or evaluates to true; otherwise, stop the loop. For example, return document.body.scrollWidth > 1000 is an example of a JavaScript command return value, and python D:/test.py is an example of a system command return value."></textarea>
+                        <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>
                     <!-- 这里添加退出循环条件,找不到元素肯定退出循环 -->
                     <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><b>History back</b> wait time:</label>
+
+
+                    <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</option>
+                        <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>
+                            <select v-model='nowNode["parameters"]["breakMode"]' class="form-control" style="font-weight: bold">
+                                <option value=0>Do not set script (even if a script is written below, it will not be executed)</option>
+                                <option value=1>JavaScript script</option>
+                                <option value=2>Operating system-level command</option>
+                            </select>
+                            <div>
+                            <textarea style="margin-top: 10px" onkeydown="inputDelete(event)" class="form-control" rows="2"
+                                      placeholder='Exit the loop directly if the command return value is less than or equal to 0 or evaluates to false. Example: return document.body.scrollWidth > 1000 or python D:/test.py, which are examples of JavaScript command and system command return values.' v-model='nowNode["parameters"]["breakCode"]'></textarea>
+                                <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"]["breakCodeWaitTime"]'></input>
+                            </div>
+                        </div>
+                    </div>
                 </div>
 
                 <div class="elements" v-if="nodeType==9">
@@ -240,11 +431,27 @@
                         <option value = 2>Element inside current page</option>
                         <option v-if="nowNode['isInLoop']" value = 3>Text inside current loop</option>
                         <option v-if="nowNode['isInLoop']" value = 4>Element inside current loop</option>
+                        <option value = 5>Return value of JavaScript command</option>
+                        <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>
-                    <label v-if='TClass'>Text/Xpath of Element: <span style="font-size: 30px!important;" title="Relative XPATH writing: start with /, e.g. the loop item XPATH is /html/body/div[1], your input is /*[@id='tab-customer'], then the final addressed xpath is: /html/body/div[1]/*[@id='tab-customer']">☺</span></label>
-                    <!-- <textarea onkeydown="inputDelete(event)" required placeholder="如果是当前循环包含元素,则输入相对元素的xpath。" v-if='TClass' class="form-control" rows="3" v-model='nowNode["parameters"]["value"]'></textarea> -->
-                    <textarea onkeydown="inputDelete(event)" required placeholder="If it is the current loop that contains the element, enter the xpath of the relative element" v-if='TClass' class="form-control" rows="3" v-model='nowNode["parameters"]["value"]'></textarea>
-                    
+                    <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 it is an element included in the current loop, input the relative XPath of the element." class="form-control" rows="3" v-model='nowNode["parameters"]["value"]'></textarea>
+                    </div>
+                    <div v-else-if='TClass > 0 && TClass < 7'>
+                        <label>Code/Script Content: </label>
+                        <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" v-model='nowNode["parameters"]["code"]' placeholder="If the return value is greater than 0 or true, the operations within this branch will be executed; otherwise, they will not be executed. For example: return document.body.scrollWidth > 1000 or python D:/test.py, representing examples of JS command and system command return values."></textarea>
+                        <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 v-else-if='TClass == 7'>
+                        <label>Code/Script Content (<a href="https://github.com/NaiboWang/EasySpider/wiki/Example-of-JavaScript-instruction-for-the-current-iteration-in-a-conditional-statement" target="_blank">Click here</a> for more examples): </label>
+                        <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" v-model='nowNode["parameters"]["code"]' placeholder="Enter the JS command for the current loop item. The loop item is represented by arguments[0]. If the return value is greater than 0 or true, the operations within this branch will be executed; otherwise, they will not be executed. For example: return arguments[0].innerText.length >= 5, which checks if the text length of the current loop item is greater than 5. Note that this is used in combination with element-related loop types (e.g., non-fixed element lists)."></textarea>
+                        <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>:</label>
@@ -283,6 +490,8 @@
         </div>
         <!-- /.modal -->
     </div>
+
+
 </body>
 <script src="FlowChart.js"></script>
 <script src="logic.js"></script>
@@ -312,9 +521,10 @@
                     url = "taskInfo.html?id="+getUrlParam("id")+"&lang=en&type="+getUrlParam("type")+"&wsport="+getUrlParam("wsport")+"&backEndAddressServiceWrapper=" + this.backEndAddressServiceWrapper
                 }
                 window.location.href = url;
-            },
+            }
         }
     });
+
 </script>
 
 </html>

+ 23 - 6
ElectronJS/src/taskGrid/FlowChart.js

@@ -40,6 +40,7 @@ var app = new Vue({
         paras: { "parameters": [] }, //提取数据的参数列表
         TClass: -1, //条件分支的条件类别
         paraIndex: 0, //当前参数的index
+        XPaths: "",
     },
     watch: {
         nowArrow: { //变量发生变化的时候进行一些操作
@@ -86,6 +87,13 @@ var app = new Vue({
         },
     },
     methods: {
+        changeXPaths: function (XPaths){
+            let result = "";
+            for (let i = 0; i < XPaths.length; i++) {
+                result += XPaths[i] + "\n";
+            }
+            this.XPaths = result;
+        },
         modifyParas: function(i) { //修改第i个参数
             this.paraIndex = i;
         },
@@ -539,18 +547,27 @@ 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("Do you really want to delete the selected operation?")) {
             deleteElement();
-        }
+        // }
     } else { //ctrl+s保存服务
-        var currKey = 0,
-            e = e || event || window.event;
+        let currKey = 0;
         currKey = e.keyCode || e.which || e.charCode;
         if (currKey == 83 && (e.ctrlKey || e.metaKey)) {
             $('#save').click();
-            return false;
+            return true;
+        } else if (currKey == 116) {
+            location.reload();
+        } else if (currKey == 123) {
+            console.log("打开devtools")
+            let command = new WebSocket("ws://localhost:8084")
+            command.onopen = function() {
+                let message = {
+                    type: 6, //消息类型,0代表连接操作
+                };
+                this.send(JSON.stringify(message));
+            };
         }
-
     }
 }
 

+ 51 - 51
ElectronJS/src/taskGrid/FlowChart_CN.html

@@ -76,7 +76,7 @@
                             </div>
                             <div class="modal-body">
                                 <label>以下提供除默认生成的XPath外其余等价的XPath,都能定位到同一个元素(但不完全准确,可能可以定位到除该元素外的其他元素,因此只是提供在这里作为参考)。 </label>
-                                <label>每行一个XPath(可使用预装的XPath Helper扩展调试):</label>
+                                <label>每行一个XPath(可使用预装的XPath Helper扩展调试)</label>
                                 <textarea id="otherXPaths" onkeydown="inputDelete(event)" class="form-control" rows="4">{{XPaths}}</textarea>
                                 <div>
                                     <img src="../img/XPather_helper.png" style="width:50%;height:50%; margin: 10px auto"></div>
@@ -100,12 +100,12 @@
                     <div v-if='!useLoop'>
 <!--                        <label>url:</label>-->
 <!--                        <input onkeydown="inputDelete(event)" class="form-control" v-model='nowNode["parameters"]["url"]'></input>-->
-                        <label>链接(每行一个链接,有多少行链接整个任务流程就会被执行多少次):</label>
+                        <label>链接(每行一个链接,有多少行链接整个任务流程就会被执行多少次)</label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["links"]'></textarea>
                     </div>
                     <label>页面加载最长等待时间(秒):</label>
                     <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['maxWaitTime']" type="number" required></input>
-                    <label>打开网页后是否向下滚动:</label>
+                    <label>打开网页后是否向下滚动</label>
                     <select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
                         <option value = 0>不滚动</option>
                         <option value = 1>向下滚动一屏</option>
@@ -123,13 +123,13 @@
                         <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='useLoop'></input>使用循环内的元素</p>
                     </div>
                     <div v-if='!useLoop'>
-                        <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>
+                        <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)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
                         <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode['parameters']['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">点此查看其他等价的XPath</button></p>
                     </div>
                     <label>点击后页面加载最长等待时间(秒):</label>
                     <input onkeydown="inputDelete(event)" class="form-control" v-model.number="nowNode['parameters']['maxWaitTime']" type="number" required></input>
-                    <label>点击后是否向下滚动页面:</label>
+                    <label>点击后是否向下滚动页面</label>
                     <select v-model='nowNode["parameters"]["scrollType"]' class="form-control">
                         <option value = 0>不滚动</option>
                         <option value = 1>向下滚动一屏</option>
@@ -146,14 +146,14 @@
                     </p>
                     <div :class="{collapse: true, 'show': nowNode['parameters']['beforeJS'].length!=0 || nowNode['parameters']['afterJS'].length!=0}" id="collapseExample">
                         <div>
-                            <label>点击该元素<strong>前</strong>针对该元素执行一段JavaScript脚本: </label>
+                            <label>点击该元素<strong>前</strong>针对该元素执行一段JavaScript脚本 </label>
                             <textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
-                                      placeholder='该元素用arguments[0]来表示,示例JS代码:arguments[0].innerText = arguments[0].innerText.replace("上海","Shanghai")即实现了将元素文字中所有的“上海”替换成”Shanghai“的功能,然后后续如提取数据时就会提取到替换后的值。' v-model='nowNode["parameters"]["beforeJS"]'></textarea>
-                            <label>最长等待脚本执行时间(0代表无限等待): </label>
+                                      placeholder='该元素用arguments[0]来表示,示例JS代码:arguments[0].innerText = arguments[0].innerText.replace("上海","Shanghai")即实现了将元素文字中的“上海”替换成”Shanghai“的功能,然后后续如提取数据时就会提取到替换后的值。' v-model='nowNode["parameters"]["beforeJS"]'></textarea>
+                            <label>最长等待脚本执行时间(0代表无限等待) </label>
                             <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["beforeJSWaitTime"]'></input>
-                            <label>点击该元素<strong>后</strong>针对该元素执行一段JavaScript脚本: </label>
+                            <label>点击该元素<strong>后</strong>针对该元素执行一段JavaScript脚本 </label>
                             <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" placeholder='该元素用arguments[0]来表示,示例JS代码:arguments[0].click()即点击此元素' v-model='nowNode["parameters"]["afterJS"]'></textarea>
-                            <label>最长等待脚本执行时间(0代表无限等待): </label>
+                            <label>最长等待脚本执行时间(0代表无限等待) </label>
                             <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["afterJSWaitTime"]'></input>
                         </div>
                     </div>
@@ -187,10 +187,10 @@
                         </table>
                     </div>
                     <div style="font-size: 13px;" v-if="paraIndex<paras.parameters.length">
-                        <p>当前编辑字段参数名(点击字段的“修改”选项切换参数): </p>
+                        <p>当前编辑字段参数名(点击字段的“修改”选项切换参数) </p>
                         <label><strong>{{paras.parameters[paraIndex]["name"]}}</strong></label>
                         <p v-if="nowNode['isInLoop']"><input onkeydown="inputDelete(event)" type="checkbox" v-model='paras.parameters[paraIndex]["relative"]'></input>使用相对循环内的XPATH</p>
-                        <p>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></p>
+                        <p>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></p>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='paras.parameters[paraIndex]["relativeXPath"]'></textarea>
                         <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(paras.parameters[paraIndex]['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">点此查看其他等价的XPath</button></p>
                         <p style="margin-top: 10px">
@@ -200,14 +200,14 @@
                         </p>
                         <div :class="{collapse: true, 'show': paras.parameters[paraIndex]['beforeJS'].length!=0 || paras.parameters[paraIndex]['afterJS'].length!=0}" id="elementAdvanced">
                             <div>
-                                <label>提取该元素数据<strong>前</strong>针对该元素执行一段JavaScript脚本: </label>
+                                <label>提取该元素数据<strong>前</strong>针对该元素执行一段JavaScript脚本 </label>
                                 <textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
-                                          placeholder='该元素用arguments[0]来表示,示例JS代码:arguments[0].innerText = arguments[0].innerText.replace("上海","Shanghai")即实现了将元素文字中所有的“上海”替换成”Shanghai“的功能,然后后续如提取数据时就会提取到替换后的值。' v-model='paras.parameters[paraIndex]["beforeJS"]'></textarea>
-                                <label>最长等待脚本执行时间(0代表无限等待): </label>
+                                          placeholder='该元素用arguments[0]来表示,示例JS代码:arguments[0].innerText = arguments[0].innerText.replace("上海","Shanghai")即实现了将元素文字中的“上海”替换成”Shanghai“的功能,然后后续如提取数据时就会提取到替换后的值。' v-model='paras.parameters[paraIndex]["beforeJS"]'></textarea>
+                                <label>最长等待脚本执行时间(0代表无限等待) </label>
                                 <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='paras.parameters[paraIndex]["beforeJSWaitTime"]'></input>
-                                <label>提取该元素数据<strong>后</strong>针对该元素执行一段JavaScript脚本: </label>
+                                <label>提取该元素数据<strong>后</strong>针对该元素执行一段JavaScript脚本 </label>
                                 <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" placeholder='该元素用arguments[0]来表示,示例JS代码:arguments[0].click()即点击此元素' v-model='paras.parameters[paraIndex]["afterJS"]'></textarea>
-                                <label>最长等待脚本执行时间(0代表无限等待): </label>
+                                <label>最长等待脚本执行时间(0代表无限等待) </label>
                                 <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='paras.parameters[paraIndex]["afterJSWaitTime"]'></input>
                             </div>
                         </div>
@@ -218,7 +218,7 @@
                             <option :value = 2>innerHTML</option>
                             <option :value = 3>outerHTML</option>
                             <option :value = 4>背景图片地址</option>
-                            <option :value = 5>页面URL</option>
+                            <option :value = 5>页面网址</option>
                             <option :value = 6>页面标题</option>
                             <option :value = 7>元素截图</option>
                             <option :value = 8>OCR识别文字</option>
@@ -227,10 +227,10 @@
                             <option :value = 11>当前选择框选中的选项文本</option>
                         </select>
                         <div v-if='paras.parameters[paraIndex]["contentType"] == 9'>
-                            <label>JavaScript代码: </label>
+                            <label>JavaScript代码</label>
                             <textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
                                       placeholder='该元素用arguments[0]来表示,示例:return arguments[0].innerText + "美元",即实现了提取该元素innerText并后面加“美元”的功能。' v-model='paras.parameters[paraIndex]["JS"]'></textarea>
-                            <label>最长等待脚本执行时间(0代表无限等待): </label>
+                            <label>最长等待脚本执行时间(0代表无限等待) </label>
                             <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='paras.parameters[paraIndex]["JSWaitTime"]'></input>
                         </div>
                         <label>节点类型</label>
@@ -242,7 +242,7 @@
                             <option :value = 4>图片地址</option>
                         </select>
                         <div v-if='paras.parameters[paraIndex]["nodeType"] == 4'>
-                            <label>提取图片地址后是否同时下载图片</label>
+                            <label>提取图片地址后是否同时<b>下载图片</b></label>
                             <select v-model='paras.parameters[paraIndex]["downloadPic"]' class="form-control">
                                 <option :value = 0>否</option>
                                 <option :value = 1>是</option>
@@ -253,9 +253,9 @@
 <!--                            <option :value = 0>普通提取</option>-->
 <!--                            <option :value = 1>OCR提取</option>-->
 <!--                        </select>-->
-                        <label>参数描述:</label>
+                        <label>参数描述</label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" style="min-height: 60px" v-model='paras.parameters[paraIndex]["desc"]'></textarea>
-                        <label>元素找不到时的值:</label>
+                        <label>元素找不到时的值</label>
                         <input onkeydown="inputDelete(event)" class="form-control" v-model='paras.parameters[paraIndex]["default"]'></textarea>
 
                     </div>
@@ -269,11 +269,11 @@
                         <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='useLoop'></input>使用循环内的文本(不勾选则每次输入的文本为下方“输入值”文本框内的文本,勾选后会使用所在循环内设置的文本)</p>
                     </div>
                     <div v-if='!useLoop'>
-                        <label>输入值:</label>
+                        <label>输入值</label>
                         <input onkeydown="inputDelete(event)" class="form-control" v-model='nowNode["parameters"]["value"]'></input>
                     </div>
 
-                    <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>
+                    <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)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
                     <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode.parameters['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">点此查看其他等价的XPath</button></p>
                     <p style="margin-top: 10px">
@@ -283,14 +283,14 @@
                     </p>
                     <div :class="{collapse: true, 'show': nowNode['parameters']['beforeJS'].length!=0 || nowNode['parameters']['afterJS'].length!=0}" id="inputAdvanced">
                         <div>
-                            <label>对该元素输入文字<strong>前</strong>针对该元素执行一段JavaScript脚本: </label>
+                            <label>对该元素输入文字<strong>前</strong>针对该元素执行一段JavaScript脚本 </label>
                             <textarea onkeydown="inputDelete(event)" class="form-control" rows="2"
-                                      placeholder='该元素用arguments[0]来表示,示例JS代码:arguments[0].innerText = arguments[0].innerText.replace("上海","Shanghai")即实现了将元素文字中所有的“上海”替换成”Shanghai“的功能,然后后续如提取数据时就会提取到替换后的值。' v-model='nowNode["parameters"]["beforeJS"]'></textarea>
-                            <label>最长等待脚本执行时间(0代表无限等待): </label>
+                                      placeholder='该元素用arguments[0]来表示,示例JS代码:arguments[0].innerText = arguments[0].innerText.replace("上海","Shanghai")即实现了将元素文字中的“上海”替换成”Shanghai“的功能,然后后续如提取数据时就会提取到替换后的值。' v-model='nowNode["parameters"]["beforeJS"]'></textarea>
+                            <label>最长等待脚本执行时间(0代表无限等待) </label>
                             <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["beforeJSWaitTime"]'></input>
-                            <label>对该元素输入文字<strong>后</strong>针对该元素执行一段JavaScript脚本: </label>
+                            <label>对该元素输入文字<strong>后</strong>针对该元素执行一段JavaScript脚本 </label>
                             <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" placeholder='该元素用arguments[0]来表示,示例JS代码:arguments[0].click()即点击此元素' v-model='nowNode["parameters"]["afterJS"]'></textarea>
-                            <label>最长等待脚本执行时间(0代表无限等待): </label>
+                            <label>最长等待脚本执行时间(0代表无限等待) </label>
                             <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["afterJSWaitTime"]'></input>
                         </div>
                     </div>
@@ -305,14 +305,14 @@
                     </select>
 
                     <div>
-                        <label>代码/脚本内容: </label>
+                        <label>代码/脚本内容 </label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["code"]' placeholder="输入JS或系统命令,如:document.body.innerText = '1' 或 python D:/test.py,分别为JS命令和系统命令示例。如选择针对当前循环项的JS脚本,则循环项元素用arguments[0]表示,如arguments[0].style.color = 'blue'"></textarea>
                         <p style="margin-top: 15px">是否将执行后的输出/返回值作为字段记录:</p>
                         <p><select v-model='nowNode["parameters"]["recordASField"]' class="form-control">
                             <option value = 0>否</option>
                             <option value = 1>是</option>
                         </select></p>
-                        <label>最长等待脚本执行时间(0代表无限等待): </label>
+                        <label>最长等待脚本执行时间(0代表无限等待) </label>
                         <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["waitTime"]'></input>
                     </div>
 
@@ -323,7 +323,7 @@
                 </div>
 
                 <div class="elements" v-if="nodeType==6">
-                    <label>XPath: </label>
+                    <label>XPath </label>
                     <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
                     <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode['parameters']['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">点此查看其他等价的XPath</button></p>
 
@@ -345,7 +345,7 @@
                         <p><input onkeydown="inputDelete(event)" type="checkbox" v-model='useLoop'></input>使用循环内的元素</p>
                     </div>
                     <div v-if='!useLoop'>
-                        <label>XPath: </label>
+                        <label>XPath </label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
                         <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode['parameters']['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">点此查看其他等价的XPath</button></p>
                     </div>
@@ -355,7 +355,7 @@
 
                 <div class="elements" v-if="nodeType==8">
                     <!-- 循环选项 -->
-                    <label>循环类型:</label>
+                    <label>循环类型</label>
                     <select v-model='loopType' class="form-control">
                         <option value = 0>单个元素(多用于循环点击下一页)</option>
                         <option value = 1>不固定元素列表</option>
@@ -366,26 +366,26 @@
                         <option value = 6>系统命令返回值</option>
                       </select>
                     <div v-if='parseInt(loopType) < 2'>
-                        <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>
+                        <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)" class="form-control" rows="2" v-model='nowNode["parameters"]["xpath"]'></textarea>
                         <p><button type="button" data-toggle="modal" data-target="#myModal_XPath" @click="changeXPaths(nowNode.parameters['allXPaths'])" class="btn btn-primary" style="margin-top: 10px">(测试功能)点此查看其他可能的XPath写法</button></p>
                     </div>
                     <div v-else-if='parseInt(loopType) == 2'>
-                        <label>XPath列表:</label>
+                        <label>XPath列表</label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" placeholder="每行一个XPath"  v-model='nowNode["parameters"]["pathList"]'></textarea>
                     </div>
                     <div v-else-if='parseInt(loopType) < 5'>
-                        <label>内容列表:</label>
+                        <label>内容列表</label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" placeholder="每行一个文本/网址" v-model='nowNode["parameters"]["textList"]'></textarea>
                     </div>
                     <div v-else-if='parseInt(loopType) < 7'>
-                        <label>代码/脚本内容: </label>
+                        <label>代码/脚本内容 </label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" v-model='nowNode["parameters"]["code"]' placeholder="命令返回值大于0或为真则继续循环,否则停止循环。如:return document.body.scrollWidth > 1000 或 python D:/test.py,分别为JS命令和系统命令返回值示例。"></textarea>
-                        <label>最长等待脚本执行时间(0代表无限等待): </label>
+                        <label>最长等待脚本执行时间(0代表无限等待) </label>
                         <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["waitTime"]'></input>
                     </div>
                     <!-- 这里添加退出循环条件,找不到元素肯定退出循环 -->
-                    <label v-if='parseInt(loopType) == 0'>最多执行循环次数(0代表无限循环直到找不到元素为止):</label>
+                    <label v-if='parseInt(loopType) == 0'>最多执行循环次数(0代表无限循环直到找不到元素为止)</label>
                     <input onkeydown="inputDelete(event)" required v-if='parseInt(loopType) == 0' class="form-control" type="number" v-model.number='nowNode["parameters"]["exitCount"]'></input>
 
 
@@ -403,7 +403,7 @@
                     <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>(高级操作)使用代码/脚本定义循环退出条件: </label></p>
+                            <p><label>(高级操作)使用代码/脚本定义循环退出条件 </label></p>
                             <select v-model='nowNode["parameters"]["breakMode"]' class="form-control" style="font-weight: bold">
                                 <option value = 0>不设置脚本(选择这个下面写了脚本也不会执行)</option>
                                 <option value = 1>JavaScript脚本</option>
@@ -412,7 +412,7 @@
                             <div>
                                 <textarea style="margin-top: 10px" onkeydown="inputDelete(event)" class="form-control" rows="2"
                                           placeholder='命令返回值小于等于0或为假时则直接退出循环,不管其他条件如何。如:return document.body.scrollWidth > 1000 或 python D:/test.py,分别为JS命令和系统命令返回值示例。' v-model='nowNode["parameters"]["breakCode"]'></textarea>
-                                <label>最长等待脚本执行时间(0代表无限等待): </label>
+                                <label>最长等待脚本执行时间(0代表无限等待)</label>
                                 <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["breakCodeWaitTime"]'></input>
                             </div>
                         </div>
@@ -424,7 +424,7 @@
                 </div>
 
                 <div class="elements" v-if="nodeType==10">
-                    <label>条件类型:</label>
+                    <label>条件类型</label>
                     <select v-model='TClass' class="form-control">
                         <option value = 0>无条件</option>
                         <option value = 1>当前页面包括文本</option>
@@ -436,19 +436,19 @@
                         <option v-if="nowNode['isInLoop']" value = 7>针对当前循环项的JavaScript命令返回值</option>
                       </select>
                     <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>
+                        <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。" class="form-control" rows="3" v-model='nowNode["parameters"]["value"]'></textarea>
                     </div>
                     <div v-else-if='TClass > 0 && TClass < 7'>
-                        <label>代码/脚本内容: </label>
+                        <label>代码/脚本内容 </label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" v-model='nowNode["parameters"]["code"]' placeholder="命令返回值大于0或为真则执行此分支内操作,否则不执行。如:return document.body.scrollWidth > 1000 或 python D:/test.py,分别为JS命令和系统命令返回值示例。"></textarea>
-                        <label>最长等待脚本执行时间(0代表无限等待): </label>
+                        <label>最长等待脚本执行时间(0代表无限等待) </label>
                         <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["waitTime"]'></input>
                     </div>
                     <div v-else-if='TClass == 7'>
-                        <label>代码/脚本内容(<a href="https://github.com/NaiboWang/EasySpider/wiki/Example-of-JavaScript-instruction-for-the-current-iteration-in-a-conditional-statement" target="_blank">点击此处</a>查看更多示例): </label>
+                        <label>代码/脚本内容(<a href="https://github.com/NaiboWang/EasySpider/wiki/Example-of-JavaScript-instruction-for-the-current-iteration-in-a-conditional-statement" target="_blank">点击此处</a>查看更多示例) </label>
                         <textarea onkeydown="inputDelete(event)" class="form-control" rows="3" v-model='nowNode["parameters"]["code"]' placeholder="输入针对该循环项的JS命令,该循环项用arguments[0]表示,返回值大于0或为真则执行此分支内操作,否则不执行。如:return arguments[0].innerText.length >=5 即判断当前循环项的文本长度是否大于5,注意要配合循环类型为元素相关(如不固定元素列表)使用。"></textarea>
-                        <label>最长等待脚本执行时间(0代表无限等待): </label>
+                        <label>最长等待脚本执行时间(0代表无限等待) </label>
                         <input onkeydown="inputDelete(event)" required class="form-control" type="number" v-model.number='nowNode["parameters"]["waitTime"]'></input>
                     </div>
                 </div>
@@ -475,9 +475,9 @@
                 <div class="modal-body">
                     <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>
-                    <label>任务名称:</label>
+                    <label>任务名称</label>
                     <input onkeydown="inputDelete(event)" required name="serviceName" value="新web采集任务" id="serviceName" class="form-control"></input>
-                    <label>任务描述:</label>
+                    <label>任务描述</label>
                     <textarea onkeydown="inputDelete(event)" id="serviceDescription" name="serviceDescription" class="form-control" rows="3"></textarea>
                 </div>
                 <div class="modal-footer">

+ 116 - 23
ElectronJS/src/taskGrid/logic.js

@@ -24,25 +24,56 @@ ws.onclose = function() {
     // 关闭 websocket
     console.log("连接已关闭...");
 };
-var ttt;
+let old_title = "";
 ws.onmessage = function(evt) {
     evt = JSON.parse(evt.data);
     console.log(evt);
-    if (evt["type"] == "special") { //如果不是特殊处理的话,默认全部是增加元素操作
-
+    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]["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 "Collect" + match[1];
+    } else {
+        return "New Web Collection Task";
+    }
+}
+
 function handleAddElement(msg) {
     if (msg["type"] == "openPage") {
         addElement(1, msg);
-    } else if (msg["type"] == "InputText") {
-        addElement(4, 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);
@@ -53,6 +84,7 @@ function handleAddElement(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"];
@@ -110,34 +142,63 @@ function addParameters(t) {
         useLoop: false, //是否使用循环中的元素
         xpath: "", //xpath
         wait: 0, //执行后等待
+        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"] = 0; //滚动次数
+        t["parameters"]["scrollCount"] = 1; //滚动次数
+        t["parameters"]["scrollWaitTime"] = 1; //滚动后等待时间
     } else if (t.option == 2) { //点击元素
         t["parameters"]["scrollType"] = 0; //滚动类型,0不滚动,1向下滚动1屏,2滚动到底部
-        t["parameters"]["scrollCount"] = 0; //滚动次数
+        t["parameters"]["scrollCount"] = 1; //滚动次数
+        t["parameters"]["scrollWaitTime"] = 1; //滚动后等待时间
+        t["parameters"]["maxWaitTime"] = 10; //最长等待时间
         t["parameters"]["paras"] = []; //默认参数列表
+        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; //是否记录脚本输出
     } else if (t.option == 8) { //循环
         t["parameters"]["scrollType"] = 0; //滚动类型,0不滚动,1向下滚动1屏,2滚动到底部
-        t["parameters"]["scrollCount"] = 0; //滚动次数
+        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; //最长等待时间
     }
 }
 
@@ -150,15 +211,27 @@ function modifyParameters(t, para) {
         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"];
-    } else if (t.option == 2) { //鼠标点击事件
+        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"] = "Loop click next page"
         } else {
@@ -170,7 +243,7 @@ function modifyParameters(t, para) {
         }
     } else if (t.option == 3) { //采集数据
         for (let i = 0; i < para["parameters"].length; i++) {
-            para["parameters"][i]["default"] = ""; //找不到元素时候的默认值
+            changeGetDataParameters(para, i);
         }
         t["parameters"]["paras"] = para["parameters"];
     }
@@ -204,13 +277,13 @@ var sId = getUrlParam('id');
 var backEndAddressServiceWrapper = getUrlParam("backEndAddressServiceWrapper");
 
 function saveService(type) {
-    var serviceId = $("#serviceId").val();
-    var text = "Confirm to save this task (If cannot click, can press Enter)? ";
+    let serviceId = $("#serviceId").val();
+    let text = "Confirm to save this task (If cannot click, can press Enter)? ";
     if (type == 1) { //任务另存为
         serviceId = -1;
         text = "Confirm to save as another task in the system (If cannot click, can press Enter)?";
     }
-    if (confirm(text)) {
+    // if (confirm(text)) {
         let serviceName = $("#serviceName").val();
         let url = $("#url").val();
         let serviceDescription = $("#serviceDescription").val();
@@ -233,7 +306,6 @@ function saveService(type) {
                             nodeId: i, //记录操作位于的节点位置,重要!!!
                             nodeName: nodeList[i]["title"],
                             value: nodeList[i]["parameters"]["links"],
-                            // desc: "要采集的网址列表,多行以\\n分开",
                             desc: "List of URLs to be collected, separated by \\n for multiple lines",
                             type: "string",
                             exampleValue: nodeList[i]["parameters"]["links"]
@@ -249,7 +321,6 @@ function saveService(type) {
                             name: "inputText_" + inputIndex++,
                             nodeName: nodeList[i]["title"],
                             nodeId: i,
-                            // desc: "要输入的文本,如京东搜索框输入:电脑",
                             desc: "The text to be entered, such as 'computer' at eBay search box",
                             type: "string",
                             exampleValue: nodeList[i]["parameters"]["value"],
@@ -258,26 +329,23 @@ function saveService(type) {
                     }
                 } else if (nodeList[i]["option"] == 8) //循环操作
                 {
-                    if (parseInt(nodeList[i]["parameters"]["loopType"]) > 2) {
+                    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分开",
                             desc:"Text/URL to be entered, multiple lines should be separated by \\n",
                             type: "string",
                             exampleValue: nodeList[i]["parameters"]["textList"],
                             value: nodeList[i]["parameters"]["textList"],
                         });
-                    } //循环中的循环输入文本或循环输入网址
-                    else if (parseInt(nodeList[i]["parameters"]["loopType"]) == 0) {
+                    } 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代表无限循环)",
                             desc: "Number of loop executions, 0 means unlimited loops (until element not found)",
                             type: "int",
                             exampleValue: nodeList[i]["parameters"]["exitCount"],
@@ -298,6 +366,28 @@ function saveService(type) {
                             });
                         }
                     }
+                } 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: "",
+                        });
+                    }
                 } else if (nodeList[i]["option"] == 9) //条件判断
                 {
                     containJudge = true;
@@ -309,21 +399,24 @@ function saveService(type) {
             "name": serviceName,
             "url": url,
             "links": links,
+            "create_time": new Date().toLocaleString(),
+            "version": "0.3.1",
             "containJudge": containJudge,
             "desc": serviceDescription,
             "inputParameters": inputParameters,
             "outputParameters": outputParameters,
             "graph": nodeList, //图结构要存储下来
         };
-        $.post(backEndAddressServiceWrapper + "/manageTask", { paras: JSON.stringify(serviceInfo) }, function(result) { $("#serviceId").val(parseInt(result)) });
+        $.post(backEndAddressServiceWrapper + "/manageTask", { paras: JSON.stringify(serviceInfo) },
+            function(result) { $("#serviceId").val(parseInt(result)) });
         // alert("保存成功!");
         $('#myModal').modal('hide');
         $("#tip").slideDown(); //提示框
-        fadeout = setTimeout(function() {
+        let fadeout = setTimeout(function() {
             $("#tip").slideUp();
         }, 2000);
 
-    }
+    // }
 }
 
 //点击保存任务按钮时的处理

+ 1 - 1
ExecuteStage/easyspider_executestage.py

@@ -182,7 +182,7 @@ def customOperation(node, loopValue, loopPath, index):
             output = execute_code(codeMode, code, max_wait_time, element)
         except:
             output = ""
-            recordLog("JavaScript execution failed")
+            print("JavaScript execution failed")
     else:
         output = execute_code(codeMode, code, max_wait_time)
     recordASField = int(paras["recordASField"])

+ 4 - 4
Extension/manifest_v3/src/content-scripts/global.js

@@ -245,8 +245,8 @@ function parameterName(value){
             case "_图片地址": return "_image_address";
             case "背景图片地址": return "background_image_address";
             case "_背景图片": return "_background_image";
-            case "页面URL": return "page_url";
-            case "_页面URL": return "_page_url";
+            case "页面网址": return "page_url";
+            case "_页面网址": return "_page_url";
             case "页面标题": return "page_title";
             case "_页面标题": return "_page_title";
             case "选择的选项文本": return "selected_option_text";
@@ -260,7 +260,7 @@ function parameterName(value){
 
 //根据nodelist列表内的元素生成参数列表
 //适合:nodelist中的元素为同类型元素
-//type:0为全部文本 1为节点内直接的文字 2为innerhtml 3为outerhtml 4为backgroundImg 5为当前页面URL 6为当前页面标题 7为元素截图 8为OCR识别 9为JavaScript返回值 10为选择框选择的值 11为选择框选择的文本
+//type:0为全部文本 1为节点内直接的文字 2为innerhtml 3为outerhtml 4为backgroundImg 5为当前页面网址 6为当前页面标题 7为元素截图 8为OCR识别 9为JavaScript返回值 10为选择框选择的值 11为选择框选择的文本
 //nodetype:0,对应全type0123
 //nodetype:1 链接,对应type0123
 //nodetype:2 链接地址 对应type0
@@ -322,7 +322,7 @@ export function generateParameters(type, linktext = true, linkhref = true) {
                 pname = parameterName("背景图片地址");
             } else if(type == 5){
                 ndText = window.location.href;
-                pname = parameterName("页面URL");
+                pname = parameterName("页面网址");
             } else if(type == 6){
                 ndText = document.title;
                 pname = parameterName("页面标题");

+ 2 - 2
Extension/manifest_v3/src/content-scripts/toolkit.vue

@@ -10,7 +10,7 @@
             <p style="margin-bottom:10px;display:inline-block">特殊点选模式</p>
             <div class="innercontent" v-if = "list.nl.length==0">
               <div><a v-on:mousedown="getCurrentTitle">采集当前页面的标题</a><span title="">☺</span></div>
-              <div><a v-on:mousedown="getCurrentURL">采集当前页面的URL地址</a><span title="">☺</span></div>
+              <div><a v-on:mousedown="getCurrentURL">采集当前页面的址</a><span title="">☺</span></div>
             </div>
             <p style="color:black; margin-top: 10px">● 鼠标移动到元素上后,请<strong>右键</strong>点击或者按<strong>F7</strong>键选中页面元素。
             </p>
@@ -236,7 +236,7 @@
                     v-on:mousedown="loopClickEveryElement">Loop-click every {{ tname() | toEng}}</a><span title="">☺</span>
                 </div>
                 <div v-if="tname()!='选择框' && tname()!='文本框' && !selectedDescendents"><a
-                    v-on:mousedown="loopMouseMove">Loop-move-to every {{ tname() }}</a><span title="">☺</span></div>
+                    v-on:mousedown="loopMouseMove">Loop-mouse-move to every {{ tname() | toEng}}</a><span title="">☺</span></div>
                 <div><a v-on:mousedown="revoke">Revoke selection</a><span title="">☺</span></div>
               </div>
             </div>