diff --git a/am.py b/am.py
index 00492e6..186613d 100644
--- a/am.py
+++ b/am.py
@@ -269,6 +269,21 @@ def backup_db():
print(f"[数据库备份失败] {e}")
+def cleanup_uploads():
+ """删除 uploads/ 里 7 天前的图片"""
+ try:
+ udir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "uploads")
+ if not os.path.isdir(udir):
+ return
+ cutoff = time.time() - 7 * 86400
+ for f in os.listdir(udir):
+ fp = os.path.join(udir, f)
+ if os.path.isfile(fp) and os.path.getmtime(fp) < cutoff:
+ os.remove(fp)
+ except Exception as e:
+ print(f"[清理上传文件失败] {e}")
+
+
def cleanup_old_conversations():
"""自动清理 30 天前的旧对话,防止数据库无限膨胀"""
try:
@@ -290,6 +305,7 @@ def cleanup_old_conversations():
backup_db()
+cleanup_uploads()
# 自动清理已关闭,不删旧对话
@@ -919,6 +935,9 @@ def stream_chat():
conv_id = request.args.get("conv_id", type=int)
if not msg:
return Response(sse({"e": "问题不能为空"}), mimetype="text/event-stream")
+ # 纯图片消息:不调AI
+ if "[图片]" in msg and len(msg) < 300:
+ return Response(sse({"t": "收到您的图片,请描述一下您的问题。"}), mimetype="text/event-stream")
# 运单号:直接给轨迹卡片,不让模型编
track_no = extract_tracking_no(msg)
@@ -1437,8 +1456,19 @@ def uploaded_file(name):
def index():
return render_template("index.html")
+@app.route("/admin/login", methods=["POST"])
+def admin_login():
+ pwd = request.json.get("password", "")
+ if pwd == "123":
+ return jsonify({"ok": True})
+ return jsonify({"ok": False}), 401
+
@app.route("/admin")
def admin():
+ return render_template("admin_login.html")
+
+@app.route("/admin/dashboard")
+def admin_dash():
return render_template("admin.html")
diff --git a/templates/admin.html b/templates/admin.html
index f0315f5..80567ca 100644
--- a/templates/admin.html
+++ b/templates/admin.html
@@ -149,7 +149,12 @@ function switchSession(sid){
function appendMsg(m){
var div=document.createElement("div");
div.className="msg "+(m.side==="customer"?"cust":"agent");
- div.innerHTML='
'+m.content.replace(/
'+(m.side==="customer"?"客户":"客服")+'
';
+ var c = m.content.replace(/');
+ }
+ div.innerHTML='
'+c+'
'+(m.side==="customer"?"客户":"客服")+'
';
document.getElementById("chatBox").appendChild(div);
document.getElementById("chatBox").scrollTop=999999;
}
diff --git a/templates/index.html b/templates/index.html
index 0161ddf..5a9323f 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1321,9 +1321,15 @@ uploadInput.onchange = function(){
.then(data=>{
uploadBtn.textContent = "📎";
if(data.ok){
- let text = "[附件] " + data.url;
- if(data.is_image) text = "[图片] " + data.url;
- inputDom.value = text;
+ window.__pendingImg = data.url;
+ var old = document.getElementById("imgPreview");
+ if(old) old.remove();
+ var prev = document.createElement("div");
+ prev.id = "imgPreview";
+ prev.style.cssText="margin:0 0 8px 50px;";
+ prev.innerHTML = '

';
+ var area = document.querySelector(".input-area");
+ area.insertBefore(prev, area.firstChild);
inputDom.focus();
}else{
alert(data.error || "上传失败");
@@ -1961,7 +1967,8 @@ function addMsg(content, isUser){
const userMsg = document.createElement('div');
userMsg.className = "user-message";
if(imgUrl){
- userMsg.innerHTML = '

';
+ var txt = content.replace(/\[图片\]\/uploads\/\S+\.(?:jpg|jpeg|png|gif|webp)/i, "").trim();
+ userMsg.innerHTML = '

' + (txt ? '
'+txt+'
' : '');
}else{
userMsg.innerText = content;
}
@@ -2121,17 +2128,21 @@ function renderFollowups(bubble){
async function doSend(retryText){
const isRetry = typeof retryText === "string";
const sendWord = (isRetry ? retryText : inputDom.value).trim();
- if(!sendWord){
+ var imgUrl = window.__pendingImg || "";
+ if(!sendWord && !imgUrl){
inputDom.focus();
return;
}
-
// 人工客服模式:直接发给客服,不走AI
if(window.humanSid){
- if(!isRetry) addMsg(sendWord, true);
+ var content = sendWord;
+ if(imgUrl) content += (content ? " " : "") + "[图片]" + imgUrl;
+ if(!isRetry) addMsg(content, true);
inputDom.value = "";
+ window.__pendingImg = "";
+ var p = document.getElementById("imgPreview"); if(p) p.remove();
inputDom.focus();
- fetch("/api/human/send",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sid:window.humanSid,side:"customer",content:sendWord})});
+ fetch("/api/human/send",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sid:window.humanSid,side:"customer",content:content})});
return;
}
@@ -2139,8 +2150,12 @@ async function doSend(retryText){
if(cancelActive){ cancelActive(); cancelActive = null; }
if(!isRetry){
- addMsg(sendWord, true);
+ var fullContent = sendWord;
+ if(imgUrl) fullContent += (fullContent ? " " : "") + "[图片]" + imgUrl;
+ addMsg(fullContent, true);
inputDom.value = "";
+ window.__pendingImg = "";
+ var p = document.getElementById("imgPreview"); if(p) p.remove();
inputDom.focus();
}
@@ -2294,7 +2309,7 @@ async function doSend(retryText){
fetch("/stream_chat", {
method: "POST",
headers: {"Content-Type": "application/json"},
- body: JSON.stringify({msg: sendWord, conv_id: currentConvId}),
+ body: JSON.stringify({msg: fullContent, conv_id: currentConvId}),
signal: controller.signal
}).then(function(res){
if(!res.ok){ throw new Error("HTTP " + res.status); }