Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/master'

Gaosheng 1 settimana fa
parent
commit
e60a774d95

+ 45 - 3
xinkeaboard-gemini-langgraph_prompt/backend/src/agent/graph.py

@@ -305,14 +305,56 @@ def finalize_answer(state: OverallState, config: RunnableConfig):
     logger.info("开始:llm.invoke")
     result = llm.invoke(formatted_prompt)
     logger.info("结束:llm.invoke:{}",result)
+    
     # Replace the short urls with the original urls and add all used urls to the sources_gathered
     unique_sources = []
+    citation_map = {}  # Map of short_url to source info
+    citation_numbers = {}  # Map of short_url to citation number
+    
+    # Build source map and assign citation numbers
+    citation_counter = 1
     for source in state["sources_gathered"]:
         if source["short_url"] in result.content:
-            result.content = result.content.replace(
-                source["short_url"], source["value"]
-            )
             unique_sources.append(source)
+            citation_map[source["short_url"]] = source
+            citation_numbers[source["short_url"]] = citation_counter
+            citation_counter += 1
+    
+    # Collect citations from the content
+    citations_in_text = []
+    
+    # Find all citations in the content and replace them with numbered markers
+    import re
+    
+    # Pattern to match markdown links like [text](url)
+    link_pattern = r'\[([^\]]+)\]\(([^)]+)\)'
+    
+    def replace_with_numbered_citation(match):
+        text = match.group(1)
+        url = match.group(2)
+        if url in citation_numbers:
+            # Store the citation for later use
+            citations_in_text.append((citation_numbers[url], text, citation_map[url]))
+            # Return just the text with a numbered citation marker
+            return f"{text}[{citation_numbers[url]}]"
+        return match.group(0)  # Return unchanged if not a citation
+    
+    # Replace inline citations with numbered markers
+    result.content = re.sub(link_pattern, replace_with_numbered_citation, result.content)
+    
+    # Add a section for citations at the end
+    if citations_in_text:
+        # Sort citations by their number
+        citations_in_text.sort(key=lambda x: x[0])
+        
+        # Add citations section
+        result.content += "\n\n## 参考资料\n"
+        added_citations = set()  # To avoid duplicates
+        for num, text, source in citations_in_text:
+            if num not in added_citations:
+                result.content += f"{num}. [{source['label']}]({source['value']})\n"
+                added_citations.add(num)
+    
     #save the result to a markdown file
     # with open(f"result_{get_research_topic(state['messages'])}.md", "w", encoding="utf-8") as f:
     #     f.write(result.content)

+ 15 - 1
xinkeaboard-seller/src/components/SldEditFormCom/SldEditFormCom.js

@@ -2,6 +2,7 @@ import React, { Component, Fragment } from 'react';
 import { Form, Select, Input,InputNumber,DatePicker,TreeSelect,Cascader,Checkbox,Radio } from 'antd';
 import global from '@/global.less';
 import { sldInputAfterAddons, } from '@/utils/utils';
+import { emojiRegex } from "@/utils/regex";
 import styles from './SldEditFormCom.less';
 const FormItem = Form.Item;
 const { RangePicker } = DatePicker;
