Forráskód Böngészése

审核新加退回功能

mxs 7 hónapja
szülő
commit
3146e62703

+ 0 - 1
backend/config.yaml

@@ -8,7 +8,6 @@
 #disable-extensions: true
 ## 默认浏览器检查
 #default-browser-check: false
-isOnly4MainSite: false
 browserLoadResourceTimeout: 5
 #验证码解析
 timeout: 15

+ 41 - 40
backend/script/script.go

@@ -608,7 +608,7 @@ func getCodeByPay(path, stype, head, cookie string, proxy bool) (code string, re
 	return
 }
 func getCode(b []byte, stype string, free bool) (code string, err error) {
-	qu.Debug("验证码类型:", stype)
+	qu.Debug("验证码类型:", stype, ",是否免费:", free)
 	//解析验证码
 	request := req.C().R().
 		SetHeader("accept", "application/json").
@@ -637,25 +637,26 @@ func getCode(b []byte, stype string, free bool) (code string, err error) {
 	}
 	var result map[string]interface{}
 	err = json.Unmarshal(resp.Bytes(), &result)
-	qu.Debug("验证码解析结果:", result)
+	qu.Debug("验证码解析结果:", free, result)
 	if err == nil && result != nil {
 		if free {
 			r, _ := result["r"].(map[string]interface{})
-			codeTmp := fmt.Sprint(r["code"])
+			codeTmp := qu.ObjToString(r["code"])
 			if len(codeTmp) >= 4 || stype == "6001" && codeTmp != "" {
-				qu.Debug("验证码解析结果:", codeTmp)
 				return codeTmp, nil
 			}
 		} else {
-			if codeTmp, ok := result["r"].(map[string]interface{})["pic_str"].(string); ok && codeTmp != "" && len(codeTmp) >= 4 {
-				return codeTmp, nil
+			if codeTmp, ok := result["r"].(map[string]interface{})["pic_str"].(string); ok && codeTmp != "" {
+				if stype == "6001" || len(codeTmp) >= 4 {
+					return codeTmp, nil
+				}
 			}
 		}
 	}
 	return
 }
 
-// 截屏
+// AnalyzeCodeScreenShot 截屏解析验证码
 func (b *GLBrowser) AnalyzeCodeScreenShot(tabTitle, tabUrl, selector string, selectorType int, timeout int64, stype string) (code string, err error) {
 	ctx, err := b.findTabContext(tabTitle, tabUrl, timeout)
 	if err != nil {
@@ -681,7 +682,7 @@ func (b *GLBrowser) AnalyzeCodeScreenShot(tabTitle, tabUrl, selector string, sel
 	if err = ioutil.WriteFile("code.png", bt, 0755); err != nil {
 		qu.Debug(err)
 	}
-	code, err = getCode(bt, stype, false) //免费
+	code, err = getCode(bt, stype, true) //免费
 	if err != nil || code == "" {
 		code, err = getCode(bt, stype, false) //收费
 	}
@@ -856,35 +857,35 @@ func (b *GLBrowser) BindLuaState(s *lua.LState, recordId string) {
 		}
 		return 1
 	}))
-	s.SetGlobal("browser_analyzecode_bypath", s.NewFunction(func(S *lua.LState) int {
-		cookie := S.ToString(-1)
-		head := S.ToTable(-2)
-		stype := S.ToString(-3)
-		path := S.ToString(-4)
-		proxy := S.ToBool(-5)
-		headMap := TableToMap(head)
-		//qu.Debug("cookie----------", cookie)
-		//qu.Debug("headMap----------", headMap)
-		headJsonStr := ""
-		headByte, err := json.Marshal(headMap)
-		if err == nil {
-			headJsonStr = string(headByte)
-		}
-		code, respHead, respCookie := b.AnalyzeCodeByPath(path, stype, headJsonStr, cookie, proxy)
-		rhead, _ := json.Marshal(respHead)
-		respHeadMap := map[string]interface{}{}
-		json.Unmarshal(rhead, &respHeadMap)
-		hTable := MapToTable(respHeadMap)
-
-		rcookie, _ := json.Marshal(respCookie)
-		respCookieMap := []map[string]interface{}{}
-		json.Unmarshal(rcookie, &respCookieMap)
-		cTable := MapToTable(map[string]interface{}{"cookie": respCookieMap})
-		S.Push(lua.LString(code))
-		S.Push(hTable)
-		S.Push(cTable.RawGetString("cookie"))
-		return 3
-	}))
+	//s.SetGlobal("browser_analyzecode_bypath", s.NewFunction(func(S *lua.LState) int {
+	//	proxy := S.ToBool(-5)
+	//	url := S.ToString(-4)
+	//	stype := S.ToString(-3)
+	//	head := S.ToTable(-2)
+	//	cookie := S.ToString(-1)
+	//	headMap := TableToMap(head)
+	//	//qu.Debug("cookie----------", cookie)
+	//	//qu.Debug("headMap----------", headMap)
+	//	headJsonStr := ""
+	//	headByte, err := json.Marshal(headMap)
+	//	if err == nil {
+	//		headJsonStr = string(headByte)
+	//	}
+	//	code, respHead, respCookie := b.AnalyzeCodeByPath(url, stype, headJsonStr, cookie, proxy)
+	//	rhead, _ := json.Marshal(respHead)
+	//	respHeadMap := map[string]interface{}{}
+	//	json.Unmarshal(rhead, &respHeadMap)
+	//	hTable := MapToTable(respHeadMap)
+	//
+	//	rcookie, _ := json.Marshal(respCookie)
+	//	respCookieMap := []map[string]interface{}{}
+	//	json.Unmarshal(rcookie, &respCookieMap)
+	//	cTable := MapToTable(map[string]interface{}{"cookie": respCookieMap})
+	//	S.Push(lua.LString(code))
+	//	S.Push(hTable)
+	//	S.Push(cTable.RawGetString("cookie"))
+	//	return 3
+	//}))
 	//发布时间格式化
 	s.SetGlobal("browser_publishtime", s.NewFunction(func(l *lua.LState) int {
 		text := l.ToString(-1)
@@ -907,14 +908,14 @@ func (b *GLBrowser) BindLuaState(s *lua.LState, recordId string) {
 	//保存数据
 	s.SetGlobal("browser_savedata", s.NewFunction(func(l *lua.LState) int {
 		//fmt.Println("---browser_savedata---")
-		page := l.ToString(-2)
+		pageType := l.ToString(-2)
 		data := l.ToTable(-1)
 		result := TableToMap(data)
-		if page == "list" {
+		if pageType == "list" {
 			result["recordid"] = recordId
 		}
 		DataCache <- result
-		return 1
+		return 0
 	}))
 	//获取数据
 	s.SetGlobal("browser_getdata", s.NewFunction(func(l *lua.LState) int {

+ 40 - 27
frontend/src/components/spider/CodeEditor.vue

@@ -1,54 +1,67 @@
 <template>
-    <el-dialog title="编辑代码" :model-value="props.show" @update:model-value="updateModelValue" :close-on-click-modal="false" width="70%">
-        <div class="textarea-container">
-            <el-input v-model="dialogInfo.text" class="textarea" autofocus :autosize="{ minRows: 12, maxRows: 13 }" type="textarea" placeholder="Please input"></el-input>
-        </div>
-        <div slot="footer" class="dialog-footer" style="text-align:right">
-            <el-button @click="handleSave(false)">取 消</el-button>
-            <el-button type="primary" @click="handleSave(false)">保 存</el-button>
-        </div>
-    </el-dialog>
+  <el-dialog title="编辑代码" :model-value="props.show" @update:model-value="updateModelValue" :close-on-click-modal="false" width="70%">
+    <el-button-group>
+      <el-tooltip v-for="item,index in TemplateJsCode.InitListPageJsCodes" :key="index" class="box-item" effect="dark" :content="item.tooltip" placement="top-start">
+        <el-button size="small" type="primary" @click='useInitPageJsCode(index)'>{{item.name}}</el-button>
+      </el-tooltip>
+    </el-button-group>
+    <div style="line-height: 6px;height: 6px;"></div>
+    <div class="textarea-container">
+      <el-input v-model="dialogInfo.text" class="textarea" autofocus :autosize="{ minRows: 12, maxRows: 13 }" type="textarea" placeholder="Please input"></el-input>
+    </div>
+    <div slot="footer" class="dialog-footer" style="text-align:right">
+      <el-button @click="handleSave(false)">取 消</el-button>
+      <el-button type="primary" @click="handleSave(false)">保 存</el-button>
+    </div>
+  </el-dialog>
 </template>
 <script setup>
 import { reactive } from 'vue';
+import { TemplateJsCode } from './jscodetpl.js'
 
 const emit = defineEmits(['update:show', 'save'])
 const props = defineProps({
-    show: {
-        type: Boolean,
-        default: false
-    }
+  show: {
+    type: Boolean,
+    default: false
+  }
 })
 
+//使用页面初始化模板
+const useInitPageJsCode=(index)=>{
+  let code =  TemplateJsCode.InitListPageJsCodes[index].code
+  dialogInfo.text = code
+}
+
 const dialogInfo = reactive({
-    key: '',
-    text: '',
+  key: '',
+  text: '',
 })
 
 const setPageData = ({ text, key }) => {
-    dialogInfo.text = text
-    dialogInfo.key = key
+  dialogInfo.text = text
+  dialogInfo.key = key
 }
 const handleSave = (f) => {
-    emit('update:show', f)
-    emit('save', {
-        text: dialogInfo.text,
-        key: dialogInfo.key,
-    })
+  emit('update:show', f)
+  emit('save', {
+    text: dialogInfo.text,
+    key: dialogInfo.key,
+  })
 }
 const updateModelValue = (e) => {
-    emit('update:show', e)
+  emit('update:show', e)
 }
 
 defineExpose({
-    setPageData
+  setPageData
 })
 </script>
 
 <style lang="scss" scoped>
 .textarea-container {
-    .textarea {
-        height: 300px;
-    }
+  .textarea {
+    height: 300px;
+  }
 }
 </style>

+ 17 - 2
frontend/src/components/spider/EditSpider.vue

@@ -247,7 +247,7 @@
 </template>
 
 <script setup>
-import { ref, reactive, defineEmits, computed, watchEffect } from 'vue';
+import { ref, reactive, defineEmits, defineProps, computed, watchEffect } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { TemplateJsCode } from './jscodetpl.js'
 import { Link } from '@element-plus/icons-vue'
@@ -262,6 +262,13 @@ const codeEditor =ref(null)
 const codeEditorShow = ref(false)
 
 const emit = defineEmits(['custom-event', 'data-tag', 'form-change']);
+const props = defineProps({
+  from: {
+    type: String,
+    default: ''
+  }
+})
+
 let originData = {}
 
 const dialogTitle = ref('仅编辑 CSS选择器部分')
@@ -371,10 +378,18 @@ const dialogVisible = ref(false)
 // 用户身份标识
 const userRole = computed(() => store.getters.userRole)
 const isDeveloper = computed(() => [USER_ROLE_DEVELOPER].includes(userRole.value))
+const isReviewer = computed(() => [USER_ROLE_REVIEWER].includes(userRole.value))
 
 // 待完成和未通过的爬虫可以保存,其他都不可以提交(并禁用保存按钮)
 const canSubmitStatusArr = [0, 2]
-const canSubmit = computed(() => canSubmitStatusArr.includes(formData.value.state) && isDeveloper.value)
+const userSubmitRolePass = computed(() => isDeveloper.value || isReviewer.value)
+const canSubmit = computed(() => {
+  if (props.from === 'reviewList') {
+    return userSubmitRolePass.value
+  } else {
+    return canSubmitStatusArr.includes(formData.value.state) && userSubmitRolePass.value
+  }
+})
 const savaButtonDisabled = computed(() => !canSubmit.value)
 
 //编辑器事件管理

+ 15 - 1
frontend/src/components/spider/RunSpiderDialog.vue

@@ -22,6 +22,12 @@ import { useStore } from 'vuex';
 import { USER_ROLE_ADMIN, USER_ROLE_DEVELOPER, USER_ROLE_REVIEWER } from '../../data/user'
 
 const emit = defineEmits(['save'])
+const props = defineProps({
+  from: {
+    type: String,
+    default: ''
+  }
+})
 
 const store = useStore();
 
@@ -34,10 +40,18 @@ const dialogTitle = ref('调试/运行')
 // 用户身份标识
 const userRole = computed(() => store.getters.userRole)
 const isDeveloper = computed(() => [USER_ROLE_DEVELOPER].includes(userRole.value))
+const isReviewer = computed(() => [USER_ROLE_REVIEWER].includes(userRole.value))
 
 // 待完成和未通过的爬虫可以保存,其他都不可以提交(并禁用保存按钮)
 const canSubmitStatusArr = [0, 2]
-const canSubmit = computed(() => canSubmitStatusArr.includes(formData.value.state) && isDeveloper.value)
+const userSubmitRolePass = computed(() => isDeveloper.value || isReviewer.value)
+const canSubmit = computed(() => {
+  if (props.from === 'reviewList') {
+    return userSubmitRolePass.value
+  } else {
+    return canSubmitStatusArr.includes(formData.value.state) && userSubmitRolePass.value
+  }
+})
 const savaButtonDisabled = computed(() => !canSubmit.value)
 
 const setPageData = (e) => {

+ 188 - 87
frontend/src/components/spider/jscodetpl.js

@@ -221,23 +221,23 @@ if ("{{.PublishTimeCss}}" != "") {//发布时间
 	if (tmp) ret["publishTime"] = tmp.getAttribute("title") || tmp.innerText
 }
 if ("{{.ContentCss}}" != "") {//正文内容
-  tmp = document.querySelector("{{.ContentCss}}") || document.querySelector("第二套CSS选择器,请修改")
-  if (tmp) {
-    ret["content"] = tmp.innerText
-    ret["contentHtml"] = tmp.innerHTML
-    var patchContent = false
-    //处理详情页中的大图,大图作为附件使用
-    const images = tmp.querySelectorAll("img");
-    images.forEach((img, i) => {
-      if (img.width > 300) {
-        patchContent = true
-        const a = document.createElement("a");
-        a.href = img.src;
-        a.innerText = img.src;
-        tmp.appendChild(a);
-      }
-    })
-  }
+    tmp = document.querySelector("{{.ContentCss}}") || document.querySelector("第二套CSS选择器,请修改")
+        if (tmp) {
+            ret["content"] = tmp.innerText
+            ret["contentHtml"] = tmp.innerHTML
+            var patchContent = false
+            //处理详情页中的大图,大图作为附件使用
+            const images = tmp.querySelectorAll("img");
+            images.forEach((img, i) => {
+                if (img.width > 300) {
+                    patchContent = true
+                    const a = document.createElement("a");
+                    a.href = img.src;
+                    a.innerText = img.src;
+                    tmp.appendChild(a);
+                }
+            })
+    }
 }
 if("{{.AttachCss}}"!=""){//附件
 	tmp = document.querySelectorAll("{{.AttachCss}} a")  
@@ -270,51 +270,51 @@ ret
     var tmp = null
     
     if ("{{.TitleCss}}" != "") {//标题
-    tmp = document.querySelector("{{.TitleCss}}") || document.querySelector("#activity-name")
-    if (tmp) ret["title"] = tmp.getAttribute("title") || tmp.innerText
+        tmp = document.querySelector("{{.TitleCss}}") || document.querySelector("#activity-name")
+        if (tmp) ret["title"] = tmp.getAttribute("title") || tmp.innerText
     }
     if ("{{.PublishUnitCss}}" != "") {//采购单位
-    tmp = document.querySelector("{{.PublishUnitCss}}") || document.querySelector("#js_name")
-    if (tmp) ret["publishUnit"] = tmp.getAttribute("title") || tmp.innerText
+        tmp = document.querySelector("{{.PublishUnitCss}}") || document.querySelector("#js_name")
+        if (tmp) ret["publishUnit"] = tmp.getAttribute("title") || tmp.innerText
     }
     if ("{{.PublishTimeCss}}" != "") {//发布时间
-    tmp = document.querySelector("{{.PublishTimeCss}}") || document.querySelector("#publish_time")
-    if (tmp) ret["publishTime"] = tmp.getAttribute("title") || tmp.innerText
+        tmp = document.querySelector("{{.PublishTimeCss}}") || document.querySelector("#publish_time")
+        if (tmp) ret["publishTime"] = tmp.getAttribute("title") || tmp.innerText
     }
     if ("{{.ContentCss}}" != "") {//正文内容
-    tmp = document.querySelector("{{.ContentCss}}") || document.querySelector("#js_content")
-    if (tmp) {
-    ret["content"] = tmp.innerText
-    ret["contentHtml"] = tmp.innerHTML
-    var patchContent = false
-    //处理详情页中的大图,大图作为附件使用
-    const images = tmp.querySelectorAll("img");
-    images.forEach((img, i) => {
-      if (img.width > 300) {
-        patchContent = true
-        const a = document.createElement("a");
-        a.href = img.src;
-        a.innerText = img.src;
-        tmp.appendChild(a);
-      }
-    })
-    }
+        tmp = document.querySelector("{{.ContentCss}}") || document.querySelector("#js_content")
+        if (tmp) {
+            ret["content"] = tmp.innerText
+            ret["contentHtml"] = tmp.innerHTML
+            var patchContent = false
+            //处理详情页中的大图,大图作为附件使用
+            const images = tmp.querySelectorAll("img");
+            images.forEach((img, i) => {
+              if (img.width > 300) {
+                patchContent = true
+                const a = document.createElement("a");
+                a.href = img.src;
+                a.innerText = img.src;
+                tmp.appendChild(a);
+              }
+            })
+        }
     }
     if("{{.AttachCss}}"!=""){//附件
-    tmp = document.querySelectorAll("{{.AttachCss}} a")
-    let attach=[]
-    if(tmp){
-        tmp.forEach((v,i)=>{
-            attach.push({title:v.getAttribute("title")||v.innerText,href:v.href})
-        })
-    }
-    tmp = document.querySelectorAll("#js_content a")
-    if(tmp){
-        tmp.forEach((v,i)=>{
-            attach.push({title:v.getAttribute("title")||v.innerText,href:v.href})
-        })
-    }
-    ret["attachLinks"]=attach
+        tmp = document.querySelectorAll("{{.AttachCss}} a")
+        let attach=[]
+        if(tmp){
+            tmp.forEach((v,i)=>{
+                attach.push({title:v.getAttribute("title")||v.innerText,href:v.href})
+            })
+        }
+        tmp = document.querySelectorAll("#js_content a")
+        if(tmp){
+            tmp.forEach((v,i)=>{
+                attach.push({title:v.getAttribute("title")||v.innerText,href:v.href})
+            })
+        }
+        ret["attachLinks"]=attach
     }
     //检查中文字符个数,少于20,修正正文内容
     let regex = /[\\u4e00-\\u9fa5]/g;
@@ -393,51 +393,115 @@ var ret = {}
 var tmp = null
 
 if ("{{.TitleCss}}" != "") {//标题
-tmp = document.querySelector("{{.TitleCss}}")
-if (tmp) ret["title"] = tmp.getAttribute("title") || tmp.innerText
+    tmp = document.querySelector("{{.TitleCss}}")
+    if (tmp) ret["title"] = tmp.getAttribute("title") || tmp.innerText
 }
 if ("{{.PublishUnitCss}}" != "") {//采购单位
-tmp = document.querySelector("{{.PublishUnitCss}}")
-if (tmp) ret["publishUnit"] = tmp.getAttribute("title") || tmp.innerText
+    tmp = document.querySelector("{{.PublishUnitCss}}")
+    if (tmp) ret["publishUnit"] = tmp.getAttribute("title") || tmp.innerText
 }
 if ("{{.PublishTimeCss}}" != "") {//发布时间
-tmp = document.querySelector("{{.PublishTimeCss}}")
-if (tmp) {
-    //        格式:2024/01/05                  2024-01-05                   2024年01月05日                   15:01:01 (仅时间,可以自己修改)
-    var regTpl = ["(\\\\d{4}/\\\\d{1,2}/\\\\d{1,2})","(\\\\d{4}-\\\\d{1,2}-\\\\d{1,2})","(\\\\d{4}年\\\\d{1,2}月\\\\d{1,2}日)","\\\\d{1,2}:\\\\d{1,2}:\\\\d{1,2})"]
-    //TODO 重点要修改这里的regTpl 索引号,也可以自己修改设置正则表达式
-    var reg = new RegExp(regTpl[0])
-    tmp = tmp.innerText.match(reg)
-    if(tmp && tmp.length>1)ret["publishTime"] = tmp[1]
+    tmp = document.querySelector("{{.PublishTimeCss}}")
+    if (tmp) {
+        //        格式:2024/01/05                  2024-01-05                   2024年01月05日                   15:01:01 (仅时间,可以自己修改)
+        var regTpl = ["(\\\\d{4}/\\\\d{1,2}/\\\\d{1,2})","(\\\\d{4}-\\\\d{1,2}-\\\\d{1,2})","(\\\\d{4}年\\\\d{1,2}月\\\\d{1,2}日)","\\\\d{1,2}:\\\\d{1,2}:\\\\d{1,2})"]
+        //TODO 重点要修改这里的regTpl 索引号,也可以自己修改设置正则表达式
+        var reg = new RegExp(regTpl[0])
+        tmp = tmp.innerText.match(reg)
+        if(tmp && tmp.length>1)ret["publishTime"] = tmp[1]
     }
 }
 if ("{{.ContentCss}}" != "") {//正文内容
-tmp = document.querySelector("{{.ContentCss}}")
-if (tmp) {
-ret["content"] = tmp.innerText
-ret["contentHtml"] = tmp.innerHTML
-var patchContent = false
-//处理详情页中的大图,大图作为附件使用
-const images = tmp.querySelectorAll("img");
-images.forEach((img, i) => {
-  if (img.width > 300) {
-    patchContent = true
-    const a = document.createElement("a");
-    a.href = img.src;
-    a.innerText = img.src;
-    tmp.appendChild(a);
-  }
-})
+    tmp = document.querySelector("{{.ContentCss}}")
+    if (tmp) {
+        ret["content"] = tmp.innerText
+        ret["contentHtml"] = tmp.innerHTML
+        var patchContent = false
+        //处理详情页中的大图,大图作为附件使用
+        const images = tmp.querySelectorAll("img");
+        images.forEach((img, i) => {
+          if (img.width > 300) {
+            patchContent = true
+            const a = document.createElement("a");
+            a.href = img.src;
+            a.innerText = img.src;
+            tmp.appendChild(a);
+          }
+        })
+    }
 }
+if("{{.AttachCss}}"!=""){//附件
+    tmp = document.querySelectorAll("{{.AttachCss}} a")
+    let attach=[]
+    if(tmp){
+        tmp.forEach((v,i)=>{
+            attach.push({title:v.getAttribute("title")||v.innerText,href:v.href})
+        })
+    }
+    ret["attachLinks"]=attach
+}
+//检查中文字符个数,少于20,修正正文内容
+let regex = /[\\u4e00-\\u9fa5]/g;
+let chineseCharacters = ret["content"]?ret["content"].match(regex):[];
+let chineseCharactersLen=chineseCharacters ? chineseCharacters.length : 0;
+if (chineseCharactersLen < 20 && ret["attachLinks"] && ret["attachLinks"].length>0) ret["content"] = '详情请访问原网页!'
+ret 
+`}, {
+            "name": "模版6",
+            "tooltip": "基础模板扩展,正文、附件区域,都需要找附件",
+            "code": `
+var ret = {}
+var tmp = null
+
+if ("{{.TitleCss}}" != "") {//标题
+    tmp = document.querySelector("{{.TitleCss}}")
+    if (tmp) ret["title"] = tmp.getAttribute("title") || tmp.innerText
+}
+if ("{{.PublishUnitCss}}" != "") {//采购单位
+    tmp = document.querySelector("{{.PublishUnitCss}}")
+    if (tmp) ret["publishUnit"] = tmp.getAttribute("title") || tmp.innerText
+}
+if ("{{.PublishTimeCss}}" != "") {//发布时间
+    tmp = document.querySelector("{{.PublishTimeCss}}")
+    if (tmp) ret["publishTime"] = tmp.getAttribute("title") || tmp.innerText
+}
+if ("{{.ContentCss}}" != "") {//正文内容
+    tmp = document.querySelector("{{.ContentCss}}")
+    if (tmp) {
+        ret["content"] = tmp.innerText
+        ret["contentHtml"] = tmp.innerHTML
+        var patchContent = false
+        //处理详情页中的大图,大图作为附件使用
+        const images = tmp.querySelectorAll("img");
+        images.forEach((img, i) => {
+            if (img.width > 300) {
+                patchContent = true
+                const a = document.createElement("a");
+                a.href = img.src;
+                a.innerText = img.src;
+                tmp.appendChild(a);
+            }
+        })
+    }
 }
 if("{{.AttachCss}}"!=""){//附件
-tmp = document.querySelectorAll("{{.AttachCss}} a")
-let attach=[]
+    //附件区域检查
+    tmp = document.querySelectorAll("{{.AttachCss}} a")
+    let attach=[]
+    if(tmp){
+    tmp.forEach((v,i)=>{
+        attach.push({title:v.getAttribute("title")||v.innerText,href:v.href})
+    })
+}
+//正文区域附件检查
+tmp = document.querySelectorAll("{{.ContentCss}} a")
 if(tmp){
     tmp.forEach((v,i)=>{
         attach.push({title:v.getAttribute("title")||v.innerText,href:v.href})
     })
 }
+//附件过滤
+attch = attach.filter((item)=>item.href && item.href.startsWith('http'))
 ret["attachLinks"]=attach
 }
 //检查中文字符个数,少于20,修正正文内容
@@ -446,7 +510,8 @@ let chineseCharacters = ret["content"]?ret["content"].match(regex):[];
 let chineseCharactersLen=chineseCharacters ? chineseCharacters.length : 0;
 if (chineseCharactersLen < 20 && ret["attachLinks"] && ret["attachLinks"].length>0) ret["content"] = '详情请访问原网页!'
 ret 
-`},
+`,
+        },
     ],
 
     AttachJsCode: `
@@ -486,7 +551,7 @@ document.querySelectorAll("{{.ListNextPageCss}}").forEach(link=>{
             "tooltip": "选择器匹配多个 翻页链接/按钮,可根据检测是否包含子对象匹配",
             "code": `
 document.querySelectorAll("{{.ListNextPageCss}}").forEach(link=>{
-  if(link.querySelector("对象CSS选择器,请修改"))link.click();
+  if(link.querySelector("对象CSS选择器,请修改"))link.click();
 })
 ""
     `,
@@ -517,5 +582,41 @@ window.location=href
     `,
         },
     ],
-
+    //初始化代码集合
+    InitListPageJsCodes: [
+        {
+            "name": "模版1",
+            "tooltip": "页面跳转",
+            "code": `
+window.location=''
+''
+    `,
+        }, {
+            "name": "模版2",
+            "tooltip": "点击唯一元素",
+            "code": `
+var link=document.querySelector("CSS选择器");
+if(link)link.click();
+"" 
+    `,
+        }, {
+            "name": "模版3",
+            "tooltip": "点击需要文本过滤",
+            "code": `
+document.querySelectorAll("CSS选择器").forEach(link=>{
+  if(link.innerText==="下一页")link.click();
+})
+""
+    `,
+        }, {
+            "name": "模版4",
+            "tooltip": "点击需要子元素过滤",
+            "code": `
+document.querySelectorAll("CSS选择器").forEach(link=>{
+  if(link.querySelector("子对象CSS选择器,请修改"))link.click();
+})
+""
+    `,
+        },
+    ],
 }

+ 19 - 3
frontend/src/views/ReviewList.vue

@@ -90,7 +90,7 @@
                         </el-tooltip>
                     </template>
                 </el-table-column>
-                <el-table-column label="功能" :width="isAdmin ? 160 : 120" align="center">
+                <el-table-column label="功能" width="160" align="center">
                     <template #default="scope">
                         <!-- 管理员:上线和退回 -->
                         <template v-if="tableActionShow.adminGroup(scope.row)">
@@ -126,6 +126,11 @@
                                 <el-icon><CircleClose /></el-icon>
                             </el-button>
                         </el-tooltip>
+                        <el-tooltip content="退回" placement="top" v-if="tableActionShow.reviewerRollback2(scope.row)">
+                          <el-button size="small" :class="{ active: scope.row._action_clicked_reject_code }" @click="tableEvents.adminRollback(scope.$index, scope.row)">
+                            <el-icon><DArrowLeft /></el-icon>
+                          </el-button>
+                        </el-tooltip>
                     </template>
                 </el-table-column>
             </el-table>
@@ -138,8 +143,13 @@
             </div>
         </el-main>
     </el-card>
-    <EditSpider ref="editSpiderDialog" @custom-event="dialogEvents.editSpiderConfigSaveEvent" @data-tag="editDialogMarkClick($event)" />
-    <RunSpiderDialog ref="runSpiderDialog" />
+    <EditSpider
+        ref="editSpiderDialog"
+        from="reviewList"
+        @custom-event="dialogEvents.editSpiderConfigSaveEvent"
+        @data-tag="editDialogMarkClick($event)"
+    />
+    <RunSpiderDialog from="reviewList" ref="runSpiderDialog" />
     <VerifySpider ref="verifySpiderDialog" />
     <el-dialog v-model="dialog.rollbackReason" title="选择退回原因" width="500">
       <el-form>
@@ -521,6 +531,9 @@ const tableActionShow = {
     adminRollback(row) {
         // 只有已上线,才展示退回
         return row.state === 11
+    },
+    reviewerRollback2(row) {
+        return isReviewer.value
     }
 }
 const actionButtonDisabled = {
@@ -538,6 +551,9 @@ const actionButtonDisabled = {
     reviewerRollback(row) {
         return this.reviewerSubmit(row)
     },
+    reviewerRollback2(row) {
+      return this.reviewerSubmit(row)
+    },
     adminSubmit(row) {
         // 只有审核通过才能上线,否则不展示
         const canOnline = row.state === 3 || row.state === 6

+ 4 - 3
main.go

@@ -21,14 +21,14 @@ var (
 	db                   *bdb.SpiderDb
 	exitCh               chan bool
 	baseDir, attachesDir string           = ".", ""
-	qlmDir               string           = ""
 	currentSpiderConfig  *be.SpiderConfig = new(be.SpiderConfig)
 	currentResults                        = list.New() //b.ResultItems = make(b.ResultItems, 0)
 	vm                   *bvm.VM
 	glvm                 *script.GLVm
 	ws                   *bws.WebService
-	//serverAddress        = "http://visualizeld.spdata.jianyu360.com/%s" //正式环境
-	serverAddress = "http://127.0.0.1:8091/%s" //正式环境
+	isOnly4MainSite      = "false"
+	serverAddress        = "http://visualizeld.spdata.jianyu360.com/%s" //正式环境
+	//serverAddress = "http://127.0.0.1:8091/%s" //正式环境
 )
 
 //build
@@ -36,6 +36,7 @@ var (
 
 func init() {
 	be.LoadConfig("backend/config.yaml")
+	be.Cfg.IsOnly4MainSite = isOnly4MainSite == "true"
 	if be.Cfg.IsOnly4MainSite {
 		serverAddress = "http://visualize.spdata.jianyu360.com/%s" //重点网站
 	}