@@ -43,13 +44,26 @@ export default  class SldEditFormCom extends Component {
     	  //普通输入框
       item_width  = item_width!=undefined?item_width:'auto'
 			if(val.type == 'input'){
+        // 统一规则定义
+        const unifyInputRules = [
+          {
+            validator: (_, value, callback) => {
+              const hasEmoji = emojiRegex.test(value);
+              if (hasEmoji) {
+                callback('不支持输入表情符号');
+              } else {
+                callback();
+              }
+            },
+          },
+        ];
 				return (<FormItem
 							key={index}
 							label={val.label}
 							extra={val.extra}
               style={{width:val.width!=undefined?val.width+80:250}}
 						>
-							{getFieldDecorator(val.name,{initialValue:val.initialValue,rules:val.rules})(
+							{getFieldDecorator(val.name,{initialValue:val.initialValue,rules: [...(val.rules || []), ...unifyInputRules]})(
 								<Input maxLength={val.maxLength!=undefined&&val.maxLength?val.maxLength:99999999} className={styles.item}  disabled={val.disable} placeholder={val.placeholder}/>
 							)}
 						</FormItem>

+ 15 - 1
xinkeaboard-seller/src/components/SldTableRowThree/index.js

@@ -8,6 +8,7 @@ import {
 import global from '@/global.less';
 import styles from './index.less';
 import { sldInputAfterAddons, sldBeforeUpload,input_limit_length ,sldComLanguage,getLocalStorageStingVal} from '@/utils/utils';
+import { emojiRegex } from "@/utils/regex";
 import { Scrollbars } from 'react-custom-scrollbars';
 import StandardTable from '@/components/StandardTable';
 
@@ -80,12 +81,25 @@ export default class SldTableRowTwo extends PureComponent {
       </div>
     );
     if (val.type == 'input') {
+      // 统一规则定义
+      const unifyInputRules = [
+        {
+          validator: (_, value, callback) => {
+            const hasEmoji = emojiRegex.test(value);
+            if (hasEmoji) {
+              callback('不支持输入表情符号');
+            } else {
+              callback();
+            }
+          },
+        },
+      ];
       return (<FormItem
           key={index}
           extra={val.extra}
           style={{width: `${val.ipwidth != undefined ? val.ipwidth : 80}%`}}
         >
-          {getFieldDecorator(val.name, { initialValue: val.initialValue, rules: val.rules })(
+          {getFieldDecorator(val.name, { initialValue: val.initialValue, rules: [...(val.rules || []), ...unifyInputRules] })(
             <Input maxLength={val.maxLength!=undefined&&val.maxLength?val.maxLength:input_limit_length} disabled={val.disable != undefined ? val.disable : false} className={styles.item}
                    placeholder={val.placeholder}/>,
           )}

+ 17 - 2
xinkeaboard-seller/src/components/SldTableRowTwo/index.js

@@ -32,6 +32,7 @@ import {
   sldComLanguage,
   sldBeforeMoreUpload,
 } from '@/utils/utils';
+import { emojiRegex } from "@/utils/regex";
 import {reserveInfoLimitType} from '@/utils/util_data';
 import ALibbSvg from '@/components/ALibbSvg';
 
@@ -130,15 +131,29 @@ export default class SldTableRowTwo extends PureComponent {
       </div>
     );
     if (val.type == 'input') {
+      // 统一规则定义
+      const unifyInputRules = [
+        {
+          validator: (_, value, callback) => {
+            const hasEmoji = emojiRegex.test(value);
+            if (hasEmoji) {
+              callback('不支持输入表情符号');
+            } else {
+              callback();
+            }
+          },
+        },
+      ];
       return (<FormItem
           key={index}
           extra={val.extra}
           style={{ width: '80%' }}
         >
-          {getFieldDecorator(val.name, { initialValue: val.initialValue, rules: val.rules })(
+          {getFieldDecorator(val.name, { initialValue: val.initialValue, rules: [...(val.rules || []), ...unifyInputRules] })(
             <Input maxLength={val.maxLength != undefined ? val.maxLength : 250}
                    disabled={val.disable != undefined ? val.disable : false} className={styles.item}
-                   placeholder={val.placeholder}/>,
+                   placeholder={val.placeholder}
+            />,
           )}
         </FormItem>
       );

+ 61 - 18
xinkeaboard-seller/src/components/SldUEditor/index.js

@@ -1,11 +1,12 @@
-import React, { PureComponent, Fragment } from 'react';
-import {apiUrl} from '@/utils/sldconfig.js';
+import React, { PureComponent, Fragment } from "react";
+import { apiUrl } from "@/utils/sldconfig.js";
+import { emojiRegex } from "@/utils/regex";
+import { message } from "antd";
 
 export default class SldUEditor extends PureComponent {
   constructor(props) {
     super(props);
-    this.state = {
-    };
+    this.state = {};
   }
 
   componentDidMount() {
@@ -13,38 +14,80 @@ export default class SldUEditor extends PureComponent {
   }
 
   componentWillReceiveProps(nextProps, nextContext) {
-    if(nextProps.getContentFlag&&!this.props.getContentFlag){
+    if (nextProps.getContentFlag && !this.props.getContentFlag) {
       let con = UE.getEditor(nextProps.id).getContent();
       this.props.getEditorContent(con);
     }
   }
 
   componentWillUnmount() {
-    UE.delEditor(this.props.id)
+    UE.delEditor(this.props.id);
   }
 
   initEditor = () => {
-    const {id,initEditorContent} = this.props;
+    const { id, initEditorContent } = this.props;
     console.info();
-    const ueEditor = UE.getEditor(id,{
-      serverUrl: `${apiUrl}v3/oss/ueditor/upload?configPath=config.json&storeId=`+localStorage.getItem('storeId'),
+    const ueEditor = UE.getEditor(id, {
+      serverUrl:
+        `${apiUrl}v3/oss/ueditor/upload?configPath=config.json&storeId=` +
+        localStorage.getItem("storeId"),
     });
-    ueEditor.ready((editor)=>{
-      if(!editor){
+    ueEditor.ready((editor) => {
+      if (!editor) {
         UE.delEditor(id);
         this.initEditor();
-      }else{
+      } else {
         if (initEditorContent) {
           UE.getEditor(id).setContent(initEditorContent);
         }
+
+        // 监听输入内容变化
+        ueEditor.addListener("contentChange", () => {
+          const content = ueEditor.getContentTxt(); // 纯文本
+          if (this.hasEmoji(content)) {
+            message.warn("监测到输入表情符号,已自动去除");
+            ueEditor.body.innerHTML = ueEditor.body.innerHTML.replace(
+              emojiRegex,
+              ""
+            );
+            ueEditor.focus();
+            const range = ueEditor.selection.getRange();
+            const body = ueEditor.body;
+
+            // 找到最后一个文本节点或者元素节点
+            let lastNode = body.lastChild;
+
+            // 如果最后一个是元素节点,尝试找到它的最后一个文本节点
+            function getLastTextNode(node) {
+              if (node.nodeType === 3) return node; // 文本节点
+              if (!node.hasChildNodes()) return null;
+              return getLastTextNode(node.lastChild);
+            }
+
+            const lastTextNode = getLastTextNode(lastNode) || lastNode;
+
+            // 如果找到文本节点,光标放在文本末尾
+            if (lastTextNode && lastTextNode.nodeType === 3) {
+              range.setStart(lastTextNode, lastTextNode.nodeValue.length);
+            } else {
+              // 否则放在最后元素后面
+              range.setStartAfter(lastNode);
+            }
+
+            range.collapse(true);
+            range.select();
+          }
+        });
       }
-    })
-  }
+    });
+  };
+
+  hasEmoji = (str) => {
+    return emojiRegex.test(str);
+  };
 
   render() {
-    const {id} = this.props
-    return(
-      <div id={id} name="content" type="text/plain"></div>
-    )
+    const { id } = this.props;
+    return <div id={id} name="content" type="text/plain" />;
   }
 }

+ 12 - 0
xinkeaboard-seller/src/utils/regex.js

@@ -0,0 +1,12 @@
+export const emojiRegex = new RegExp(
+  [
+    "[\u{1F600}-\u{1F64F}]", // 😀-🙏 表情
+    "[\u{1F300}-\u{1F5FF}]", // 🌐-🗿 符号&象形文字
+    "[\u{1F680}-\u{1F6FF}]", // 🚀-🛺 交通工具
+    "[\u{2600}-\u{26FF}]", // ☀-⛿ 杂项符号
+    "[\u{2700}-\u{27BF}]", // ✀-➿ 装饰符号
+    "[\u{1F900}-\u{1F9FF}]", // 🤰-🦿 补充符号
+    "[\u{1FA70}-\u{1FAFF}]", // 🪀-🪿 扩展符号
+  ].join("|"),
+  "gu"
+);

+ 30 - 0
xinkeaboard-web/assets/style/member/index.scss

@@ -583,6 +583,7 @@ a:active {
                 justify-content: center;
                 overflow: hidden;
                 cursor: pointer;
+                position: relative;
                 &:last-child {
                     margin-right: 0;
                 }
@@ -595,6 +596,35 @@ a:active {
                     border: 1px solid $colorMain;
                 }
 
+                /* 遮罩层 */
+            .mask {
+                position: absolute;
+                top: 0;
+                left: 0;
+                width: 100%;
+                height: 100%;
+                background-color: rgba(153, 153, 153, 0.6);
+                /* 灰色透明遮罩 */
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                z-index: 1;
+            }
+
+            /* 白色圆圈 */
+            .circle {
+                background-color: white;
+                width: 30px;
+                height: 30px;
+                border-radius: 50%;
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                font-size: 5px;
+                color: black;
+                text-align: center;
+            }
+
 
             }
         }

+ 8 - 2
xinkeaboard-web/pages/member/index/home.vue

@@ -59,7 +59,7 @@
           <ul class="clearfix">
             <li
               class="list_item"
-              v-for="({ productImage, productId,goodsName }, index) in collectGoods.goods"
+              v-for="({ productImage, productId,goodsName, state }, index) in collectGoods.goods"
               :key="index"
             >
               <router-link
@@ -67,6 +67,9 @@
                   :to="'/goods/detail/'+ calcProductName(goodsName) +'_'+ productId"
               >
                 <img :src="productImage" alt="" />
+                <div class="mask" v-if="[5, 6].includes(state)">
+                  <div class="circle">{{  L['已下架']  }}</div>
+                </div>
               </router-link>
             </li>
             <div class="no_footprint" v-show="!collectGoods.goods.length">
@@ -115,7 +118,7 @@
           <ul class="clearfix">
             <li
               class="list_item"
-              v-for="({ goodsImage, productId,goodsName }, index) in looklog.log"
+              v-for="({ goodsImage, productId,goodsName, state }, index) in looklog.log"
               :key="index"
             >
               <router-link
@@ -123,6 +126,9 @@
                   :to="'/goods/detail/'+ calcProductName(goodsName) +'_'+ productId"
               >
                 <img :src="goodsImage" alt="" />
+                 <div class="mask" v-if="[5, 6].includes(state)">
+                  <div class="circle">{{  L['已下架']  }}</div>
+                </div>
               </router-link>
             </li>
             <div class="no_footprint" v-show="!looklog.log.length">