+ {!isUpperPanelCollapsed && (
+
+ {/* Close button — collapses this whole upper panel so the
+ conversation log takes the freed vertical space. */}
+
setIsUpperPanelCollapsed(true)}
+ className="absolute top-3 right-4 flex items-center gap-1 text-[10px] font-bold text-slate-400 hover:text-primary hover:bg-slate-100 px-2 py-1 rounded-lg transition cursor-pointer z-10"
+ title="收起圆桌控制台"
+ >
+
+ 收起
+
{/* Left part: Roundtable circle diagram */}
@@ -1423,14 +1678,14 @@ export default function RoundtablePlanningPage() {
{/* Clickable Roundtable Agent Bubbles */}
{/* Role 3: 情报收集 (Top) */}
-
setSelectedAgentId(3)}
- className={`absolute -top-3.5 left-1/2 -translate-x-1/2 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 3 || activeSpeakerId === 3 ? 'ring-4 ring-purple-400 scale-[1.12]' : 'border-slate-200'}`}
+ className={`absolute -top-3.5 left-1/2 -translate-x-1/2 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 3 || (activeSpeakerId === 3 || activeSpeakerSet.has(3)) ? 'ring-4 ring-purple-400 scale-[1.12]' : 'border-slate-200'}`}
title="点击配置 情报收集 智能体人设与LLM"
>
情报收集
- {(activeSpeakerId === 3 || selectedAgentId === 3) && (
+ {((activeSpeakerId === 3 || activeSpeakerSet.has(3)) || selectedAgentId === 3) && (
@@ -1441,12 +1696,12 @@ export default function RoundtablePlanningPage() {
{/* Role 4: 环境评估 (Top Right) */}
setSelectedAgentId(4)}
- className={`absolute top-5 -right-4 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 4 || activeSpeakerId === 4 ? 'ring-4 ring-emerald-400 scale-[1.12]' : 'border-slate-200'}`}
+ className={`absolute top-5 -right-4 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 4 || (activeSpeakerId === 4 || activeSpeakerSet.has(4)) ? 'ring-4 ring-emerald-400 scale-[1.12]' : 'border-slate-200'}`}
title="点击配置 环境评估 智能体人设与LLM"
>
环境评估
- {(activeSpeakerId === 4 || selectedAgentId === 4) && (
+ {((activeSpeakerId === 4 || activeSpeakerSet.has(4)) || selectedAgentId === 4) && (
@@ -1457,12 +1712,12 @@ export default function RoundtablePlanningPage() {
{/* Human Participant: 方案设计 (Bottom Right) */}
setSelectedAgentId(5)}
- className={`absolute bottom-5 -right-4 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 5 || activeSpeakerId === 5 ? 'ring-4 ring-primary/50 scale-[1.12]' : 'border-slate-200'}`}
+ className={`absolute bottom-5 -right-4 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 5 || (activeSpeakerId === 5 || activeSpeakerSet.has(5)) ? 'ring-4 ring-primary/50 scale-[1.12]' : 'border-slate-200'}`}
title="点击查看 方案设计 角色参数"
>
方案设计
- {(activeSpeakerId === 5 || selectedAgentId === 5) && (
+ {((activeSpeakerId === 5 || activeSpeakerSet.has(5)) || selectedAgentId === 5) && (
@@ -1473,12 +1728,12 @@ export default function RoundtablePlanningPage() {
{/* Role 6: 风险审查 (Bottom) */}
setSelectedAgentId(6)}
- className={`absolute -bottom-3.5 left-1/2 -translate-x-1/2 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 6 || activeSpeakerId === 6 ? 'ring-4 ring-amber-400 scale-[1.12]' : 'border-slate-200'}`}
+ className={`absolute -bottom-3.5 left-1/2 -translate-x-1/2 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 6 || (activeSpeakerId === 6 || activeSpeakerSet.has(6)) ? 'ring-4 ring-amber-400 scale-[1.12]' : 'border-slate-200'}`}
title="点击配置 风险审查 智能体人设与LLM"
>
风险审查
- {(activeSpeakerId === 6 || selectedAgentId === 6) && (
+ {((activeSpeakerId === 6 || activeSpeakerSet.has(6)) || selectedAgentId === 6) && (
@@ -1489,12 +1744,12 @@ export default function RoundtablePlanningPage() {
{/* Role 7: 执行规划 (Bottom Left) */}
setSelectedAgentId(7)}
- className={`absolute bottom-5 -left-4 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 7 || activeSpeakerId === 7 ? 'ring-4 ring-pink-400 scale-[1.12]' : 'border-slate-200'}`}
+ className={`absolute bottom-5 -left-4 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 7 || (activeSpeakerId === 7 || activeSpeakerSet.has(7)) ? 'ring-4 ring-pink-400 scale-[1.12]' : 'border-slate-200'}`}
title="点击配置 执行规划 智能体人设与LLM"
>
执行规划
- {(activeSpeakerId === 7 || selectedAgentId === 7) && (
+ {((activeSpeakerId === 7 || activeSpeakerSet.has(7)) || selectedAgentId === 7) && (
@@ -1505,12 +1760,12 @@ export default function RoundtablePlanningPage() {
{/* Role 2: 总结陈述 (Top Left) */}
setSelectedAgentId(2)}
- className={`absolute top-5 -left-4 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 2 || activeSpeakerId === 2 ? 'ring-4 ring-primary/50 scale-[1.12]' : 'border-slate-200'}`}
+ className={`absolute top-5 -left-4 transition-transform duration-300 hover:scale-[1.18] active:scale-95 bg-white p-0.5 rounded-full border shadow-md cursor-pointer ${selectedAgentId === 2 || (activeSpeakerId === 2 || activeSpeakerSet.has(2)) ? 'ring-4 ring-primary/50 scale-[1.12]' : 'border-slate-200'}`}
title="点击配置 总结陈述 主持人"
>
总结陈述
- {(activeSpeakerId === 2 || selectedAgentId === 2) && (
+ {((activeSpeakerId === 2 || activeSpeakerSet.has(2)) || selectedAgentId === 2) && (
@@ -1585,6 +1840,7 @@ export default function RoundtablePlanningPage() {
+ )}
{/* Selected Agent Persona Config Drawer / Drawer dialog box */}
{selectedAgentId && (
@@ -1650,8 +1906,8 @@ export default function RoundtablePlanningPage() {
推理本源模型
- {
const val = e.target.value;
setAgentConfigs(prev => ({
@@ -1663,9 +1919,18 @@ export default function RoundtablePlanningPage() {
}));
}}
className="w-full bg-white border border-slate-200 rounded-xl px-2.5 py-1.5 text-xs text-slate-700 font-bold focus:outline-none"
+ disabled={modelsLoading}
>
- Gemini 2.5 Pro (深度分析型)
- Gemini 2.5 Flash (疾速自适应型)
+ {availableModels.length === 0 && (
+ {DEFAULT_MODEL}
+ )}
+ {availableModels.map((m) => (
+
+ {m.display_name && m.display_name !== m.name
+ ? `${m.display_name} (${m.name})`
+ : m.name}
+
+ ))}
@@ -1708,9 +1973,9 @@ export default function RoundtablePlanningPage() {
)}
{/* Conversation Log inside Step 2 */}
-
{step2RoundtableDialogues.map((msg) => (
{msg.time}
-
+
{msg.content}
+ {msg.streaming && (
+
+ )}
diff --git a/newpython/OUT-v1.py b/newpython/OUT-v1.py
new file mode 100644
index 00000000..f11ae88e
--- /dev/null
+++ b/newpython/OUT-v1.py
@@ -0,0 +1,483 @@
+"""
+测试流式接口的完整脚本
+API流程:
+1. 登录获取token
+2. 创建thread(可选,也可以使用无状态接口)
+3. 调用流式接口
+
+两种方式:
+- 方式1: POST /api/runs/stream (无状态,自动创建thread)
+- 方式2: POST /api/threads/{thread_id}/runs/stream (需要先创建thread)
+"""
+import requests
+import json
+import asyncio
+import httpx # 异步 HTTP 客户端
+# ============ 配置区域 ============
+BASE_URL = "http://127.0.0.1:8001"
+LOGIN_URL = f"{BASE_URL}/api/v1/auth/login/username"
+THREADS_URL = f"{BASE_URL}/api/threads"
+RUNS_STREAM_URL = f"{BASE_URL}/api/runs/stream" # 无状态流式接口
+
+# ============ 登录获取token ============
+def get_token(username: str) -> str:
+ """登录获取token"""
+ response = requests.post(
+ LOGIN_URL,
+ json={"username": username},
+ headers={"Content-Type": "application/json"}
+ )
+ # print(f"登录状态码: {response.status_code}")
+ data = response.json()
+ # print(f"登录响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
+
+ # 根据实际返回结构调整
+ token = data.get("token") or data.get("access_token") or data.get("data", {}).get("token")
+ return token
+# ============ 方式2: 先创建thread,再调用流式接口 ============
+def create_thread(token: str) -> str:
+ """创建thread,返回thread_id"""
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+
+ response = requests.post(
+ THREADS_URL,
+ json={}, # 空body,使用默认值
+ headers=headers
+ )
+
+ print(f"创建thread状态码: {response.status_code}")
+ data = response.json()
+ print(f"创建thread响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
+
+ return data.get("thread_id")
+def get_agent_name_description(agent_name="Taiwan-intelligence-gathering-agent"):
+ url = f"{BASE_URL}/api/agents/{agent_name}"
+ token=get_token("ji")
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+ response = requests.get(
+ url,
+ headers=headers,
+ )
+ print(response.json())
+ return response.json()["name"].lower(),"agent_name:"+response.json()["name"].lower()+","+response.json()["description"]
+
+# ============ 创建智能体 ============
+def create_agent(
+ name: str,
+ description: str = "",
+ model: str | None = None,
+ tool_groups: list[str] | None = None,
+ skills: list[str] | None = None,
+ soul: str = "",
+):
+ """
+ 创建一个新的智能体
+
+ Args:
+ name: 智能体名称(必须是 hyphen-case,如 my-agent)
+ description: 智能体描述
+ model: 可选的模型覆盖(如 gpt-4, claude-3-opus 等)
+ tool_groups: 工具组白名单(如 ["web", "file"])
+ skills: 技能白名单(如 ["skill1", "skill2"])
+ soul: SOUL.md 内容(智能体人格/身份描述)
+
+ Returns:
+ 创建结果
+ """
+ TOKEN =get_token("ji")
+ url = f"{BASE_URL}/api/agents"
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {TOKEN}"
+ }
+
+ payload = {
+ "name": name,
+ "description": description,
+ "soul": soul
+ }
+
+ # 添加可选字段
+ if model is not None:
+ payload["model"] = model
+ if tool_groups is not None:
+ payload["tool_groups"] = tool_groups
+ if skills is not None:
+ payload["skills"] = skills
+
+ print(f"请求: POST {url}")
+ print(f"请求体: {json.dumps(payload, indent=2, ensure_ascii=False)}")
+
+ response = requests.post(url, json=payload, headers=headers)
+
+ print(f"状态码: {response.status_code}")
+
+ if response.status_code == 201:
+ result = response.json()
+ print(f"✓ 智能体创建成功!")
+ print(f"响应: {json.dumps(result, indent=2, ensure_ascii=False)}")
+ return result
+ else:
+ print(f"✗ 创建失败: {response.text}")
+ return f"✗ 创建失败: {response.text}"
+
+def create_main_agent(zhihui_name,agent_names):
+ d={}
+ token=get_token("ji")
+ l=[]
+ for i in agent_names:
+ # print(i)
+ name,description=get_agent_name_description(i)
+ thread_id_tmp=create_thread(token)
+ d[name]=thread_id_tmp
+ l.append(description)
+ name="你是总控控制协调智能体,负责协调其他智能体干活"
+ miaoshu=f"""#核心定位:
+ 用途:跟用用户的任务目标,调用其他智能体干活,只负责协调汇总,当你觉得已有信息支持回答用户问题时候,停止调度,并输出最后的结果
+ #调度方法:
+ 通过使用agent_orchestration技能调用其他智能体干活,使用技能时传入agent_name和需要做的活
+ #可调度智能体:
+ {l}"""
+ thread_id_tmp=create_thread(token)
+ engname=zhihui_name
+ d[engname]=thread_id_tmp
+ tmp_skills=["agent_orchestration"]
+ tmpres=create_agent(
+ name=engname,
+ description=name,
+ model="glm-5", # 指定模型
+ skills=tmp_skills, # 关联技能
+ soul=miaoshu)
+ # print("创建智能体响应:",tmpres)
+ if "失败" in tmpres and "exists" not in tmpres:
+ return tmpres
+ print(f"创建协调智能体成功,可以协调列表:{l}")
+ return d
+import re
+def extract_name_task(text="python3 scripts/agent_orchestration.py \"Us-intelligence-gathering-agent\" \"收集和分析美国人民对特朗普的看法,包括支持率和反对率、不同群体的观点差异、主要支持理由和反对理由等\""):
+ try:
+ pattern = r'\.py\s+"([^"]*)"\s+"([^"]*)"'
+ match = re.search(pattern, text)
+ if match:
+ agent_name = match.group(1)
+ task_content = match.group(2)
+ return agent_name,task_content
+ except:
+ return "",""
+def update_agent_message(thread_id,new_content):
+ token=get_token("ji")
+ url = f"{BASE_URL}/api/threads/{thread_id}/state"
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+ tmp = requests.get(
+ url,
+ headers=headers
+ ).json()
+ # print(tmp)
+ if "messages" in tmp["values"]:
+ tmp["values"]["messages"].append({"type": "human", "content": new_content})
+ else:
+ tmp["values"]["messages"]=[]
+ tmp["values"]["messages"].append({"type": "human", "content": new_content})
+ url = f"{BASE_URL}/api/threads/{thread_id}/state"
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+ response = requests.post(
+ url,
+ json=tmp,
+ headers=headers,
+ stream=True
+ )
+ del tmp
+ print(f"线程{thread_id}更新状态码: {response.status_code}")
+def Broadcast_message(message,d_agent_thread_id,pingbi_agent_name):
+ for key in d_agent_thread_id:
+ if key not in pingbi_agent_name:
+ update_agent_message(d_agent_thread_id[key],message)
+ print("广播消息成功:",key,message)
+null=None
+true=True
+false=False
+async def leader_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names):
+ """使用 httpx 异步请求的 leader_agent"""
+ # 使用 to_thread 异步运行同步的 Broadcast_message
+ await asyncio.to_thread(Broadcast_message, "总控智能体收到用户消息:" + new_message, d_agent_thread_id, [agent_name])
+ thread_id = d_agent_thread_id[agent_name]
+ l = []
+ token = get_token("ji")
+ url = f"{BASE_URL}/api/threads/{thread_id}/runs/stream"
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+ payload = {
+ "input": {
+ "messages": [
+ {
+ "type": "human",
+ "content": [
+ {
+ "type": "text",
+ "text": new_message
+ }
+ ],
+ "additional_kwargs": {}
+ }
+ ]
+ },
+ "config": {
+ "recursion_limit": 1000
+ },
+ "context": {
+ "agent_name": agent_name,
+ "model_name": "deepseek-chat",
+ "mode": "pro",
+ "reasoning_effort": "medium",
+ "thinking_enabled": true,
+ "is_plan_mode": false,
+ "subagent_enabled": false,
+ "thread_id": thread_id
+ },
+ "stream_mode": [
+ "messages-tuple",
+ "values",
+ ],
+ "stream_subgraphs": true,
+ "stream_resumable": true,
+ "assistant_id": "lead_agent",
+ "on_disconnect": "continue",
+ "excluded_tools": ["web_search"],
+ "skill_stop_names": skill_stop_names,
+ }
+
+ async with httpx.AsyncClient(timeout=None) as client:
+ async with client.stream("POST", url, json=payload, headers=headers) as response:
+ print(f"状态码: {response.status_code}")
+ if response.status_code != 200:
+ body = await response.aread()
+ raise RuntimeError(
+ f"upstream {url} returned HTTP {response.status_code}: "
+ f"{body.decode('utf-8', 'replace')[:1000]}"
+ )
+ async for line in response.aiter_lines():
+ if line:
+ yield line
+ if line[:5] == "data:":
+ print(line)
+ l.append(line)
+ if len(l) < 2:
+ raise RuntimeError(
+ f"upstream stream produced no usable data lines (got {len(l)} line(s)). "
+ f"Check the server log on {url} for the real failure."
+ )
+
+ null = None
+ tmpres = eval(l[-2][5:])
+ tmpres = tmpres["messages"][-1]
+ tmp_task = []
+ if tmpres.get("tool_calls", []) == []:
+ yield {"status": []}
+ else:
+ for tool_call in tmpres.get("tool_calls", []):
+ try:
+ agentname, task_text = extract_name_task(str(tool_call["args"]))
+ print("agentname", agentname)
+ print("task_text:", task_text)
+ # 使用 to_thread 异步运行同步的 Broadcast_message
+ await asyncio.to_thread(Broadcast_message, f"总控智能体给{agentname}派活,内容为:" + task_text, d_agent_thread_id, [agent_name, agentname])
+ tmp_task.append([agentname, task_text])
+ except:
+ pass
+ yield {"status": tmp_task}
+
+
+async def special_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names):
+ """使用 httpx 异步请求的 special_agent"""
+ thread_id = d_agent_thread_id[agent_name]
+ l = []
+ token = get_token("ji")
+ url = f"{BASE_URL}/api/threads/{thread_id}/runs/stream"
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+ payload = {
+ "input": {
+ "messages": [
+ {
+ "type": "human",
+ "content": [
+ {
+ "type": "text",
+ "text": new_message
+ }
+ ],
+ "additional_kwargs": {}
+ }
+ ]
+ },
+ "config": {
+ "recursion_limit": 1000
+ },
+ "context": {
+ "agent_name": agent_name,
+ "model_name": "deepseek-chat",
+ "mode": "pro",
+ "reasoning_effort": "medium",
+ "thinking_enabled": true,
+ "is_plan_mode": false,
+ "subagent_enabled": false,
+ "thread_id": thread_id
+ },
+ "stream_mode": [
+ "messages-tuple",
+ "values",
+ ],
+ "stream_subgraphs": true,
+ "stream_resumable": true,
+ "assistant_id": "lead_agent",
+ "on_disconnect": "continue",
+ "excluded_tools": ["web_search", "ask_clarification", "present_files", "view_image"],
+ "skill_stop_names": skill_stop_names,
+ }
+
+ async with httpx.AsyncClient(timeout=None) as client:
+ async with client.stream("POST", url, json=payload, headers=headers) as response:
+ print(f"状态码: {response.status_code}")
+ if response.status_code != 200:
+ body = await response.aread()
+ raise RuntimeError(
+ f"upstream {url} returned HTTP {response.status_code}: "
+ f"{body.decode('utf-8', 'replace')[:1000]}"
+ )
+ async for line in response.aiter_lines():
+ if line:
+ yield line
+ if line[:5] == "data:":
+ print(line)
+ l.append(line)
+ if len(l) < 2:
+ raise RuntimeError(
+ f"upstream stream produced no usable data lines (got {len(l)} line(s)). "
+ f"Check the server log on {url} for the real failure."
+ )
+
+ null = None
+ tmpres = eval(l[-2][5:])
+ tmpres = tmpres["messages"][-1]
+ # 使用 to_thread 异步运行同步的 Broadcast_message
+ await asyncio.to_thread(Broadcast_message, f"子智能体{agent_name}完成{new_message}工作,交付内容为:" + tmpres["content"], d_agent_thread_id, [agent_name])
+ yield {"status": f"子智能体{agent_name}完成{new_message}工作"}
+
+async def agent_init(agent_names, main_agent_name):
+ """异步的 agent_init,同步函数调用使用 to_thread"""
+ agent_names = [i.lower() for i in agent_names]
+ # 使用 to_thread 异步运行同步的 create_main_agent
+ tmpd = await asyncio.to_thread(create_main_agent, main_agent_name, agent_names)
+ message = """
+你的职责:你只是若干个智能体的其中一员,你们的共同目标是完成一个庞大的任务,总控控制协调智能体会分配特定的活给你,你只需要用你自身的能力来完成这个特定的活
+注意:你只是一个子智能体,你不必把你的分析结果存放到文件里面去,你只关心于完成总控控制协调智能体分配的活即可
+"""
+ # 使用 to_thread 异步运行同步的 Broadcast_message
+ await asyncio.to_thread(Broadcast_message, message, tmpd, [main_agent_name])
+ return main_agent_name, tmpd
+
+
+from fastapi import FastAPI, HTTPException,Body
+from fastapi.responses import StreamingResponse
+from fastapi.middleware.cors import CORSMiddleware
+import asyncio
+import uvicorn
+
+app = FastAPI()
+ # CORS 配置
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+import time
+@app.post("/generate_mul_agent")
+async def generate_stream(payload: dict = Body(...)):
+ """
+ 根据 payload 中的 agent_type 字段判断调用 leader_agent 还是 special_agent
+
+ payload 结构示例:
+ {
+ "agent_type": "leader", # 或 "special"
+ "d_agent_thread_id": {...},
+ "agent_name": "xxx",
+ "new_message": "xxx",
+ "skill_stop_names": [...]
+ }
+ """
+ # 从 payload 中提取参数
+ agent_type = payload.get("agent_type", "leader") # 默认为 leader
+ d_agent_thread_id = payload.get("d_agent_thread_id", {})
+ agent_name = payload.get("agent_name", "")
+ new_message = payload.get("new_message", "")
+ skill_stop_names = payload.get("skill_stop_names", [])
+
+ # 流式响应生成器
+ async def stream_generator():
+ # 根据 agent_type 选择调用的函数
+ if agent_type == "leader":
+ async_generator = leader_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names)
+ else: # special
+ async_generator = special_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names)
+
+ # 异步迭代生成器
+ async for chunk in async_generator:
+ if isinstance(chunk, dict):
+ yield f"data: {json.dumps(chunk, ensure_ascii=False)}\n\n"
+ else:
+ yield f"{chunk}"
+
+ # 使用 SSE 流式响应
+ return StreamingResponse(
+ stream_generator(),
+ media_type="text/event-stream",
+ headers={
+ "Cache-Control": "no-cache",
+ "X-Accel-Buffering": "no",
+ "Connection": "keep-alive",
+ }
+ )
+
+
+@app.post("/agent_init")
+async def init_agents(payload: dict = Body(...)):
+ """
+ 初始化智能体接口
+ payload 结构示例:
+ {
+ "agent_names": ["agent1", "agent2"],
+ "main_agent_name": "main-agent"
+ }
+ """
+ agent_names = payload.get("agent_names", [])
+ main_agent_name = payload.get("main_agent_name", "main-agent")
+
+ # 调用异步的 agent_init
+ name, thread_id_dict = await agent_init(agent_names, main_agent_name)
+
+ return {
+ "status": "success",
+ "main_agent_name": name,
+ "thread_ids": thread_id_dict
+ }
+if __name__ == "__main__":
+ uvicorn.run("testapi:app", host="0.0.0.0", port=8095, workers=1,limit_concurrency=1000) # 多进程提升并发能力
\ No newline at end of file
diff --git a/newpython/OUT.py b/newpython/OUT.py
new file mode 100644
index 00000000..f11ae88e
--- /dev/null
+++ b/newpython/OUT.py
@@ -0,0 +1,483 @@
+"""
+测试流式接口的完整脚本
+API流程:
+1. 登录获取token
+2. 创建thread(可选,也可以使用无状态接口)
+3. 调用流式接口
+
+两种方式:
+- 方式1: POST /api/runs/stream (无状态,自动创建thread)
+- 方式2: POST /api/threads/{thread_id}/runs/stream (需要先创建thread)
+"""
+import requests
+import json
+import asyncio
+import httpx # 异步 HTTP 客户端
+# ============ 配置区域 ============
+BASE_URL = "http://127.0.0.1:8001"
+LOGIN_URL = f"{BASE_URL}/api/v1/auth/login/username"
+THREADS_URL = f"{BASE_URL}/api/threads"
+RUNS_STREAM_URL = f"{BASE_URL}/api/runs/stream" # 无状态流式接口
+
+# ============ 登录获取token ============
+def get_token(username: str) -> str:
+ """登录获取token"""
+ response = requests.post(
+ LOGIN_URL,
+ json={"username": username},
+ headers={"Content-Type": "application/json"}
+ )
+ # print(f"登录状态码: {response.status_code}")
+ data = response.json()
+ # print(f"登录响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
+
+ # 根据实际返回结构调整
+ token = data.get("token") or data.get("access_token") or data.get("data", {}).get("token")
+ return token
+# ============ 方式2: 先创建thread,再调用流式接口 ============
+def create_thread(token: str) -> str:
+ """创建thread,返回thread_id"""
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+
+ response = requests.post(
+ THREADS_URL,
+ json={}, # 空body,使用默认值
+ headers=headers
+ )
+
+ print(f"创建thread状态码: {response.status_code}")
+ data = response.json()
+ print(f"创建thread响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
+
+ return data.get("thread_id")
+def get_agent_name_description(agent_name="Taiwan-intelligence-gathering-agent"):
+ url = f"{BASE_URL}/api/agents/{agent_name}"
+ token=get_token("ji")
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+ response = requests.get(
+ url,
+ headers=headers,
+ )
+ print(response.json())
+ return response.json()["name"].lower(),"agent_name:"+response.json()["name"].lower()+","+response.json()["description"]
+
+# ============ 创建智能体 ============
+def create_agent(
+ name: str,
+ description: str = "",
+ model: str | None = None,
+ tool_groups: list[str] | None = None,
+ skills: list[str] | None = None,
+ soul: str = "",
+):
+ """
+ 创建一个新的智能体
+
+ Args:
+ name: 智能体名称(必须是 hyphen-case,如 my-agent)
+ description: 智能体描述
+ model: 可选的模型覆盖(如 gpt-4, claude-3-opus 等)
+ tool_groups: 工具组白名单(如 ["web", "file"])
+ skills: 技能白名单(如 ["skill1", "skill2"])
+ soul: SOUL.md 内容(智能体人格/身份描述)
+
+ Returns:
+ 创建结果
+ """
+ TOKEN =get_token("ji")
+ url = f"{BASE_URL}/api/agents"
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {TOKEN}"
+ }
+
+ payload = {
+ "name": name,
+ "description": description,
+ "soul": soul
+ }
+
+ # 添加可选字段
+ if model is not None:
+ payload["model"] = model
+ if tool_groups is not None:
+ payload["tool_groups"] = tool_groups
+ if skills is not None:
+ payload["skills"] = skills
+
+ print(f"请求: POST {url}")
+ print(f"请求体: {json.dumps(payload, indent=2, ensure_ascii=False)}")
+
+ response = requests.post(url, json=payload, headers=headers)
+
+ print(f"状态码: {response.status_code}")
+
+ if response.status_code == 201:
+ result = response.json()
+ print(f"✓ 智能体创建成功!")
+ print(f"响应: {json.dumps(result, indent=2, ensure_ascii=False)}")
+ return result
+ else:
+ print(f"✗ 创建失败: {response.text}")
+ return f"✗ 创建失败: {response.text}"
+
+def create_main_agent(zhihui_name,agent_names):
+ d={}
+ token=get_token("ji")
+ l=[]
+ for i in agent_names:
+ # print(i)
+ name,description=get_agent_name_description(i)
+ thread_id_tmp=create_thread(token)
+ d[name]=thread_id_tmp
+ l.append(description)
+ name="你是总控控制协调智能体,负责协调其他智能体干活"
+ miaoshu=f"""#核心定位:
+ 用途:跟用用户的任务目标,调用其他智能体干活,只负责协调汇总,当你觉得已有信息支持回答用户问题时候,停止调度,并输出最后的结果
+ #调度方法:
+ 通过使用agent_orchestration技能调用其他智能体干活,使用技能时传入agent_name和需要做的活
+ #可调度智能体:
+ {l}"""
+ thread_id_tmp=create_thread(token)
+ engname=zhihui_name
+ d[engname]=thread_id_tmp
+ tmp_skills=["agent_orchestration"]
+ tmpres=create_agent(
+ name=engname,
+ description=name,
+ model="glm-5", # 指定模型
+ skills=tmp_skills, # 关联技能
+ soul=miaoshu)
+ # print("创建智能体响应:",tmpres)
+ if "失败" in tmpres and "exists" not in tmpres:
+ return tmpres
+ print(f"创建协调智能体成功,可以协调列表:{l}")
+ return d
+import re
+def extract_name_task(text="python3 scripts/agent_orchestration.py \"Us-intelligence-gathering-agent\" \"收集和分析美国人民对特朗普的看法,包括支持率和反对率、不同群体的观点差异、主要支持理由和反对理由等\""):
+ try:
+ pattern = r'\.py\s+"([^"]*)"\s+"([^"]*)"'
+ match = re.search(pattern, text)
+ if match:
+ agent_name = match.group(1)
+ task_content = match.group(2)
+ return agent_name,task_content
+ except:
+ return "",""
+def update_agent_message(thread_id,new_content):
+ token=get_token("ji")
+ url = f"{BASE_URL}/api/threads/{thread_id}/state"
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+ tmp = requests.get(
+ url,
+ headers=headers
+ ).json()
+ # print(tmp)
+ if "messages" in tmp["values"]:
+ tmp["values"]["messages"].append({"type": "human", "content": new_content})
+ else:
+ tmp["values"]["messages"]=[]
+ tmp["values"]["messages"].append({"type": "human", "content": new_content})
+ url = f"{BASE_URL}/api/threads/{thread_id}/state"
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+ response = requests.post(
+ url,
+ json=tmp,
+ headers=headers,
+ stream=True
+ )
+ del tmp
+ print(f"线程{thread_id}更新状态码: {response.status_code}")
+def Broadcast_message(message,d_agent_thread_id,pingbi_agent_name):
+ for key in d_agent_thread_id:
+ if key not in pingbi_agent_name:
+ update_agent_message(d_agent_thread_id[key],message)
+ print("广播消息成功:",key,message)
+null=None
+true=True
+false=False
+async def leader_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names):
+ """使用 httpx 异步请求的 leader_agent"""
+ # 使用 to_thread 异步运行同步的 Broadcast_message
+ await asyncio.to_thread(Broadcast_message, "总控智能体收到用户消息:" + new_message, d_agent_thread_id, [agent_name])
+ thread_id = d_agent_thread_id[agent_name]
+ l = []
+ token = get_token("ji")
+ url = f"{BASE_URL}/api/threads/{thread_id}/runs/stream"
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+ payload = {
+ "input": {
+ "messages": [
+ {
+ "type": "human",
+ "content": [
+ {
+ "type": "text",
+ "text": new_message
+ }
+ ],
+ "additional_kwargs": {}
+ }
+ ]
+ },
+ "config": {
+ "recursion_limit": 1000
+ },
+ "context": {
+ "agent_name": agent_name,
+ "model_name": "deepseek-chat",
+ "mode": "pro",
+ "reasoning_effort": "medium",
+ "thinking_enabled": true,
+ "is_plan_mode": false,
+ "subagent_enabled": false,
+ "thread_id": thread_id
+ },
+ "stream_mode": [
+ "messages-tuple",
+ "values",
+ ],
+ "stream_subgraphs": true,
+ "stream_resumable": true,
+ "assistant_id": "lead_agent",
+ "on_disconnect": "continue",
+ "excluded_tools": ["web_search"],
+ "skill_stop_names": skill_stop_names,
+ }
+
+ async with httpx.AsyncClient(timeout=None) as client:
+ async with client.stream("POST", url, json=payload, headers=headers) as response:
+ print(f"状态码: {response.status_code}")
+ if response.status_code != 200:
+ body = await response.aread()
+ raise RuntimeError(
+ f"upstream {url} returned HTTP {response.status_code}: "
+ f"{body.decode('utf-8', 'replace')[:1000]}"
+ )
+ async for line in response.aiter_lines():
+ if line:
+ yield line
+ if line[:5] == "data:":
+ print(line)
+ l.append(line)
+ if len(l) < 2:
+ raise RuntimeError(
+ f"upstream stream produced no usable data lines (got {len(l)} line(s)). "
+ f"Check the server log on {url} for the real failure."
+ )
+
+ null = None
+ tmpres = eval(l[-2][5:])
+ tmpres = tmpres["messages"][-1]
+ tmp_task = []
+ if tmpres.get("tool_calls", []) == []:
+ yield {"status": []}
+ else:
+ for tool_call in tmpres.get("tool_calls", []):
+ try:
+ agentname, task_text = extract_name_task(str(tool_call["args"]))
+ print("agentname", agentname)
+ print("task_text:", task_text)
+ # 使用 to_thread 异步运行同步的 Broadcast_message
+ await asyncio.to_thread(Broadcast_message, f"总控智能体给{agentname}派活,内容为:" + task_text, d_agent_thread_id, [agent_name, agentname])
+ tmp_task.append([agentname, task_text])
+ except:
+ pass
+ yield {"status": tmp_task}
+
+
+async def special_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names):
+ """使用 httpx 异步请求的 special_agent"""
+ thread_id = d_agent_thread_id[agent_name]
+ l = []
+ token = get_token("ji")
+ url = f"{BASE_URL}/api/threads/{thread_id}/runs/stream"
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {token}"
+ }
+ payload = {
+ "input": {
+ "messages": [
+ {
+ "type": "human",
+ "content": [
+ {
+ "type": "text",
+ "text": new_message
+ }
+ ],
+ "additional_kwargs": {}
+ }
+ ]
+ },
+ "config": {
+ "recursion_limit": 1000
+ },
+ "context": {
+ "agent_name": agent_name,
+ "model_name": "deepseek-chat",
+ "mode": "pro",
+ "reasoning_effort": "medium",
+ "thinking_enabled": true,
+ "is_plan_mode": false,
+ "subagent_enabled": false,
+ "thread_id": thread_id
+ },
+ "stream_mode": [
+ "messages-tuple",
+ "values",
+ ],
+ "stream_subgraphs": true,
+ "stream_resumable": true,
+ "assistant_id": "lead_agent",
+ "on_disconnect": "continue",
+ "excluded_tools": ["web_search", "ask_clarification", "present_files", "view_image"],
+ "skill_stop_names": skill_stop_names,
+ }
+
+ async with httpx.AsyncClient(timeout=None) as client:
+ async with client.stream("POST", url, json=payload, headers=headers) as response:
+ print(f"状态码: {response.status_code}")
+ if response.status_code != 200:
+ body = await response.aread()
+ raise RuntimeError(
+ f"upstream {url} returned HTTP {response.status_code}: "
+ f"{body.decode('utf-8', 'replace')[:1000]}"
+ )
+ async for line in response.aiter_lines():
+ if line:
+ yield line
+ if line[:5] == "data:":
+ print(line)
+ l.append(line)
+ if len(l) < 2:
+ raise RuntimeError(
+ f"upstream stream produced no usable data lines (got {len(l)} line(s)). "
+ f"Check the server log on {url} for the real failure."
+ )
+
+ null = None
+ tmpres = eval(l[-2][5:])
+ tmpres = tmpres["messages"][-1]
+ # 使用 to_thread 异步运行同步的 Broadcast_message
+ await asyncio.to_thread(Broadcast_message, f"子智能体{agent_name}完成{new_message}工作,交付内容为:" + tmpres["content"], d_agent_thread_id, [agent_name])
+ yield {"status": f"子智能体{agent_name}完成{new_message}工作"}
+
+async def agent_init(agent_names, main_agent_name):
+ """异步的 agent_init,同步函数调用使用 to_thread"""
+ agent_names = [i.lower() for i in agent_names]
+ # 使用 to_thread 异步运行同步的 create_main_agent
+ tmpd = await asyncio.to_thread(create_main_agent, main_agent_name, agent_names)
+ message = """
+你的职责:你只是若干个智能体的其中一员,你们的共同目标是完成一个庞大的任务,总控控制协调智能体会分配特定的活给你,你只需要用你自身的能力来完成这个特定的活
+注意:你只是一个子智能体,你不必把你的分析结果存放到文件里面去,你只关心于完成总控控制协调智能体分配的活即可
+"""
+ # 使用 to_thread 异步运行同步的 Broadcast_message
+ await asyncio.to_thread(Broadcast_message, message, tmpd, [main_agent_name])
+ return main_agent_name, tmpd
+
+
+from fastapi import FastAPI, HTTPException,Body
+from fastapi.responses import StreamingResponse
+from fastapi.middleware.cors import CORSMiddleware
+import asyncio
+import uvicorn
+
+app = FastAPI()
+ # CORS 配置
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+import time
+@app.post("/generate_mul_agent")
+async def generate_stream(payload: dict = Body(...)):
+ """
+ 根据 payload 中的 agent_type 字段判断调用 leader_agent 还是 special_agent
+
+ payload 结构示例:
+ {
+ "agent_type": "leader", # 或 "special"
+ "d_agent_thread_id": {...},
+ "agent_name": "xxx",
+ "new_message": "xxx",
+ "skill_stop_names": [...]
+ }
+ """
+ # 从 payload 中提取参数
+ agent_type = payload.get("agent_type", "leader") # 默认为 leader
+ d_agent_thread_id = payload.get("d_agent_thread_id", {})
+ agent_name = payload.get("agent_name", "")
+ new_message = payload.get("new_message", "")
+ skill_stop_names = payload.get("skill_stop_names", [])
+
+ # 流式响应生成器
+ async def stream_generator():
+ # 根据 agent_type 选择调用的函数
+ if agent_type == "leader":
+ async_generator = leader_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names)
+ else: # special
+ async_generator = special_agent(d_agent_thread_id, agent_name, new_message, skill_stop_names)
+
+ # 异步迭代生成器
+ async for chunk in async_generator:
+ if isinstance(chunk, dict):
+ yield f"data: {json.dumps(chunk, ensure_ascii=False)}\n\n"
+ else:
+ yield f"{chunk}"
+
+ # 使用 SSE 流式响应
+ return StreamingResponse(
+ stream_generator(),
+ media_type="text/event-stream",
+ headers={
+ "Cache-Control": "no-cache",
+ "X-Accel-Buffering": "no",
+ "Connection": "keep-alive",
+ }
+ )
+
+
+@app.post("/agent_init")
+async def init_agents(payload: dict = Body(...)):
+ """
+ 初始化智能体接口
+ payload 结构示例:
+ {
+ "agent_names": ["agent1", "agent2"],
+ "main_agent_name": "main-agent"
+ }
+ """
+ agent_names = payload.get("agent_names", [])
+ main_agent_name = payload.get("main_agent_name", "main-agent")
+
+ # 调用异步的 agent_init
+ name, thread_id_dict = await agent_init(agent_names, main_agent_name)
+
+ return {
+ "status": "success",
+ "main_agent_name": name,
+ "thread_ids": thread_id_dict
+ }
+if __name__ == "__main__":
+ uvicorn.run("testapi:app", host="0.0.0.0", port=8095, workers=1,limit_concurrency=1000) # 多进程提升并发能力
\ No newline at end of file
diff --git a/newpython/__pycache__/OUT.cpython-310.pyc b/newpython/__pycache__/OUT.cpython-310.pyc
new file mode 100644
index 00000000..0f8e4dc7
Binary files /dev/null and b/newpython/__pycache__/OUT.cpython-310.pyc differ
diff --git a/newpython/__pycache__/demo_call.cpython-310.pyc b/newpython/__pycache__/demo_call.cpython-310.pyc
new file mode 100644
index 00000000..701e5343
Binary files /dev/null and b/newpython/__pycache__/demo_call.cpython-310.pyc differ
diff --git a/newpython/demo_call.py b/newpython/demo_call.py
new file mode 100644
index 00000000..c4df49e6
--- /dev/null
+++ b/newpython/demo_call.py
@@ -0,0 +1,132 @@
+"""
+OUT.py 的调用链 demo —— 跑通 agent_init → leader 派活 → 各 special agent 干活 这一整套流程。
+
+前置条件:
+ 1. DeerFlow 后端在 http://127.0.0.1:8001 在跑
+ 2. OUT.py 已经 `python -m uvicorn OUT:app --host 0.0.0.0 --port 8095` 起好
+ 3. 下面 SUB_AGENT_NAMES 里的 agent 已经在 DeerFlow 里创建好(/api/agents 能查到)
+
+直接 `python demo_call.py` 即可。
+"""
+import asyncio
+import json
+
+import httpx
+
+# ============ 改这里 ============
+OUT_BASE = "http://127.0.0.1:8095"
+
+# 已经在 DeerFlow 里建好的子 agent 名字 (会被 OUT.py 强制 lowercase)
+SUB_AGENT_NAMES = [
+ "us-intelligence-gathering-agent",
+ "taiwan-intelligence-gathering-agent",
+]
+
+# 总控 agent 名字 —— OUT.py 会现场创建它,所以填一个新名字即可 (hyphen-case)
+MAIN_AGENT_NAME = "demo-coordinator"
+
+# 用户提问 —— 总控会基于此派活
+USER_QUESTION = "对比中美两国 2024 年在 AI 监管政策上的差异"
+
+# leader 阶段命中即停的 skill 名字单 (OUT.py 用 agent_orchestration 作为派活信号)
+SKILL_STOP_NAMES = ["agent_orchestration"]
+# ================================
+
+
+async def call_agent_init() -> tuple[str, dict[str, str]]:
+ """步骤 1: 初始化 —— 拿回 thread_ids 字典 {agent_name → thread_id}."""
+ print(">>> [1/3] POST /agent_init")
+ async with httpx.AsyncClient(timeout=300.0) as client:
+ r = await client.post(
+ f"{OUT_BASE}/agent_init",
+ json={
+ "agent_names": SUB_AGENT_NAMES,
+ "main_agent_name": MAIN_AGENT_NAME,
+ },
+ )
+ r.raise_for_status()
+ body = r.json()
+ main_name = body["main_agent_name"]
+ thread_ids = body["thread_ids"]
+ print(f" main_agent_name = {main_name}")
+ print(f" thread_ids = {json.dumps(thread_ids, ensure_ascii=False, indent=2)}")
+ return main_name, thread_ids
+
+
+async def stream_generate(payload: dict) -> object | None:
+ """通用 SSE 消费:边收边打印,最后返回 OUT.py 自己 yield 的 {'status': ...} 里的 status 字段."""
+ final_status = None
+ async with httpx.AsyncClient(timeout=None) as client:
+ async with client.stream("POST", f"{OUT_BASE}/generate_mul_agent", json=payload) as resp:
+ print(f" HTTP {resp.status_code}")
+ if resp.status_code != 200:
+ err = await resp.aread()
+ raise RuntimeError(f"OUT.py /generate_mul_agent failed: {err.decode('utf-8', 'replace')[:500]}")
+
+ async for line in resp.aiter_lines():
+ if not line:
+ continue
+ # OUT.py 把 DeerFlow 的原始 SSE 流直接转发,自己最后会再 yield 一行
+ # `data: {"status": ...}\n\n`。两边都以 `data:` 开头,我们都尝试 JSON parse
+ # 一下,能解析成 {"status": ...} 的就是 OUT.py 的终态。
+ if line.startswith("data:"):
+ body_text = line[5:].strip()
+ try:
+ obj = json.loads(body_text)
+ except json.JSONDecodeError:
+ continue
+ if isinstance(obj, dict) and "status" in obj:
+ final_status = obj["status"]
+ # 想看上游 DeerFlow 的完整流,把下面打开即可
+ # else:
+ # print(" raw:", line[:200])
+ return final_status
+
+
+async def call_leader(main_agent_name: str, thread_ids: dict[str, str]) -> list[list[str]]:
+ """步骤 2: leader 阶段 —— 总控读完用户问题,产出派活清单."""
+ print(f"\n>>> [2/3] POST /generate_mul_agent (leader, agent={main_agent_name})")
+ status = await stream_generate(
+ {
+ "agent_type": "leader",
+ "d_agent_thread_id": thread_ids,
+ "agent_name": main_agent_name,
+ "new_message": USER_QUESTION,
+ "skill_stop_names": SKILL_STOP_NAMES,
+ }
+ )
+ dispatch = status or []
+ print(f" 派活清单 = {json.dumps(dispatch, ensure_ascii=False, indent=2)}")
+ return dispatch
+
+
+async def call_specials(thread_ids: dict[str, str], dispatch: list[list[str]]) -> None:
+ """步骤 3: 把派活清单里每一项交给对应子 agent 跑."""
+ if not dispatch:
+ print("\n>>> [3/3] 总控没派出任何任务,直接结束。")
+ return
+ for idx, item in enumerate(dispatch, 1):
+ agent_name, task_text = item[0], item[1]
+ print(f"\n>>> [3/3] ({idx}/{len(dispatch)}) POST /generate_mul_agent (special, agent={agent_name})")
+ print(f" task = {task_text[:120]}{'...' if len(task_text) > 120 else ''}")
+ result = await stream_generate(
+ {
+ "agent_type": "special",
+ "d_agent_thread_id": thread_ids,
+ "agent_name": agent_name,
+ "new_message": task_text,
+ "skill_stop_names": [],
+ }
+ )
+ print(f" 完成: {result}")
+
+
+async def main() -> None:
+ main_agent_name, thread_ids = await call_agent_init()
+ dispatch = await call_leader(main_agent_name, thread_ids)
+ await call_specials(thread_ids, dispatch)
+ print("\n✅ demo 跑完")
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
diff --git a/offline-backend-20260512/backend/app/gateway/app.py b/offline-backend-20260512/backend/app/gateway/app.py
index 3d32f61b..e1e10155 100644
--- a/offline-backend-20260512/backend/app/gateway/app.py
+++ b/offline-backend-20260512/backend/app/gateway/app.py
@@ -1,10 +1,10 @@
-import asyncio
-import logging
-import os
-import queue
-import sys
-import threading
-from collections.abc import AsyncGenerator
+import asyncio
+import logging
+import os
+import queue
+import sys
+import threading
+from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from dotenv import load_dotenv
@@ -36,6 +36,7 @@ from app.gateway.routers import (
mcp,
memory,
models,
+ multi_agent,
notifications,
public_scheduled_tasks,
recommended_questions,
@@ -247,7 +248,7 @@ async def _migrate_orphaned_threads(store, admin_user_id: str) -> int:
async def _run_db_migrations() -> None:
"""Run Alembic migrations at startup so schema changes are applied automatically."""
- from pathlib import Path
+ from pathlib import Path
from alembic import command
from alembic.config import Config
@@ -260,51 +261,51 @@ async def _run_db_migrations() -> None:
logger.info("Persistence backend=memory — skipping Alembic migrations")
return
- if os.getenv("DEER_FLOW_SKIP_ALEMBIC", "").lower() in {"1", "true", "yes"}:
- logger.warning("DEER_FLOW_SKIP_ALEMBIC is set; skipping Alembic migrations")
- return
-
- db_url = engine.url.render_as_string(hide_password=False).replace("%", "%%")
- migrations_dir = str(Path(_persistence_pkg.__file__).parent / "migrations")
- result_queue: queue.Queue[BaseException | None] = queue.Queue(maxsize=1)
-
- def _run_sync() -> None:
- try:
- cfg = Config()
- cfg.set_main_option("script_location", migrations_dir)
- cfg.set_main_option("sqlalchemy.url", db_url)
- command.upgrade(cfg, "head")
- except BaseException as exc:
- result_queue.put(exc)
- else:
- result_queue.put(None)
-
- try:
- try:
- timeout_s = float(os.getenv("DEER_FLOW_ALEMBIC_TIMEOUT", "60"))
- except ValueError:
- timeout_s = 60.0
-
- worker = threading.Thread(target=_run_sync, name="deerflow-alembic-upgrade", daemon=True)
- worker.start()
- try:
- deadline = asyncio.get_running_loop().time() + timeout_s
- while result_queue.empty():
- if asyncio.get_running_loop().time() >= deadline:
- raise asyncio.TimeoutError
- await asyncio.sleep(0.2)
- result = result_queue.get_nowait()
- except asyncio.TimeoutError:
- logger.error(
- "Database migration did not finish within %.0fs; continuing startup on create_all schema. "
- "Likely cause: MySQL metadata-lock wait or a slow DDL. Set DEER_FLOW_SKIP_ALEMBIC=1 "
- "to skip startup migrations, or inspect MySQL with SHOW FULL PROCESSLIST.",
- timeout_s,
- )
- return
- if result is not None:
- raise result
- logger.info("Database migrations applied (head)")
+ if os.getenv("DEER_FLOW_SKIP_ALEMBIC", "").lower() in {"1", "true", "yes"}:
+ logger.warning("DEER_FLOW_SKIP_ALEMBIC is set; skipping Alembic migrations")
+ return
+
+ db_url = engine.url.render_as_string(hide_password=False).replace("%", "%%")
+ migrations_dir = str(Path(_persistence_pkg.__file__).parent / "migrations")
+ result_queue: queue.Queue[BaseException | None] = queue.Queue(maxsize=1)
+
+ def _run_sync() -> None:
+ try:
+ cfg = Config()
+ cfg.set_main_option("script_location", migrations_dir)
+ cfg.set_main_option("sqlalchemy.url", db_url)
+ command.upgrade(cfg, "head")
+ except BaseException as exc:
+ result_queue.put(exc)
+ else:
+ result_queue.put(None)
+
+ try:
+ try:
+ timeout_s = float(os.getenv("DEER_FLOW_ALEMBIC_TIMEOUT", "60"))
+ except ValueError:
+ timeout_s = 60.0
+
+ worker = threading.Thread(target=_run_sync, name="deerflow-alembic-upgrade", daemon=True)
+ worker.start()
+ try:
+ deadline = asyncio.get_running_loop().time() + timeout_s
+ while result_queue.empty():
+ if asyncio.get_running_loop().time() >= deadline:
+ raise asyncio.TimeoutError
+ await asyncio.sleep(0.2)
+ result = result_queue.get_nowait()
+ except asyncio.TimeoutError:
+ logger.error(
+ "Database migration did not finish within %.0fs; continuing startup on create_all schema. "
+ "Likely cause: MySQL metadata-lock wait or a slow DDL. Set DEER_FLOW_SKIP_ALEMBIC=1 "
+ "to skip startup migrations, or inspect MySQL with SHOW FULL PROCESSLIST.",
+ timeout_s,
+ )
+ return
+ if result is not None:
+ raise result
+ logger.info("Database migrations applied (head)")
except Exception:
logger.exception("Database migration failed — schema may be out of date")
@@ -860,6 +861,9 @@ This gateway provides custom endpoints for models, MCP configuration, skills, an
# Stateless Runs API (stream/wait without a pre-existing thread)
app.include_router(runs.router)
+ # Multi-agent orchestration (coordinator + sub-agent broadcast/dispatch)
+ app.include_router(multi_agent.router)
+
@app.get("/health", tags=["health"])
async def health_check() -> dict:
"""Health check endpoint.
diff --git a/offline-backend-20260512/backend/app/gateway/routers/agents.py b/offline-backend-20260512/backend/app/gateway/routers/agents.py
index 83bc7bd1..800c4364 100644
--- a/offline-backend-20260512/backend/app/gateway/routers/agents.py
+++ b/offline-backend-20260512/backend/app/gateway/routers/agents.py
@@ -60,6 +60,10 @@ class AgentsListResponse(BaseModel):
class AgentCreateRequest(BaseModel):
"""Request body for creating a custom agent."""
+ id: str | None = Field(
+ default=None,
+ description="Optional explicit storage id. Must match the storage id pattern. If omitted, a UUID is generated.",
+ )
name: str = Field(..., description="Display name; Chinese and duplicate names are allowed")
description: str = Field(default="", description="Agent description")
soul: str = Field(default="", description="SOUL.md content - agent personality and behavioral guardrails")
@@ -411,13 +415,19 @@ async def get_agent(agent_id: str, request: Request) -> AgentResponse:
async def create_agent_endpoint(request: AgentCreateRequest, http_request: Request) -> AgentResponse:
_require_agents_api_enabled()
name = _validate_display_name(request.name)
- agent_id = uuid4().hex
+ store = get_agent_store(http_request)
+
+ if request.id:
+ agent_id = _validate_storage_id(request.id)
+ if await store.get_any(agent_id) is not None:
+ raise HTTPException(status_code=409, detail=f"Agent id '{agent_id}' already exists")
+ else:
+ agent_id = uuid4().hex
agent_dir = get_paths().agent_dir(agent_id)
try:
- store = get_agent_store(http_request)
if agent_dir.exists():
- raise HTTPException(status_code=409, detail=f"Generated agent id '{agent_id}' already exists")
+ raise HTTPException(status_code=409, detail=f"Agent id '{agent_id}' already exists")
_write_agent_config(
agent_id,
diff --git a/offline-backend-20260512/backend/app/gateway/routers/multi_agent.py b/offline-backend-20260512/backend/app/gateway/routers/multi_agent.py
new file mode 100644
index 00000000..454cc773
--- /dev/null
+++ b/offline-backend-20260512/backend/app/gateway/routers/multi_agent.py
@@ -0,0 +1,471 @@
+"""Multi-agent orchestration endpoints.
+
+Wraps the legacy ``newpython/OUT.py`` demo into the Gateway. Provides:
+
+* ``POST /api/multi-agent/init`` — create a coordinator agent and per-sub-agent
+ threads. Returns the ``{agent_name: thread_id}`` map the client passes back
+ on subsequent calls.
+* ``POST /api/multi-agent/run/stream`` — drive a *leader* or *special* agent
+ via SSE. The leader dispatches work to sub-agents through the
+ ``agent_orchestration`` skill; sub-agents execute and broadcast their result.
+
+Internal calls loop back to the same Gateway over HTTP, reusing the caller's
+``Authorization`` header — so the demo's hard-coded ``get_token("ji")`` is gone.
+The model used by both the coordinator agent and every streamed run is taken
+from the request's ``model`` field.
+"""
+
+from __future__ import annotations
+
+import asyncio
+import json
+import logging
+import re
+from typing import Any, AsyncIterator
+
+import httpx
+from fastapi import APIRouter, Body, HTTPException, Request
+from fastapi.responses import StreamingResponse
+
+logger = logging.getLogger(__name__)
+router = APIRouter(prefix="/api/multi-agent", tags=["multi-agent"])
+
+
+# ---------------------------------------------------------------------------
+# Loopback helpers
+# ---------------------------------------------------------------------------
+
+
+def _loopback_base(request: Request) -> str:
+ return f"{request.url.scheme}://{request.url.netloc}"
+
+
+def _auth_headers(request: Request) -> dict[str, str]:
+ token = request.headers.get("authorization")
+ if not token:
+ raise HTTPException(status_code=401, detail="Missing Authorization header")
+ return {"Authorization": token, "Content-Type": "application/json"}
+
+
+async def _create_thread(client: httpx.AsyncClient, base: str, headers: dict[str, str]) -> str:
+ resp = await client.post(f"{base}/api/threads", json={}, headers=headers)
+ resp.raise_for_status()
+ return resp.json()["thread_id"]
+
+
+async def _get_agent(client: httpx.AsyncClient, base: str, headers: dict[str, str], agent_id: str) -> dict[str, Any]:
+ resp = await client.get(f"{base}/api/agents/{agent_id}", headers=headers)
+ resp.raise_for_status()
+ return resp.json()
+
+
+async def _create_agent(
+ client: httpx.AsyncClient,
+ base: str,
+ headers: dict[str, str],
+ *,
+ name: str,
+ description: str,
+ model: str | None,
+ skills: list[str] | None,
+ soul: str,
+ agent_id: str | None = None,
+) -> tuple[bool, dict[str, Any] | str]:
+ payload: dict[str, Any] = {"name": name, "description": description, "soul": soul}
+ if agent_id is not None:
+ payload["id"] = agent_id
+ if model is not None:
+ payload["model"] = model
+ if skills is not None:
+ payload["skills"] = skills
+ resp = await client.post(f"{base}/api/agents", json=payload, headers=headers)
+ if resp.status_code == 201:
+ return True, resp.json()
+ return False, resp.text
+
+
+async def _append_thread_message(
+ client: httpx.AsyncClient,
+ base: str,
+ headers: dict[str, str],
+ thread_id: str,
+ content: str,
+) -> None:
+ state_url = f"{base}/api/threads/{thread_id}/state"
+ get_resp = await client.get(state_url, headers=headers)
+ if get_resp.status_code != 200:
+ logger.warning("get state %s failed: %s %s", thread_id, get_resp.status_code, get_resp.text[:200])
+ return
+ state = get_resp.json()
+ values = state.setdefault("values", {})
+ messages = values.setdefault("messages", [])
+ if not isinstance(messages, list):
+ messages = []
+ values["messages"] = messages
+ messages.append({"type": "human", "content": content})
+ post_resp = await client.post(state_url, json=state, headers=headers)
+ if post_resp.status_code >= 400:
+ logger.warning("update state %s failed: %s %s", thread_id, post_resp.status_code, post_resp.text[:200])
+
+
+async def _broadcast(
+ client: httpx.AsyncClient,
+ base: str,
+ headers: dict[str, str],
+ message: str,
+ agent_threads: dict[str, str],
+ skip: list[str],
+) -> None:
+ targets = [tid for name, tid in agent_threads.items() if name not in skip]
+ if not targets:
+ return
+ await asyncio.gather(
+ *(_append_thread_message(client, base, headers, tid, message) for tid in targets),
+ return_exceptions=True,
+ )
+
+
+_TOOL_CALL_PATTERN = re.compile(r'\.py\s+"([^"]*)"\s+"([^"]*)"')
+
+
+def _extract_name_task(text: str) -> tuple[str, str]:
+ match = _TOOL_CALL_PATTERN.search(text)
+ if match:
+ return match.group(1), match.group(2)
+ return "", ""
+
+
+def _flatten_content(content: Any) -> str:
+ if isinstance(content, str):
+ return content
+ if isinstance(content, list):
+ parts: list[str] = []
+ for item in content:
+ if isinstance(item, dict):
+ parts.append(str(item.get("text", "")))
+ else:
+ parts.append(str(item))
+ return "".join(parts)
+ return str(content)
+
+
+def _run_payload(
+ *,
+ agent_name: str,
+ model_name: str,
+ thread_id: str,
+ new_message: str,
+ excluded_tools: list[str],
+ skill_stop_names: list[str],
+) -> dict[str, Any]:
+ return {
+ "input": {
+ "messages": [
+ {
+ "type": "human",
+ "content": [{"type": "text", "text": new_message}],
+ "additional_kwargs": {},
+ }
+ ]
+ },
+ "config": {"recursion_limit": 1000},
+ "context": {
+ "agent_name": agent_name,
+ "model_name": model_name,
+ "mode": "pro",
+ "reasoning_effort": "medium",
+ "thinking_enabled": True,
+ "is_plan_mode": False,
+ "subagent_enabled": False,
+ "thread_id": thread_id,
+ },
+ "stream_mode": ["messages-tuple", "values"],
+ "stream_subgraphs": True,
+ "stream_resumable": True,
+ "assistant_id": "lead_agent",
+ "on_disconnect": "continue",
+ "excluded_tools": excluded_tools,
+ "skill_stop_names": skill_stop_names,
+ }
+
+
+# ---------------------------------------------------------------------------
+# Init endpoint
+# ---------------------------------------------------------------------------
+
+
+@router.post("/init")
+async def init_multi_agent(request: Request, payload: dict = Body(...)) -> dict[str, Any]:
+ agent_names_in = payload.get("agent_names") or []
+ agent_names: list[str] = [str(name).lower() for name in agent_names_in]
+ main_agent_name: str = str(payload.get("main_agent_name") or "main-agent").lower()
+ model: str | None = payload.get("model")
+
+ if not agent_names:
+ raise HTTPException(status_code=400, detail="agent_names is required")
+
+ base = _loopback_base(request)
+ headers = _auth_headers(request)
+
+ async with httpx.AsyncClient(timeout=None) as client:
+ # Optimization A: fetch every sub-agent's metadata and create its
+ # thread concurrently. With 6 seats this turns ~12 sequential
+ # loopback round-trips into one wave of 12 parallel ones.
+ async def _prepare_sub_agent(sub_id: str) -> tuple[str, str, str]:
+ info = await _get_agent(client, base, headers, sub_id)
+ sub_name = str(info["name"]).lower()
+ thread_id = await _create_thread(client, base, headers)
+ return sub_name, thread_id, info.get("description", "") or ""
+
+ prepared = await asyncio.gather(*(_prepare_sub_agent(sid) for sid in agent_names))
+
+ agent_threads: dict[str, str] = {sub_name: thread_id for sub_name, thread_id, _ in prepared}
+ descriptions: list[str] = [
+ f"agent_name:{sub_name},{description}" for sub_name, _, description in prepared
+ ]
+
+ soul = (
+ "#核心定位:\n"
+ "用途:根据用户的任务目标,调用其他智能体干活,只负责协调汇总,"
+ "当你觉得已有信息支持回答用户问题时候,停止调度,并输出最后的结果\n"
+ "#调度方法:\n"
+ "通过使用agent_orchestration技能调用其他智能体干活,使用技能时传入agent_name和需要做的活\n"
+ "#可调度智能体:\n"
+ f"{descriptions}"
+ )
+
+ agent_threads[main_agent_name] = await _create_thread(client, base, headers)
+ ok, result = await _create_agent(
+ client,
+ base,
+ headers,
+ name=main_agent_name,
+ description="你是总控控制协调智能体,负责协调其他智能体干活",
+ model=model,
+ skills=["agent_orchestration"],
+ soul=soul,
+ agent_id=main_agent_name,
+ )
+ if not ok and isinstance(result, str) and "exists" not in result:
+ raise HTTPException(status_code=500, detail=f"create coordinator agent failed: {result}")
+
+ # Optimization C: the sub-agent SOULs already declare "你只在被点到时
+ # 发言,由总控协调智能体派活", so the old intro broadcast (one human
+ # message per thread, 6 × GET-state + 6 × POST-state) is redundant.
+ # Skipping it removes the slowest stage of init entirely.
+
+ return {
+ "status": "success",
+ "main_agent_name": main_agent_name,
+ "thread_ids": agent_threads,
+ }
+
+
+# ---------------------------------------------------------------------------
+# Stream endpoint
+# ---------------------------------------------------------------------------
+
+
+async def _stream_upstream(
+ client: httpx.AsyncClient,
+ base: str,
+ headers: dict[str, str],
+ thread_id: str,
+ body: dict[str, Any],
+) -> AsyncIterator[tuple[str | None, list[str] | None]]:
+ """Yield (raw_line, None) for each SSE line, then ``(None, captured_data_lines)`` once."""
+
+ url = f"{base}/api/threads/{thread_id}/runs/stream"
+ captured: list[str] = []
+ async with client.stream("POST", url, json=body, headers=headers) as resp:
+ if resp.status_code != 200:
+ body_text = (await resp.aread()).decode("utf-8", "replace")[:1000]
+ raise HTTPException(
+ status_code=502,
+ detail=f"upstream {url} returned HTTP {resp.status_code}: {body_text}",
+ )
+ async for line in resp.aiter_lines():
+ if not line:
+ continue
+ yield line, None
+ if line.startswith("data:"):
+ captured.append(line)
+ yield None, captured
+
+
+def _parse_last_message(captured: list[str]) -> dict[str, Any]:
+ if len(captured) < 2:
+ raise HTTPException(
+ status_code=502,
+ detail=f"upstream produced no usable data lines (got {len(captured)})",
+ )
+ raw = captured[-2][5:].strip()
+ try:
+ parsed = json.loads(raw)
+ except json.JSONDecodeError as exc:
+ raise HTTPException(status_code=502, detail=f"failed to parse upstream frame: {exc}") from exc
+ messages = parsed.get("messages") or []
+ if not messages:
+ return {}
+ return messages[-1] or {}
+
+
+async def _leader_run(
+ client: httpx.AsyncClient,
+ base: str,
+ headers: dict[str, str],
+ agent_threads: dict[str, str],
+ agent_name: str,
+ new_message: str,
+ skill_stop_names: list[str],
+ model_name: str,
+) -> AsyncIterator[str | dict[str, Any]]:
+ await _broadcast(client, base, headers, "总控智能体收到用户消息:" + new_message, agent_threads, [agent_name])
+ thread_id = agent_threads[agent_name]
+ body = _run_payload(
+ agent_name=agent_name,
+ model_name=model_name,
+ thread_id=thread_id,
+ new_message=new_message,
+ excluded_tools=["web_search"],
+ skill_stop_names=skill_stop_names,
+ )
+
+ captured: list[str] = []
+ async for line, done in _stream_upstream(client, base, headers, thread_id, body):
+ if line is not None:
+ yield line
+ elif done is not None:
+ captured = done
+
+ last_msg = _parse_last_message(captured)
+ leader_text = _flatten_content(last_msg.get("content", ""))
+ tool_calls = last_msg.get("tool_calls") or []
+ if not tool_calls:
+ yield {"status": [], "content": leader_text, "agent_name": agent_name}
+ return
+
+ dispatched: list[list[str]] = []
+ for tool_call in tool_calls:
+ try:
+ sub_name, task_text = _extract_name_task(str(tool_call.get("args", "")))
+ if not sub_name:
+ continue
+ await _broadcast(
+ client,
+ base,
+ headers,
+ f"总控智能体给{sub_name}派活,内容为:" + task_text,
+ agent_threads,
+ [agent_name, sub_name],
+ )
+ dispatched.append([sub_name, task_text])
+ except Exception as exc:
+ logger.warning("dispatch failed: %s", exc)
+ yield {"status": dispatched, "content": leader_text, "agent_name": agent_name}
+
+
+async def _special_run(
+ client: httpx.AsyncClient,
+ base: str,
+ headers: dict[str, str],
+ agent_threads: dict[str, str],
+ agent_name: str,
+ new_message: str,
+ skill_stop_names: list[str],
+ model_name: str,
+) -> AsyncIterator[str | dict[str, Any]]:
+ thread_id = agent_threads[agent_name]
+ body = _run_payload(
+ agent_name=agent_name,
+ model_name=model_name,
+ thread_id=thread_id,
+ new_message=new_message,
+ excluded_tools=["web_search", "ask_clarification", "present_files", "view_image"],
+ skill_stop_names=skill_stop_names,
+ )
+
+ captured: list[str] = []
+ async for line, done in _stream_upstream(client, base, headers, thread_id, body):
+ if line is not None:
+ yield line
+ elif done is not None:
+ captured = done
+
+ last_msg = _parse_last_message(captured)
+ content = _flatten_content(last_msg.get("content", ""))
+ await _broadcast(
+ client,
+ base,
+ headers,
+ f"子智能体{agent_name}完成{new_message}工作,交付内容为:" + content,
+ agent_threads,
+ [agent_name],
+ )
+ yield {
+ "status": f"子智能体{agent_name}完成{new_message}工作",
+ "content": content,
+ "agent_name": agent_name,
+ }
+
+
+@router.post("/run/stream")
+async def run_multi_agent_stream(request: Request, payload: dict = Body(...)) -> StreamingResponse:
+ agent_type: str = str(payload.get("agent_type") or "leader").lower()
+ agent_threads: dict[str, str] = payload.get("d_agent_thread_id") or {}
+ agent_name: str = str(payload.get("agent_name") or "").lower()
+ new_message: str = str(payload.get("new_message") or "")
+ skill_stop_names: list[str] = payload.get("skill_stop_names") or []
+ model_name: str = str(payload.get("model") or "deepseek-chat")
+
+ if not agent_name or agent_name not in agent_threads:
+ raise HTTPException(
+ status_code=400,
+ detail=f"agent_name '{agent_name}' is missing from d_agent_thread_id",
+ )
+
+ base = _loopback_base(request)
+ headers = _auth_headers(request)
+
+ async def stream_generator() -> AsyncIterator[str]:
+ async with httpx.AsyncClient(timeout=None) as client:
+ try:
+ if agent_type == "leader":
+ gen = _leader_run(
+ client, base, headers, agent_threads, agent_name, new_message, skill_stop_names, model_name,
+ )
+ else:
+ gen = _special_run(
+ client, base, headers, agent_threads, agent_name, new_message, skill_stop_names, model_name,
+ )
+ async for chunk in gen:
+ if isinstance(chunk, dict):
+ yield f"data: {json.dumps(chunk, ensure_ascii=False)}\n\n"
+ else:
+ yield chunk if chunk.endswith("\n") else chunk + "\n"
+ except HTTPException as exc:
+ logger.warning("multi-agent stream HTTPException: %s %s", exc.status_code, exc.detail)
+ err_frame = {
+ "status": "error",
+ "content": f"[{exc.status_code}] {exc.detail}",
+ "agent_name": agent_name,
+ }
+ yield f"data: {json.dumps(err_frame, ensure_ascii=False)}\n\n"
+ except Exception as exc:
+ logger.exception("multi-agent stream unexpected failure")
+ err_frame = {
+ "status": "error",
+ "content": f"unexpected: {exc!r}",
+ "agent_name": agent_name,
+ }
+ yield f"data: {json.dumps(err_frame, ensure_ascii=False)}\n\n"
+
+ return StreamingResponse(
+ stream_generator(),
+ media_type="text/event-stream",
+ headers={
+ "Cache-Control": "no-cache",
+ "X-Accel-Buffering": "no",
+ "Connection": "keep-alive",
+ },
+ )
diff --git a/offline-backend-20260512/backend/app/gateway/routers/thread_runs.py b/offline-backend-20260512/backend/app/gateway/routers/thread_runs.py
index 0bb99f8b..38129fa5 100644
--- a/offline-backend-20260512/backend/app/gateway/routers/thread_runs.py
+++ b/offline-backend-20260512/backend/app/gateway/routers/thread_runs.py
@@ -54,6 +54,9 @@ class RunCreateRequest(BaseModel):
after_seconds: float | None = Field(default=None, description="Delayed execution")
if_not_exists: Literal["reject", "create"] = Field(default="create", description="Thread creation policy")
feedback_keys: list[str] | None = Field(default=None, description="LangSmith feedback keys")
+ excluded_tools: list[str] | None = Field(default=None, description="Tool names to exclude from the main agent (e.g. ['task', 'ask_clarification'])")
+ skill_stop_names: list[str] | None = Field(default=None, description="Skill names to stop execution when invoked (e.g. ['newsletter-generation'])")
+ skill_stop_message: str | None = Field(default=None, description="Custom message to display when skill stop is triggered")
class RunResponse(BaseModel):
diff --git a/offline-backend-20260512/backend/app/gateway/services.py b/offline-backend-20260512/backend/app/gateway/services.py
index b5a9b452..c48b8651 100644
--- a/offline-backend-20260512/backend/app/gateway/services.py
+++ b/offline-backend-20260512/backend/app/gateway/services.py
@@ -162,6 +162,9 @@ _CONTEXT_CONFIGURABLE_KEYS: frozenset[str] = frozenset(
"max_concurrent_subagents",
"agent_name",
"is_bootstrap",
+ "excluded_tools",
+ "skill_stop_names",
+ "skill_stop_message",
}
)
@@ -434,7 +437,15 @@ async def start_run(
# The ``context`` field is a custom extension for the langgraph-compat layer
# that carries agent configuration (model_name, thinking_enabled, etc.).
# Only agent-relevant keys are forwarded; unknown keys (e.g. thread_id) are ignored.
- merge_run_context_overrides(config, getattr(body, "context", None))
+ body_context = dict(getattr(body, "context", None) or {})
+ # Promote whitelisted top-level body fields into the context map so
+ # callers can pass them either nested under ``context`` or as flat
+ # top-level fields (``excluded_tools``, ``skill_stop_names``, …).
+ for top_level_key in ("excluded_tools", "skill_stop_names", "skill_stop_message"):
+ value = getattr(body, top_level_key, None)
+ if value is not None and top_level_key not in body_context:
+ body_context[top_level_key] = value
+ merge_run_context_overrides(config, body_context or None)
stream_modes = normalize_stream_modes(body.stream_mode)
diff --git a/offline-backend-20260512/backend/config.yaml b/offline-backend-20260512/backend/config.yaml
index fe958e7a..5cd1b1ce 100644
--- a/offline-backend-20260512/backend/config.yaml
+++ b/offline-backend-20260512/backend/config.yaml
@@ -132,8 +132,11 @@ agents_api:
enabled: true
database:
- backend: memory # 临时:MySQL 服务器挂了,先跑内存;恢复后改回 mysql
- mysql_url: $MYSQL_DATABASE_URL
+ # TODO: 临时切到 SQLite,等 192.168.0.123 MySQL 恢复后改回 backend: mysql
+ backend: sqlite
+ sqlite_dir: .deer-flow/data
+ # backend: mysql
+ # mysql_url: $MYSQL_DATABASE_URL
pool_size: 10
echo_sql: false
diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/agents/lead_agent/agent.py b/offline-backend-20260512/backend/packages/harness/deerflow/agents/lead_agent/agent.py
index 7f408e8b..d27dd73f 100644
--- a/offline-backend-20260512/backend/packages/harness/deerflow/agents/lead_agent/agent.py
+++ b/offline-backend-20260512/backend/packages/harness/deerflow/agents/lead_agent/agent.py
@@ -312,6 +312,37 @@ def _build_middlewares(
middlewares.append(SetupAgentApprovalMiddleware())
+ # SkillStopMiddleware — intercepts skill invocations and halts execution
+ # when a configured skill is about to run. Request-level options
+ # (skill_stop_names / skill_stop_message) take precedence; static
+ # config in ``app_config.skill_stop`` is the fallback.
+ skill_stop_names = cfg.get("skill_stop_names")
+ skill_stop_message = cfg.get("skill_stop_message")
+ if skill_stop_names:
+ from deerflow.agents.middlewares.skill_stop_middleware import SkillStopMiddleware
+
+ middlewares.append(
+ SkillStopMiddleware(
+ skill_names=skill_stop_names,
+ skills_root=resolved_app_config.skills.container_path or "/mnt/skills",
+ stop_message=skill_stop_message,
+ stop_on_match=True,
+ )
+ )
+ logger.info("SkillStopMiddleware enabled (from request): %s", skill_stop_names)
+ elif resolved_app_config.skill_stop.enabled and resolved_app_config.skill_stop.skill_names:
+ from deerflow.agents.middlewares.skill_stop_middleware import SkillStopMiddleware
+
+ middlewares.append(
+ SkillStopMiddleware(
+ skill_names=resolved_app_config.skill_stop.skill_names,
+ skills_root=resolved_app_config.skills.container_path or "/mnt/skills",
+ stop_message=resolved_app_config.skill_stop.stop_message,
+ stop_on_match=resolved_app_config.skill_stop.stop_on_match,
+ )
+ )
+ logger.info("SkillStopMiddleware enabled (from config): %s", resolved_app_config.skill_stop.skill_names)
+
# ClarificationMiddleware should always be last
middlewares.append(ClarificationMiddleware())
return middlewares
@@ -345,6 +376,7 @@ def _make_lead_agent(config: RunnableConfig, *, app_config: AppConfig):
if not isinstance(writing_artifact_path, str) or not writing_artifact_path.strip():
writing_artifact_path = None
agent_id = validate_agent_id(cfg.get("agent_id") or cfg.get("agent_name"))
+ requested_excluded_tools = cfg.get("excluded_tools") or None
try:
agent_config = load_agent_config(agent_id) if not is_bootstrap else None
@@ -415,6 +447,7 @@ def _make_lead_agent(config: RunnableConfig, *, app_config: AppConfig):
model_name=model_name,
subagent_enabled=subagent_enabled,
is_scheduled_run=cfg.get("is_scheduled_run", False),
+ excluded_tools=requested_excluded_tools,
app_config=resolved_app_config,
)
+ [setup_agent],
@@ -440,6 +473,7 @@ def _make_lead_agent(config: RunnableConfig, *, app_config: AppConfig):
groups=agent_config.tool_groups if agent_config else None,
subagent_enabled=subagent_enabled,
is_scheduled_run=cfg.get("is_scheduled_run", False),
+ excluded_tools=requested_excluded_tools,
app_config=resolved_app_config,
),
middleware=_build_middlewares(config, model_name=model_name, agent_name=agent_id, app_config=resolved_app_config),
diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/agents/middlewares/skill_stop_middleware.py b/offline-backend-20260512/backend/packages/harness/deerflow/agents/middlewares/skill_stop_middleware.py
new file mode 100644
index 00000000..f0950fd0
--- /dev/null
+++ b/offline-backend-20260512/backend/packages/harness/deerflow/agents/middlewares/skill_stop_middleware.py
@@ -0,0 +1,144 @@
+"""Middleware for intercepting skill calls and stopping execution when specific skills are invoked."""
+
+import logging
+import re
+from collections.abc import Callable
+from typing import Any, override
+
+from langchain.agents import AgentState
+from langchain.agents.middleware import AgentMiddleware
+from langchain_core.messages import ToolMessage
+from langgraph.prebuilt.tool_node import ToolCallRequest
+from langgraph.types import Command, interrupt
+
+logger = logging.getLogger(__name__)
+
+
+def extract_name_task(text: str) -> tuple[str, str]:
+ """Extract `agent_name` and `task_content` from a bash invocation that triggers
+ a sub-agent via ``python3 scripts/agent_orchestration.py "
" ""``.
+
+ Returns empty strings when no match is found so the caller can treat the
+ absence of a match as "not a target skill call".
+ """
+ try:
+ pattern = r'\.py\s+"([^"]*)"\s+"([^"]*)"'
+ match = re.search(pattern, text)
+ if match:
+ return match.group(1), match.group(2)
+ return "", ""
+ except Exception:
+ return "", ""
+
+
+class SkillStopMiddlewareState(AgentState):
+ """Compatible with the `ThreadState` schema."""
+
+ pass
+
+
+class SkillStopMiddleware(AgentMiddleware[SkillStopMiddlewareState]):
+ """Intercepts skill file reads and stops execution when specific skills are invoked.
+
+ When the model reads a file from a skill directory that matches one of the
+ configured skill names, this middleware:
+ 1. Intercepts the tool call before execution
+ 2. Returns a Command that stops execution (goes to END)
+ 3. Optionally returns a message indicating which skill was triggered
+
+ This is useful for implementing "skill triggers" that pause the agent
+ when certain skills are about to be used.
+ """
+
+ state_schema = SkillStopMiddlewareState
+
+ def __init__(
+ self,
+ *,
+ skill_names: list[str],
+ skills_root: str = "/mnt/skills",
+ stop_message: str | None = None,
+ stop_on_match: bool = True,
+ ):
+ """Initialize the skill stop middleware.
+
+ Args:
+ skill_names: List of skill names to intercept (e.g., ["newsletter-generation", "podcast-generation"]).
+ skills_root: Root directory where skills are located (default: "/mnt/skills").
+ stop_message: Optional message to return when stopping. If None, a default message is used.
+ stop_on_match: If True, stop when skill matches. If False, continue execution (for logging only).
+ """
+ super().__init__()
+ self._skill_names = set(skill_names)
+ self._skills_root = skills_root.rstrip("/")
+ self._stop_message = stop_message
+ self._stop_on_match = stop_on_match
+
+ def _is_target_skill_call(self, tool_call: dict[str, Any]) -> str | None:
+ """Return the matched skill name if this tool call invokes one of the
+ configured target skills, otherwise ``None``.
+
+ Matching today is bash-based: the lead agent reaches into a skill by
+ running ``python3 scripts/agent_orchestration.py "" ""``,
+ so we only look at ``bash`` calls and extract the skill name from the
+ argv-style quoted form.
+ """
+ name = tool_call.get("name") or ""
+ skill_name = ""
+ if name == "bash" and "args" in tool_call:
+ skill_name, _ = extract_name_task(str(tool_call["args"]))
+ if not skill_name:
+ return None
+ lowered_targets = {s.lower() for s in self._skill_names}
+ if skill_name in self._skill_names or skill_name.lower() in lowered_targets:
+ logger.info("SkillStopMiddleware: matched skill_name=%s", skill_name)
+ return skill_name
+ return None
+
+ def _handle_skill_stop(self, request: ToolCallRequest, skill_name: str) -> Command:
+ """Build the stop response — an interrupt that includes a ToolMessage
+ describing which skill was triggered."""
+ logger.info("Skill stop triggered: skill_name=%s", skill_name)
+
+ tool_call_id = request.tool_call.get("id", "")
+ message_content = self._stop_message or f"⏹️ Skill '{skill_name}' triggered. Execution stopped."
+
+ tool_message = ToolMessage(
+ id=f"skill_stop:{tool_call_id}",
+ content=message_content,
+ tool_call_id=tool_call_id,
+ name="skill_stop",
+ )
+
+ return interrupt(
+ {
+ "messages": [tool_message],
+ "skill_name": skill_name,
+ }
+ )
+
+ @override
+ def wrap_tool_call(
+ self,
+ request: ToolCallRequest,
+ handler: Callable[[ToolCallRequest], ToolMessage | Command],
+ ) -> ToolMessage | Command:
+ matched_skill = self._is_target_skill_call(request.tool_call)
+ if matched_skill:
+ if self._stop_on_match:
+ return self._handle_skill_stop(request, matched_skill)
+ logger.info("Skill detected (not stopping): skill_name=%s", matched_skill)
+ return handler(request)
+
+ @override
+ async def awrap_tool_call(
+ self,
+ request: ToolCallRequest,
+ handler: Callable[[ToolCallRequest], ToolMessage | Command],
+ ) -> ToolMessage | Command:
+ matched_skill = self._is_target_skill_call(request.tool_call)
+ if matched_skill:
+ if self._stop_on_match:
+ return self._handle_skill_stop(request, matched_skill)
+ logger.info("Skill detected (not stopping): skill_name=%s", matched_skill)
+ return await handler(request)
diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/config/app_config.py b/offline-backend-20260512/backend/packages/harness/deerflow/config/app_config.py
index fce0f936..2d56d443 100644
--- a/offline-backend-20260512/backend/packages/harness/deerflow/config/app_config.py
+++ b/offline-backend-20260512/backend/packages/harness/deerflow/config/app_config.py
@@ -22,6 +22,7 @@ from deerflow.config.sandbox_config import SandboxConfig
from deerflow.config.skill_evolution_config import SkillEvolutionConfig
from deerflow.config.skill_evolution_runtime import get_overrides_mtime as _get_skill_evolution_overrides_mtime
from deerflow.config.skill_evolution_runtime import load_overrides as _load_skill_evolution_overrides
+from deerflow.config.skill_stop_config import SkillStopConfig
from deerflow.config.skills_config import SkillsConfig
from deerflow.config.stream_bridge_config import StreamBridgeConfig, load_stream_bridge_config_from_dict
from deerflow.config.subagents_config import SubagentsAppConfig, load_subagents_config_from_dict
@@ -117,6 +118,8 @@ class AppConfig(BaseModel):
subagents: SubagentsAppConfig = Field(default_factory=SubagentsAppConfig, description="Subagent runtime configuration")
guardrails: GuardrailsConfig = Field(default_factory=GuardrailsConfig, description="Guardrail middleware configuration")
circuit_breaker: CircuitBreakerConfig = Field(default_factory=CircuitBreakerConfig, description="LLM circuit breaker configuration")
+ excluded_tools: list[str] = Field(default_factory=list, description="Tool names to exclude from the main agent (e.g. ['task', 'ask_clarification'])")
+ skill_stop: SkillStopConfig = Field(default_factory=SkillStopConfig, description="Skill stop middleware configuration")
model_config = ConfigDict(extra="allow")
database: DatabaseConfig = Field(default_factory=DatabaseConfig, description="Unified database backend configuration")
run_events: RunEventsConfig = Field(default_factory=RunEventsConfig, description="Run event storage configuration")
diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/config/skill_stop_config.py b/offline-backend-20260512/backend/packages/harness/deerflow/config/skill_stop_config.py
new file mode 100644
index 00000000..a3b29262
--- /dev/null
+++ b/offline-backend-20260512/backend/packages/harness/deerflow/config/skill_stop_config.py
@@ -0,0 +1,31 @@
+"""Configuration for skill stop middleware."""
+
+from pydantic import BaseModel, Field
+
+
+class SkillStopConfig(BaseModel):
+ """Configuration for the skill stop middleware.
+
+ When enabled, the middleware will intercept skill invocations and stop
+ execution when a matching skill is about to be used.
+ """
+
+ enabled: bool = Field(
+ default=False,
+ description="Enable skill stop middleware. When True, execution will stop when specified skills are invoked.",
+ )
+
+ skill_names: list[str] = Field(
+ default_factory=list,
+ description="List of skill names to intercept. When the model reads a file from these skill directories, execution will stop.",
+ )
+
+ stop_message: str | None = Field(
+ default=None,
+ description="Custom message to display when stopping. If None, a default message is used.",
+ )
+
+ stop_on_match: bool = Field(
+ default=True,
+ description="If True, stop execution when skill matches. If False, only log the match without stopping.",
+ )
diff --git a/offline-backend-20260512/backend/packages/harness/deerflow/tools/tools.py b/offline-backend-20260512/backend/packages/harness/deerflow/tools/tools.py
index bd5673c3..9ad2c249 100644
--- a/offline-backend-20260512/backend/packages/harness/deerflow/tools/tools.py
+++ b/offline-backend-20260512/backend/packages/harness/deerflow/tools/tools.py
@@ -39,6 +39,7 @@ def get_available_tools(
model_name: str | None = None,
subagent_enabled: bool = False,
is_scheduled_run: bool = False,
+ excluded_tools: list[str] | None = None,
*,
app_config: AppConfig | None = None,
) -> list[BaseTool]:
@@ -52,6 +53,9 @@ def get_available_tools(
include_mcp: Whether to include tools from MCP servers (default: True).
model_name: Optional model name to determine if vision tools should be included.
subagent_enabled: Whether to include subagent tools (task, task_status).
+ excluded_tools: Optional list of tool names to exclude from the result
+ (applied AFTER all other resolution). Takes precedence over
+ ``app_config.excluded_tools``.
Returns:
List of available tools.
@@ -209,4 +213,19 @@ def get_available_tools(
"Duplicate tool name %r detected and skipped — check your config.yaml and MCP server registrations (issue #1803).",
t.name,
)
+
+ # Drop tools the caller (or config) wants excluded. Request-level
+ # ``excluded_tools`` wins; the config-level list is the static fallback.
+ effective_excluded = excluded_tools if excluded_tools else getattr(config, "excluded_tools", None) or []
+ if effective_excluded:
+ excluded_set = set(effective_excluded)
+ filtered_tools = [t for t in unique_tools if t.name not in excluded_set]
+ if len(filtered_tools) < len(unique_tools):
+ logger.info(
+ "Excluded %d tool(s) by request/config: %s",
+ len(unique_tools) - len(filtered_tools),
+ effective_excluded,
+ )
+ unique_tools = filtered_tools
+
return unique_tools
diff --git a/situation-overview-web/public/Config.js b/situation-overview-web/public/Config.js
new file mode 100644
index 00000000..365fb888
--- /dev/null
+++ b/situation-overview-web/public/Config.js
@@ -0,0 +1,10 @@
+window.environment = {
+ b1ConsumerUrl: "http://127.0.0.1:8001/consumer", // http://10.106.71.194:8013/
+ // b1ConsumerUrl: "http://localhost:8004/consumer", // http://10.106.71.194:8013/
+
+ aImagePath: "https://present.a.sit.4.cn/gene", // https://present.a.uat.4.cn present.a.sit.4.cn
+ g6ImgUrl: "/src/assets",
+ mapUrl: "https://present.a.sit.4.cn/tiles/{z}/{x}/{y}.jpg",
+ b1NodeServerUrl: "https://ch1.b.uat.4.cn",
+ b5ReplyUrl:"https://pg.b.uat.4.cn"
+};
diff --git a/situation-overview-web/public/moment/locale/ar.js b/situation-overview-web/public/moment/locale/ar.js
new file mode 100644
index 00000000..1549cc15
--- /dev/null
+++ b/situation-overview-web/public/moment/locale/ar.js
@@ -0,0 +1,134 @@
+//! moment.js locale configuration
+
+;(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined'
+ && typeof require === 'function' ? factory(require('../moment')) :
+ typeof define === 'function' && define.amd ? define(['../moment'], factory) :
+ factory(global.moment)
+}(this, (function (moment) { 'use strict';
+
+
+ var symbolMap = {
+ '1': '١',
+ '2': '٢',
+ '3': '٣',
+ '4': '٤',
+ '5': '٥',
+ '6': '٦',
+ '7': '٧',
+ '8': '٨',
+ '9': '٩',
+ '0': '٠'
+ }, numberMap = {
+ '١': '1',
+ '٢': '2',
+ '٣': '3',
+ '٤': '4',
+ '٥': '5',
+ '٦': '6',
+ '٧': '7',
+ '٨': '8',
+ '٩': '9',
+ '٠': '0'
+ }, pluralForm = function (n) {
+ return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5;
+ }, plurals = {
+ s : ['أقل من ثانية', 'ثانية واحدة', ['ثانيتان', 'ثانيتين'], '%d ثوان', '%d ثانية', '%d ثانية'],
+ m : ['أقل من دقيقة', 'دقيقة واحدة', ['دقيقتان', 'دقيقتين'], '%d دقائق', '%d دقيقة', '%d دقيقة'],
+ h : ['أقل من ساعة', 'ساعة واحدة', ['ساعتان', 'ساعتين'], '%d ساعات', '%d ساعة', '%d ساعة'],
+ d : ['أقل من يوم', 'يوم واحد', ['يومان', 'يومين'], '%d أيام', '%d يومًا', '%d يوم'],
+ M : ['أقل من شهر', 'شهر واحد', ['شهران', 'شهرين'], '%d أشهر', '%d شهرا', '%d شهر'],
+ y : ['أقل من عام', 'عام واحد', ['عامان', 'عامين'], '%d أعوام', '%d عامًا', '%d عام']
+ }, pluralize = function (u) {
+ return function (number, withoutSuffix, string, isFuture) {
+ var f = pluralForm(number),
+ str = plurals[u][pluralForm(number)];
+ if (f === 2) {
+ str = str[withoutSuffix ? 0 : 1];
+ }
+ return str.replace(/%d/i, number);
+ };
+ }, months = [
+ 'يناير',
+ 'فبراير',
+ 'مارس',
+ 'أبريل',
+ 'مايو',
+ 'يونيو',
+ 'يوليو',
+ 'أغسطس',
+ 'سبتمبر',
+ 'أكتوبر',
+ 'نوفمبر',
+ 'ديسمبر'
+ ];
+
+ var ar = moment.defineLocale('ar', {
+ months : months,
+ monthsShort : months,
+ weekdays : 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
+ weekdaysShort : 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
+ weekdaysMin : 'ح_ن_ث_ر_خ_ج_س'.split('_'),
+ weekdaysParseExact : true,
+ longDateFormat : {
+ LT : 'HH:mm',
+ LTS : 'HH:mm:ss',
+ L : 'D/\u200FM/\u200FYYYY',
+ LL : 'D MMMM YYYY',
+ LLL : 'D MMMM YYYY HH:mm',
+ LLLL : 'dddd D MMMM YYYY HH:mm'
+ },
+ meridiemParse: /ص|م/,
+ isPM : function (input) {
+ return 'م' === input;
+ },
+ meridiem : function (hour, minute, isLower) {
+ if (hour < 12) {
+ return 'ص';
+ } else {
+ return 'م';
+ }
+ },
+ calendar : {
+ sameDay: '[اليوم عند الساعة] LT',
+ nextDay: '[غدًا عند الساعة] LT',
+ nextWeek: 'dddd [عند الساعة] LT',
+ lastDay: '[أمس عند الساعة] LT',
+ lastWeek: 'dddd [عند الساعة] LT',
+ sameElse: 'L'
+ },
+ relativeTime : {
+ future : 'بعد %s',
+ past : 'منذ %s',
+ s : pluralize('s'),
+ ss : pluralize('s'),
+ m : pluralize('m'),
+ mm : pluralize('m'),
+ h : pluralize('h'),
+ hh : pluralize('h'),
+ d : pluralize('d'),
+ dd : pluralize('d'),
+ M : pluralize('M'),
+ MM : pluralize('M'),
+ y : pluralize('y'),
+ yy : pluralize('y')
+ },
+ preparse: function (string) {
+ return string.replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) {
+ return numberMap[match];
+ }).replace(/،/g, ',');
+ },
+ postformat: function (string) {
+ return string.replace(/\d/g, function (match) {
+ return symbolMap[match];
+ }).replace(/,/g, '،');
+ },
+ week : {
+ dow : 6, // Saturday is the first day of the week.
+ doy : 12 // The week that contains Jan 12th is the first week of the year.
+ }
+ });
+
+ return ar;
+
+})));
diff --git a/situation-overview-web/public/moment/locale/sq.js b/situation-overview-web/public/moment/locale/sq.js
new file mode 100644
index 00000000..eb081df0
--- /dev/null
+++ b/situation-overview-web/public/moment/locale/sq.js
@@ -0,0 +1,67 @@
+//! moment.js locale configuration
+
+;(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined'
+ && typeof require === 'function' ? factory(require('../moment')) :
+ typeof define === 'function' && define.amd ? define(['../moment'], factory) :
+ factory(global.moment)
+}(this, (function (moment) { 'use strict';
+
+
+ var sq = moment.defineLocale('sq', {
+ months : 'Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_Nëntor_Dhjetor'.split('_'),
+ monthsShort : 'Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_Nën_Dhj'.split('_'),
+ weekdays : 'E Diel_E Hënë_E Martë_E Mërkurë_E Enjte_E Premte_E Shtunë'.split('_'),
+ weekdaysShort : 'Die_Hën_Mar_Mër_Enj_Pre_Sht'.split('_'),
+ weekdaysMin : 'D_H_Ma_Më_E_P_Sh'.split('_'),
+ weekdaysParseExact : true,
+ meridiemParse: /PD|MD/,
+ isPM: function (input) {
+ return input.charAt(0) === 'M';
+ },
+ meridiem : function (hours, minutes, isLower) {
+ return hours < 12 ? 'PD' : 'MD';
+ },
+ longDateFormat : {
+ LT : 'HH:mm',
+ LTS : 'HH:mm:ss',
+ L : 'DD/MM/YYYY',
+ LL : 'D MMMM YYYY',
+ LLL : 'D MMMM YYYY HH:mm',
+ LLLL : 'dddd, D MMMM YYYY HH:mm'
+ },
+ calendar : {
+ sameDay : '[Sot në] LT',
+ nextDay : '[Nesër në] LT',
+ nextWeek : 'dddd [në] LT',
+ lastDay : '[Dje në] LT',
+ lastWeek : 'dddd [e kaluar në] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : 'në %s',
+ past : '%s më parë',
+ s : 'disa sekonda',
+ ss : '%d sekonda',
+ m : 'një minutë',
+ mm : '%d minuta',
+ h : 'një orë',
+ hh : '%d orë',
+ d : 'një ditë',
+ dd : '%d ditë',
+ M : 'një muaj',
+ MM : '%d muaj',
+ y : 'një vit',
+ yy : '%d vite'
+ },
+ dayOfMonthOrdinalParse: /\d{1,2}\./,
+ ordinal : '%d.',
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 4 // The week that contains Jan 4th is the first week of the year.
+ }
+ });
+
+ return sq;
+
+})));
diff --git a/situation-overview-web/public/moment/locale/tr.js b/situation-overview-web/public/moment/locale/tr.js
new file mode 100644
index 00000000..e3fa275a
--- /dev/null
+++ b/situation-overview-web/public/moment/locale/tr.js
@@ -0,0 +1,93 @@
+
+;(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined'
+ && typeof require === 'function' ? factory(require('../moment')) :
+ typeof define === 'function' && define.amd ? define(['../moment'], factory) :
+ factory(global.moment)
+}(this, (function (moment) { 'use strict';
+
+ var suffixes = {
+ 1: '\'inci',
+ 5: '\'inci',
+ 8: '\'inci',
+ 70: '\'inci',
+ 80: '\'inci',
+ 2: '\'nci',
+ 7: '\'nci',
+ 20: '\'nci',
+ 50: '\'nci',
+ 3: '\'üncü',
+ 4: '\'üncü',
+ 100: '\'üncü',
+ 6: '\'ncı',
+ 9: '\'uncu',
+ 10: '\'uncu',
+ 30: '\'uncu',
+ 60: '\'ıncı',
+ 90: '\'ıncı'
+ };
+
+ var tr = moment.defineLocale('tr', {
+ months : 'Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık'.split('_'),
+ monthsShort : 'Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara'.split('_'),
+ weekdays : 'Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi'.split('_'),
+ weekdaysShort : 'Paz_Pts_Sal_Çar_Per_Cum_Cts'.split('_'),
+ weekdaysMin : 'Pz_Pt_Sa_Ça_Pe_Cu_Ct'.split('_'),
+ longDateFormat : {
+ LT : 'HH:mm',
+ LTS : 'HH:mm:ss',
+ L : 'DD.MM.YYYY',
+ LL : 'D MMMM YYYY',
+ LLL : 'D MMMM YYYY HH:mm',
+ LLLL : 'dddd, D MMMM YYYY HH:mm'
+ },
+ calendar : {
+ sameDay : '[bugün saat] LT',
+ nextDay : '[yarın saat] LT',
+ nextWeek : '[gelecek] dddd [saat] LT',
+ lastDay : '[dün] LT',
+ lastWeek : '[geçen] dddd [saat] LT',
+ sameElse : 'L'
+ },
+ relativeTime : {
+ future : '%s sonra',
+ past : '%s önce',
+ s : 'birkaç saniye',
+ ss : '%d saniye',
+ m : 'bir dakika',
+ mm : '%d dakika',
+ h : 'bir saat',
+ hh : '%d saat',
+ d : 'bir gün',
+ dd : '%d gün',
+ M : 'bir ay',
+ MM : '%d ay',
+ y : 'bir yıl',
+ yy : '%d yıl'
+ },
+ ordinal: function (number, period) {
+ switch (period) {
+ case 'd':
+ case 'D':
+ case 'Do':
+ case 'DD':
+ return number;
+ default:
+ if (number === 0) { // special case for zero
+ return number + '\'ıncı';
+ }
+ var a = number % 10,
+ b = number % 100 - a,
+ c = number >= 100 ? 100 : null;
+ return number + (suffixes[a] || suffixes[b] || suffixes[c]);
+ }
+ },
+ week : {
+ dow : 1, // Monday is the first day of the week.
+ doy : 7 // The week that contains Jan 7th is the first week of the year.
+ }
+ });
+
+ return tr;
+
+})));
diff --git a/situation-overview-web/public/moment/moment-with-locales.min.js b/situation-overview-web/public/moment/moment-with-locales.min.js
new file mode 100644
index 00000000..d81e02ca
--- /dev/null
+++ b/situation-overview-web/public/moment/moment-with-locales.min.js
@@ -0,0 +1 @@
+!function(e,a){"object"==typeof exports&&"undefined"!=typeof module?module.exports=a():"function"==typeof define&&define.amd?define(a):e.moment=a()}(this,function(){"use strict";var e,n;function l(){return e.apply(null,arguments)}function _(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function i(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function o(e){return void 0===e}function m(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function u(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function M(e,a){var t,s=[];for(t=0;t>>0,s=0;sTe(e)?(d=e+1,_-Te(e)):(d=e,_),{year:d,dayOfYear:r}}function Ce(e,a,t){var s,n,d=Ne(e.year(),a,t),r=Math.floor((e.dayOfYear()-d-1)/7)+1;return r<1?s=r+Ie(n=e.year()-1,a,t):r>Ie(e.year(),a,t)?(s=r-Ie(e.year(),a,t),n=e.year()+1):(n=e.year(),s=r),{week:s,year:n}}function Ie(e,a,t){var s=Ne(e,a,t),n=Ne(e+1,a,t);return(Te(e)-s+n)/7}C("w",["ww",2],"wo","week"),C("W",["WW",2],"Wo","isoWeek"),O("week","w"),O("isoWeek","W"),E("week",5),E("isoWeek",5),ie("w",B),ie("ww",B,V),ie("W",B),ie("WW",B,V),Me(["w","ww","W","WW"],function(e,a,t,s){a[s.substr(0,1)]=g(e)});function Ue(e,a){return e.slice(a,7).concat(e.slice(0,a))}C("d",0,"do","day"),C("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),C("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),C("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),C("e",0,0,"weekday"),C("E",0,0,"isoWeekday"),O("day","d"),O("weekday","e"),O("isoWeekday","E"),E("day",11),E("weekday",11),E("isoWeekday",11),ie("d",B),ie("e",B),ie("E",B),ie("dd",function(e,a){return a.weekdaysMinRegex(e)}),ie("ddd",function(e,a){return a.weekdaysShortRegex(e)}),ie("dddd",function(e,a){return a.weekdaysRegex(e)}),Me(["dd","ddd","dddd"],function(e,a,t,s){var n=t._locale.weekdaysParse(e,s,t._strict);null!=n?a.d=n:Y(t).invalidWeekday=e}),Me(["d","e","E"],function(e,a,t,s){a[s]=g(e)});var Ge="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var Ve="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var Ke="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var Ze=re;var $e=re;var Be=re;function qe(){function e(e,a){return a.length-e.length}var a,t,s,n,d,r=[],_=[],i=[],o=[];for(a=0;a<7;a++)t=c([2e3,1]).day(a),s=this.weekdaysMin(t,""),n=this.weekdaysShort(t,""),d=this.weekdays(t,""),r.push(s),_.push(n),i.push(d),o.push(s),o.push(n),o.push(d);for(r.sort(e),_.sort(e),i.sort(e),o.sort(e),a=0;a<7;a++)_[a]=me(_[a]),i[a]=me(i[a]),o[a]=me(o[a]);this._weekdaysRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+_.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+r.join("|")+")","i")}function Qe(){return this.hours()%12||12}function Xe(e,a){C(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),a)})}function ea(e,a){return a._meridiemParse}C("H",["HH",2],0,"hour"),C("h",["hh",2],0,Qe),C("k",["kk",2],0,function(){return this.hours()||24}),C("hmm",0,0,function(){return""+Qe.apply(this)+F(this.minutes(),2)}),C("hmmss",0,0,function(){return""+Qe.apply(this)+F(this.minutes(),2)+F(this.seconds(),2)}),C("Hmm",0,0,function(){return""+this.hours()+F(this.minutes(),2)}),C("Hmmss",0,0,function(){return""+this.hours()+F(this.minutes(),2)+F(this.seconds(),2)}),Xe("a",!0),Xe("A",!1),O("hour","h"),E("hour",13),ie("a",ea),ie("A",ea),ie("H",B),ie("h",B),ie("k",B),ie("HH",B,V),ie("hh",B,V),ie("kk",B,V),ie("hmm",q),ie("hmmss",Q),ie("Hmm",q),ie("Hmmss",Q),le(["H","HH"],Ye),le(["k","kk"],function(e,a,t){var s=g(e);a[Ye]=24===s?0:s}),le(["a","A"],function(e,a,t){t._isPm=t._locale.isPM(e),t._meridiem=e}),le(["h","hh"],function(e,a,t){a[Ye]=g(e),Y(t).bigHour=!0}),le("hmm",function(e,a,t){var s=e.length-2;a[Ye]=g(e.substr(0,s)),a[ye]=g(e.substr(s)),Y(t).bigHour=!0}),le("hmmss",function(e,a,t){var s=e.length-4,n=e.length-2;a[Ye]=g(e.substr(0,s)),a[ye]=g(e.substr(s,2)),a[fe]=g(e.substr(n)),Y(t).bigHour=!0}),le("Hmm",function(e,a,t){var s=e.length-2;a[Ye]=g(e.substr(0,s)),a[ye]=g(e.substr(s))}),le("Hmmss",function(e,a,t){var s=e.length-4,n=e.length-2;a[Ye]=g(e.substr(0,s)),a[ye]=g(e.substr(s,2)),a[fe]=g(e.substr(n))});var aa,ta=Se("Hours",!0),sa={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Oe,monthsShort:Pe,week:{dow:0,doy:6},weekdays:Ge,weekdaysMin:Ke,weekdaysShort:Ve,meridiemParse:/[ap]\.?m?\.?/i},na={},da={};function ra(e){return e?e.toLowerCase().replace("_","-"):e}function _a(e){var a=null;if(!na[e]&&"undefined"!=typeof module&&module&&module.exports)try{a=aa._abbr,require("./locale/"+e),ia(a)}catch(e){}return na[e]}function ia(e,a){var t;return e&&((t=o(a)?ma(e):oa(e,a))?aa=t:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),aa._abbr}function oa(e,a){if(null===a)return delete na[e],null;var t,s=sa;if(a.abbr=e,null!=na[e])S("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=na[e]._config;else if(null!=a.parentLocale)if(null!=na[a.parentLocale])s=na[a.parentLocale]._config;else{if(null==(t=_a(a.parentLocale)))return da[a.parentLocale]||(da[a.parentLocale]=[]),da[a.parentLocale].push({name:e,config:a}),null;s=t._config}return na[e]=new j(b(s,a)),da[e]&&da[e].forEach(function(e){oa(e.name,e.config)}),ia(e),na[e]}function ma(e){var a;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return aa;if(!_(e)){if(a=_a(e))return a;e=[e]}return function(e){for(var a,t,s,n,d=0;d=a&&r(n,t,!0)>=a-1)break;a--}d++}return aa}(e)}function ua(e){var a,t=e._a;return t&&-2===Y(e).overflow&&(a=t[Le]<0||11je(t[he],t[Le])?ce:t[Ye]<0||24Ie(t,d,r)?Y(e)._overflowWeeks=!0:null!=i?Y(e)._overflowWeekday=!0:(_=Re(t,s,n,d,r),e._a[he]=_.year,e._dayOfYear=_.dayOfYear)}(e),null!=e._dayOfYear&&(d=la(e._a[he],s[he]),(e._dayOfYear>Te(d)||0===e._dayOfYear)&&(Y(e)._overflowDayOfYear=!0),t=Je(d,0,e._dayOfYear),e._a[Le]=t.getUTCMonth(),e._a[ce]=t.getUTCDate()),a=0;a<3&&null==e._a[a];++a)e._a[a]=r[a]=s[a];for(;a<7;a++)e._a[a]=r[a]=null==e._a[a]?2===a?1:0:e._a[a];24===e._a[Ye]&&0===e._a[ye]&&0===e._a[fe]&&0===e._a[ke]&&(e._nextDay=!0,e._a[Ye]=0),e._d=(e._useUTC?Je:function(e,a,t,s,n,d,r){var _;return e<100&&0<=e?(_=new Date(e+400,a,t,s,n,d,r),isFinite(_.getFullYear())&&_.setFullYear(e)):_=new Date(e,a,t,s,n,d,r),_}).apply(null,r),n=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[Ye]=24),e._w&&void 0!==e._w.d&&e._w.d!==n&&(Y(e).weekdayMismatch=!0)}}var ha=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,La=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ca=/Z|[+-]\d\d(?::?\d\d)?/,Ya=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],ya=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],fa=/^\/?Date\((\-?\d+)/i;function ka(e){var a,t,s,n,d,r,_=e._i,i=ha.exec(_)||La.exec(_);if(i){for(Y(e).iso=!0,a=0,t=Ya.length;at.valueOf():t.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},Mt.isLocal=function(){return!!this.isValid()&&!this._isUTC},Mt.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},Mt.isUtc=Ra,Mt.isUTC=Ra,Mt.zoneAbbr=function(){return this._isUTC?"UTC":""},Mt.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},Mt.dates=t("dates accessor is deprecated. Use date instead.",_t),Mt.months=t("months accessor is deprecated. Use month instead",Ae),Mt.years=t("years accessor is deprecated. Use year instead",ve),Mt.zone=t("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,a){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,a),this):-this.utcOffset()}),Mt.isDSTShifted=t("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var e={};if(k(e,this),(e=va(e))._a){var a=e._isUTC?c(e._a):Ha(e._a);this._isDSTShifted=this.isValid()&&0>>0,s=0;sSe(e)?(r=e+1,o-Se(e)):(r=e,o),{year:r,dayOfYear:a}}function Ie(e,t,n){var s,i,r=Ve(e.year(),t,n),a=Math.floor((e.dayOfYear()-r-1)/7)+1;return a<1?s=a+Ae(i=e.year()-1,t,n):a>Ae(e.year(),t,n)?(s=a-Ae(e.year(),t,n),i=e.year()+1):(i=e.year(),s=a),{week:s,year:i}}function Ae(e,t,n){var s=Ve(e,t,n),i=Ve(e+1,t,n);return(Se(e)-s+i)/7}I("w",["ww",2],"wo","week"),I("W",["WW",2],"Wo","isoWeek"),C("week","w"),C("isoWeek","W"),F("week",5),F("isoWeek",5),ue("w",B),ue("ww",B,z),ue("W",B),ue("WW",B,z),fe(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=D(e)});function je(e,t){return e.slice(t,7).concat(e.slice(0,t))}I("d",0,"do","day"),I("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),I("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),I("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),I("e",0,0,"weekday"),I("E",0,0,"isoWeekday"),C("day","d"),C("weekday","e"),C("isoWeekday","E"),F("day",11),F("weekday",11),F("isoWeekday",11),ue("d",B),ue("e",B),ue("E",B),ue("dd",function(e,t){return t.weekdaysMinRegex(e)}),ue("ddd",function(e,t){return t.weekdaysShortRegex(e)}),ue("dddd",function(e,t){return t.weekdaysRegex(e)}),fe(["dd","ddd","dddd"],function(e,t,n,s){var i=n._locale.weekdaysParse(e,s,n._strict);null!=i?t.d=i:g(n).invalidWeekday=e}),fe(["d","e","E"],function(e,t,n,s){t[s]=D(e)});var Ze="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var ze="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var $e="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var qe=ae;var Je=ae;var Be=ae;function Qe(){function e(e,t){return t.length-e.length}var t,n,s,i,r,a=[],o=[],u=[],l=[];for(t=0;t<7;t++)n=y([2e3,1]).day(t),s=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),r=this.weekdays(n,""),a.push(s),o.push(i),u.push(r),l.push(s),l.push(i),l.push(r);for(a.sort(e),o.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)o[t]=he(o[t]),u[t]=he(u[t]),l[t]=he(l[t]);this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Xe(){return this.hours()%12||12}function Ke(e,t){I(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function et(e,t){return t._meridiemParse}I("H",["HH",2],0,"hour"),I("h",["hh",2],0,Xe),I("k",["kk",2],0,function(){return this.hours()||24}),I("hmm",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)}),I("hmmss",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)+L(this.seconds(),2)}),I("Hmm",0,0,function(){return""+this.hours()+L(this.minutes(),2)}),I("Hmmss",0,0,function(){return""+this.hours()+L(this.minutes(),2)+L(this.seconds(),2)}),Ke("a",!0),Ke("A",!1),C("hour","h"),F("hour",13),ue("a",et),ue("A",et),ue("H",B),ue("h",B),ue("k",B),ue("HH",B,z),ue("hh",B,z),ue("kk",B,z),ue("hmm",Q),ue("hmmss",X),ue("Hmm",Q),ue("Hmmss",X),ce(["H","HH"],ge),ce(["k","kk"],function(e,t,n){var s=D(e);t[ge]=24===s?0:s}),ce(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),ce(["h","hh"],function(e,t,n){t[ge]=D(e),g(n).bigHour=!0}),ce("hmm",function(e,t,n){var s=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s)),g(n).bigHour=!0}),ce("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s,2)),t[pe]=D(e.substr(i)),g(n).bigHour=!0}),ce("Hmm",function(e,t,n){var s=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s))}),ce("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s,2)),t[pe]=D(e.substr(i))});var tt,nt=Te("Hours",!0),st={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Ce,monthsShort:He,week:{dow:0,doy:6},weekdays:Ze,weekdaysMin:$e,weekdaysShort:ze,meridiemParse:/[ap]\.?m?\.?/i},it={},rt={};function at(e){return e?e.toLowerCase().replace("_","-"):e}function ot(e){var t=null;if(!it[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=tt._abbr,require("./locale/"+e),ut(t)}catch(e){}return it[e]}function ut(e,t){var n;return e&&((n=l(t)?ht(e):lt(e,t))?tt=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),tt._abbr}function lt(e,t){if(null===t)return delete it[e],null;var n,s=st;if(t.abbr=e,null!=it[e])T("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])s=it[t.parentLocale]._config;else{if(null==(n=ot(t.parentLocale)))return rt[t.parentLocale]||(rt[t.parentLocale]=[]),rt[t.parentLocale].push({name:e,config:t}),null;s=n._config}return it[e]=new P(x(s,t)),rt[e]&&rt[e].forEach(function(e){lt(e.name,e.config)}),ut(e),it[e]}function ht(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return tt;if(!o(e)){if(t=ot(e))return t;e=[e]}return function(e){for(var t,n,s,i,r=0;r=t&&a(i,n,!0)>=t-1)break;t--}r++}return tt}(e)}function dt(e){var t,n=e._a;return n&&-2===g(e).overflow&&(t=n[_e]<0||11Pe(n[me],n[_e])?ye:n[ge]<0||24Ae(n,r,a)?g(e)._overflowWeeks=!0:null!=u?g(e)._overflowWeekday=!0:(o=Ee(n,s,i,r,a),e._a[me]=o.year,e._dayOfYear=o.dayOfYear)}(e),null!=e._dayOfYear&&(r=ct(e._a[me],s[me]),(e._dayOfYear>Se(r)||0===e._dayOfYear)&&(g(e)._overflowDayOfYear=!0),n=Ge(r,0,e._dayOfYear),e._a[_e]=n.getUTCMonth(),e._a[ye]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=s[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ge]&&0===e._a[ve]&&0===e._a[pe]&&0===e._a[we]&&(e._nextDay=!0,e._a[ge]=0),e._d=(e._useUTC?Ge:function(e,t,n,s,i,r,a){var o;return e<100&&0<=e?(o=new Date(e+400,t,n,s,i,r,a),isFinite(o.getFullYear())&&o.setFullYear(e)):o=new Date(e,t,n,s,i,r,a),o}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ge]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(g(e).weekdayMismatch=!0)}}var mt=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,_t=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,yt=/Z|[+-]\d\d(?::?\d\d)?/,gt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],vt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],pt=/^\/?Date\((\-?\d+)/i;function wt(e){var t,n,s,i,r,a,o=e._i,u=mt.exec(o)||_t.exec(o);if(u){for(g(e).iso=!0,t=0,n=gt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},mn.isLocal=function(){return!!this.isValid()&&!this._isUTC},mn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},mn.isUtc=Et,mn.isUTC=Et,mn.zoneAbbr=function(){return this._isUTC?"UTC":""},mn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},mn.dates=n("dates accessor is deprecated. Use date instead.",un),mn.months=n("months accessor is deprecated. Use month instead",Ue),mn.years=n("years accessor is deprecated. Use year instead",Oe),mn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),mn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var e={};if(w(e,this),(e=Ot(e))._a){var t=e._isUTC?y(e._a):bt(e._a);this._isDSTShifted=this.isValid()&&0>>0,s=0;sTe(e)?(d=e+1,_-Te(e)):(d=e,_),{year:d,dayOfYear:r}}function Ce(e,a,t){var s,n,d=Ne(e.year(),a,t),r=Math.floor((e.dayOfYear()-d-1)/7)+1;return r<1?s=r+Ie(n=e.year()-1,a,t):r>Ie(e.year(),a,t)?(s=r-Ie(e.year(),a,t),n=e.year()+1):(n=e.year(),s=r),{week:s,year:n}}function Ie(e,a,t){var s=Ne(e,a,t),n=Ne(e+1,a,t);return(Te(e)-s+n)/7}C("w",["ww",2],"wo","week"),C("W",["WW",2],"Wo","isoWeek"),O("week","w"),O("isoWeek","W"),E("week",5),E("isoWeek",5),ie("w",B),ie("ww",B,V),ie("W",B),ie("WW",B,V),Me(["w","ww","W","WW"],function(e,a,t,s){a[s.substr(0,1)]=g(e)});function Ue(e,a){return e.slice(a,7).concat(e.slice(0,a))}C("d",0,"do","day"),C("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),C("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),C("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),C("e",0,0,"weekday"),C("E",0,0,"isoWeekday"),O("day","d"),O("weekday","e"),O("isoWeekday","E"),E("day",11),E("weekday",11),E("isoWeekday",11),ie("d",B),ie("e",B),ie("E",B),ie("dd",function(e,a){return a.weekdaysMinRegex(e)}),ie("ddd",function(e,a){return a.weekdaysShortRegex(e)}),ie("dddd",function(e,a){return a.weekdaysRegex(e)}),Me(["dd","ddd","dddd"],function(e,a,t,s){var n=t._locale.weekdaysParse(e,s,t._strict);null!=n?a.d=n:Y(t).invalidWeekday=e}),Me(["d","e","E"],function(e,a,t,s){a[s]=g(e)});var Ge="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var Ve="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var Ke="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var Ze=re;var $e=re;var Be=re;function qe(){function e(e,a){return a.length-e.length}var a,t,s,n,d,r=[],_=[],i=[],o=[];for(a=0;a<7;a++)t=c([2e3,1]).day(a),s=this.weekdaysMin(t,""),n=this.weekdaysShort(t,""),d=this.weekdays(t,""),r.push(s),_.push(n),i.push(d),o.push(s),o.push(n),o.push(d);for(r.sort(e),_.sort(e),i.sort(e),o.sort(e),a=0;a<7;a++)_[a]=me(_[a]),i[a]=me(i[a]),o[a]=me(o[a]);this._weekdaysRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+_.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+r.join("|")+")","i")}function Qe(){return this.hours()%12||12}function Xe(e,a){C(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),a)})}function ea(e,a){return a._meridiemParse}C("H",["HH",2],0,"hour"),C("h",["hh",2],0,Qe),C("k",["kk",2],0,function(){return this.hours()||24}),C("hmm",0,0,function(){return""+Qe.apply(this)+F(this.minutes(),2)}),C("hmmss",0,0,function(){return""+Qe.apply(this)+F(this.minutes(),2)+F(this.seconds(),2)}),C("Hmm",0,0,function(){return""+this.hours()+F(this.minutes(),2)}),C("Hmmss",0,0,function(){return""+this.hours()+F(this.minutes(),2)+F(this.seconds(),2)}),Xe("a",!0),Xe("A",!1),O("hour","h"),E("hour",13),ie("a",ea),ie("A",ea),ie("H",B),ie("h",B),ie("k",B),ie("HH",B,V),ie("hh",B,V),ie("kk",B,V),ie("hmm",q),ie("hmmss",Q),ie("Hmm",q),ie("Hmmss",Q),le(["H","HH"],Ye),le(["k","kk"],function(e,a,t){var s=g(e);a[Ye]=24===s?0:s}),le(["a","A"],function(e,a,t){t._isPm=t._locale.isPM(e),t._meridiem=e}),le(["h","hh"],function(e,a,t){a[Ye]=g(e),Y(t).bigHour=!0}),le("hmm",function(e,a,t){var s=e.length-2;a[Ye]=g(e.substr(0,s)),a[ye]=g(e.substr(s)),Y(t).bigHour=!0}),le("hmmss",function(e,a,t){var s=e.length-4,n=e.length-2;a[Ye]=g(e.substr(0,s)),a[ye]=g(e.substr(s,2)),a[fe]=g(e.substr(n)),Y(t).bigHour=!0}),le("Hmm",function(e,a,t){var s=e.length-2;a[Ye]=g(e.substr(0,s)),a[ye]=g(e.substr(s))}),le("Hmmss",function(e,a,t){var s=e.length-4,n=e.length-2;a[Ye]=g(e.substr(0,s)),a[ye]=g(e.substr(s,2)),a[fe]=g(e.substr(n))});var aa,ta=Se("Hours",!0),sa={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Oe,monthsShort:Pe,week:{dow:0,doy:6},weekdays:Ge,weekdaysMin:Ke,weekdaysShort:Ve,meridiemParse:/[ap]\.?m?\.?/i},na={},da={};function ra(e){return e?e.toLowerCase().replace("_","-"):e}function _a(e){var a=null;if(!na[e]&&"undefined"!=typeof module&&module&&module.exports)try{a=aa._abbr,require("./locale/"+e),ia(a)}catch(e){}return na[e]}function ia(e,a){var t;return e&&((t=o(a)?ma(e):oa(e,a))?aa=t:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),aa._abbr}function oa(e,a){if(null===a)return delete na[e],null;var t,s=sa;if(a.abbr=e,null!=na[e])S("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=na[e]._config;else if(null!=a.parentLocale)if(null!=na[a.parentLocale])s=na[a.parentLocale]._config;else{if(null==(t=_a(a.parentLocale)))return da[a.parentLocale]||(da[a.parentLocale]=[]),da[a.parentLocale].push({name:e,config:a}),null;s=t._config}return na[e]=new j(b(s,a)),da[e]&&da[e].forEach(function(e){oa(e.name,e.config)}),ia(e),na[e]}function ma(e){var a;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return aa;if(!_(e)){if(a=_a(e))return a;e=[e]}return function(e){for(var a,t,s,n,d=0;d=a&&r(n,t,!0)>=a-1)break;a--}d++}return aa}(e)}function ua(e){var a,t=e._a;return t&&-2===Y(e).overflow&&(a=t[Le]<0||11je(t[he],t[Le])?ce:t[Ye]<0||24Ie(t,d,r)?Y(e)._overflowWeeks=!0:null!=i?Y(e)._overflowWeekday=!0:(_=Re(t,s,n,d,r),e._a[he]=_.year,e._dayOfYear=_.dayOfYear)}(e),null!=e._dayOfYear&&(d=la(e._a[he],s[he]),(e._dayOfYear>Te(d)||0===e._dayOfYear)&&(Y(e)._overflowDayOfYear=!0),t=Je(d,0,e._dayOfYear),e._a[Le]=t.getUTCMonth(),e._a[ce]=t.getUTCDate()),a=0;a<3&&null==e._a[a];++a)e._a[a]=r[a]=s[a];for(;a<7;a++)e._a[a]=r[a]=null==e._a[a]?2===a?1:0:e._a[a];24===e._a[Ye]&&0===e._a[ye]&&0===e._a[fe]&&0===e._a[ke]&&(e._nextDay=!0,e._a[Ye]=0),e._d=(e._useUTC?Je:function(e,a,t,s,n,d,r){var _;return e<100&&0<=e?(_=new Date(e+400,a,t,s,n,d,r),isFinite(_.getFullYear())&&_.setFullYear(e)):_=new Date(e,a,t,s,n,d,r),_}).apply(null,r),n=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[Ye]=24),e._w&&void 0!==e._w.d&&e._w.d!==n&&(Y(e).weekdayMismatch=!0)}}var ha=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,La=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ca=/Z|[+-]\d\d(?::?\d\d)?/,Ya=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],ya=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],fa=/^\/?Date\((\-?\d+)/i;function ka(e){var a,t,s,n,d,r,_=e._i,i=ha.exec(_)||La.exec(_);if(i){for(Y(e).iso=!0,a=0,t=Ya.length;at.valueOf():t.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},Mt.isLocal=function(){return!!this.isValid()&&!this._isUTC},Mt.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},Mt.isUtc=Ra,Mt.isUTC=Ra,Mt.zoneAbbr=function(){return this._isUTC?"UTC":""},Mt.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},Mt.dates=t("dates accessor is deprecated. Use date instead.",_t),Mt.months=t("months accessor is deprecated. Use month instead",Ae),Mt.years=t("years accessor is deprecated. Use year instead",ve),Mt.zone=t("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,a){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,a),this):-this.utcOffset()}),Mt.isDSTShifted=t("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var e={};if(k(e,this),(e=va(e))._a){var a=e._isUTC?c(e._a):Ha(e._a);this._isDSTShifted=this.isValid()&&0.vis-custom-time-marker{background-color:inherit;color:#fff;font-size:12px;white-space:nowrap;padding:3px 5px;top:0;cursor:auto;z-index:inherit}.vis-current-time{background-color:#ff7f6e;width:2px;z-index:1;pointer-events:none}.vis-rolling-mode-btn{height:40px;width:40px;position:absolute;top:7px;right:20px;border-radius:50%;font-size:28px;cursor:pointer;opacity:.8;color:#fff;font-weight:700;text-align:center;background:#3876c2}.vis-rolling-mode-btn:before{content:"\26F6"}.vis-rolling-mode-btn:hover{opacity:1}.vis-panel{position:absolute;padding:0;margin:0;box-sizing:border-box}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-left.vis-panel.vis-vertical-scroll,.vis-right.vis-panel.vis-vertical-scroll{height:100%;overflow-x:hidden;overflow-y:scroll}.vis-left.vis-panel.vis-vertical-scroll{direction:rtl}.vis-left.vis-panel.vis-vertical-scroll .vis-content,.vis-right.vis-panel.vis-vertical-scroll{direction:ltr}.vis-right.vis-panel.vis-vertical-scroll .vis-content{direction:rtl}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-background{overflow:hidden}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}.vis-timeline{position:relative;border:1px solid #bfbfbf;overflow:hidden;padding:0;margin:0;box-sizing:border-box}.vis-loading-screen{width:100%;height:100%;position:absolute;top:0;left:0}.vis [class*=span]{min-height:0;width:auto}.vis-item{position:absolute;color:#1a1a1a;border-color:#97b0f8;border-width:1px;background-color:#d5ddf6;display:inline-block;z-index:1}.vis-item.vis-selected{border-color:#ffc200;background-color:#fff785;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-point.vis-selected{background-color:#fff785}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:none}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item-visible-frame{white-space:nowrap}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-onUpdateTime-tooltip{position:absolute;background:#4f81bd;color:#fff;width:200px;text-align:center;white-space:nowrap;padding:5px;border-radius:1px;transition:.4s;-o-transition:.4s;-moz-transition:.4s;-webkit-transition:.4s}.vis-item .vis-delete,.vis-item .vis-delete-rtl{position:absolute;top:0;width:24px;height:24px;box-sizing:border-box;padding:0 5px;cursor:pointer;-webkit-transition:background .2s linear;-moz-transition:background .2s linear;-ms-transition:background .2s linear;-o-transition:background .2s linear;transition:background .2s linear}.vis-item .vis-delete{right:-24px}.vis-item .vis-delete-rtl{left:-24px}.vis-item .vis-delete-rtl:after,.vis-item .vis-delete:after{content:"\00D7";color:red;font-family:arial,sans-serif;font-size:22px;font-weight:700;-webkit-transition:color .2s linear;-moz-transition:color .2s linear;-ms-transition:color .2s linear;-o-transition:color .2s linear;transition:color .2s linear}.vis-item .vis-delete-rtl:hover,.vis-item .vis-delete:hover{background:red}.vis-item .vis-delete-rtl:hover:after,.vis-item .vis-delete:hover:after{color:#fff}.vis-item .vis-drag-center{position:absolute;width:100%;height:100%;top:0;left:0;cursor:move}.vis-item.vis-range .vis-drag-left{left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-left,.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0}.vis-item.vis-range .vis-drag-right{right:-4px;cursor:e-resize}.vis-range.vis-item.vis-readonly .vis-drag-left,.vis-range.vis-item.vis-readonly .vis-drag-right{cursor:auto}.vis-item.vis-cluster{vertical-align:center;text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-cluster-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item.vis-cluster-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}div.vis-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none;z-index:5}.vis-itemset{position:relative;padding:0;margin:0;box-sizing:border-box}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-nesting-group{cursor:pointer}.vis-label.vis-nested-group.vis-group-level-unknown-but-gte1{background:#f5f5f5}.vis-label.vis-nested-group.vis-group-level-0{background-color:#fff}.vis-ltr .vis-label.vis-nested-group.vis-group-level-0 .vis-inner{padding-left:0}.vis-rtl .vis-label.vis-nested-group.vis-group-level-0 .vis-inner{padding-right:0}.vis-label.vis-nested-group.vis-group-level-1{background-color:rgba(0,0,0,.05)}.vis-ltr .vis-label.vis-nested-group.vis-group-level-1 .vis-inner{padding-left:15px}.vis-rtl .vis-label.vis-nested-group.vis-group-level-1 .vis-inner{padding-right:15px}.vis-label.vis-nested-group.vis-group-level-2{background-color:rgba(0,0,0,.1)}.vis-ltr .vis-label.vis-nested-group.vis-group-level-2 .vis-inner{padding-left:30px}.vis-rtl .vis-label.vis-nested-group.vis-group-level-2 .vis-inner{padding-right:30px}.vis-label.vis-nested-group.vis-group-level-3{background-color:rgba(0,0,0,.15)}.vis-ltr .vis-label.vis-nested-group.vis-group-level-3 .vis-inner{padding-left:45px}.vis-rtl .vis-label.vis-nested-group.vis-group-level-3 .vis-inner{padding-right:45px}.vis-label.vis-nested-group.vis-group-level-4{background-color:rgba(0,0,0,.2)}.vis-ltr .vis-label.vis-nested-group.vis-group-level-4 .vis-inner{padding-left:60px}.vis-rtl .vis-label.vis-nested-group.vis-group-level-4 .vis-inner{padding-right:60px}.vis-label.vis-nested-group.vis-group-level-5{background-color:rgba(0,0,0,.25)}.vis-ltr .vis-label.vis-nested-group.vis-group-level-5 .vis-inner{padding-left:75px}.vis-rtl .vis-label.vis-nested-group.vis-group-level-5 .vis-inner{padding-right:75px}.vis-label.vis-nested-group.vis-group-level-6{background-color:rgba(0,0,0,.3)}.vis-ltr .vis-label.vis-nested-group.vis-group-level-6 .vis-inner{padding-left:90px}.vis-rtl .vis-label.vis-nested-group.vis-group-level-6 .vis-inner{padding-right:90px}.vis-label.vis-nested-group.vis-group-level-7{background-color:rgba(0,0,0,.35)}.vis-ltr .vis-label.vis-nested-group.vis-group-level-7 .vis-inner{padding-left:105px}.vis-rtl .vis-label.vis-nested-group.vis-group-level-7 .vis-inner{padding-right:105px}.vis-label.vis-nested-group.vis-group-level-8{background-color:rgba(0,0,0,.4)}.vis-ltr .vis-label.vis-nested-group.vis-group-level-8 .vis-inner{padding-left:120px}.vis-rtl .vis-label.vis-nested-group.vis-group-level-8 .vis-inner{padding-right:120px}.vis-label.vis-nested-group.vis-group-level-9{background-color:rgba(0,0,0,.45)}.vis-ltr .vis-label.vis-nested-group.vis-group-level-9 .vis-inner{padding-left:135px}.vis-rtl .vis-label.vis-nested-group.vis-group-level-9 .vis-inner{padding-right:135px}.vis-label.vis-nested-group{background-color:rgba(0,0,0,.5)}.vis-ltr .vis-label.vis-nested-group .vis-inner{padding-left:150px}.vis-rtl .vis-label.vis-nested-group .vis-inner{padding-right:150px}.vis-group-level-unknown-but-gte1{border:1px solid red}.vis-label.vis-nesting-group:before{display:inline-block;width:15px}.vis-label.vis-nesting-group.expanded:before{content:"\25BC"}.vis-label.vis-nesting-group.collapsed:before{content:"\25B6"}.vis-rtl .vis-label.vis-nesting-group.collapsed:before{content:"\25C0"}.vis-ltr .vis-label:not(.vis-nesting-group):not(.vis-group-level-0){padding-left:15px}.vis-rtl .vis-label:not(.vis-nesting-group):not(.vis-group-level-0){padding-right:15px}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-labelset{overflow:hidden}.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-group-is-dragging{background:rgba(0,0,0,.1)}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper:after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;padding:1px;margin:0;pointer-events:none}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:transparent;width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(180deg,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#dedede",endColorstr="#c8c8c8",GradientType=0);border:1px solid #999;box-shadow:0 0 3px 0 #aaa;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2,#385380);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(180deg,#3876c2 0,#385380);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#3876c2",endColorstr="#385380",GradientType=0);box-shadow:0 0 1px 0 #111927;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:none}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(180deg,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#9d9d9d",endColorstr="#c8c8c8",GradientType=0)}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(180deg,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr="#dedede",endColorstr="#c8c8c8",GradientType=0);border:1px solid #999;box-shadow:0 0 3px 0 #aaa;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:1px solid #fff;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:transparent;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0) rgba(136,183,213,0) rgba(136,183,213,0) rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0) rgba(194,225,245,0) rgba(194,225,245,0) #f2faff;border-width:12px;margin-top:-12px}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px hsla(0,0%,60.4%,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}
+/*# sourceMappingURL=vis-timeline-graph2d.min.css.map */
\ No newline at end of file
diff --git a/situation-overview-web/public/vis/timeline/vis-timeline-graph2d.min.js b/situation-overview-web/public/vis/timeline/vis-timeline-graph2d.min.js
new file mode 100644
index 00000000..fff97195
--- /dev/null
+++ b/situation-overview-web/public/vis/timeline/vis-timeline-graph2d.min.js
@@ -0,0 +1,34 @@
+/**
+ * vis-timeline and vis-graph2d
+ * https://visjs.github.io/vis-timeline/
+ *
+ * Create a fully customizable, interactive timeline with items and ranges.
+ *
+ * @version 7.5.1
+ * @date 2022-03-01T18:26:09.239Z
+ *
+ * @copyright (c) 2011-2017 Almende B.V, http://almende.com
+ * @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs
+ *
+ * @license
+ * vis.js is dual licensed under both
+ *
+ * 1. The Apache 2.0 License
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * and
+ *
+ * 2. The MIT License
+ * http://opensource.org/licenses/MIT
+ *
+ * vis.js may be distributed under either license.
+ */
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).vis=t.vis||{})}(this,(function(t){var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function n(t){throw new Error('Could not dynamically require "'+t+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}var o={exports:{}};!function(t,e){t.exports=function(){var e,i;function o(){return e.apply(null,arguments)}function r(t){e=t}function s(t){return t instanceof Array||"[object Array]"===Object.prototype.toString.call(t)}function a(t){return null!=t&&"[object Object]"===Object.prototype.toString.call(t)}function l(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function h(t){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(t).length;var e;for(e in t)if(l(t,e))return!1;return!0}function u(t){return void 0===t}function d(t){return"number"==typeof t||"[object Number]"===Object.prototype.toString.call(t)}function c(t){return t instanceof Date||"[object Date]"===Object.prototype.toString.call(t)}function p(t,e){var i,n=[];for(i=0;i>>0;for(e=0;e0)for(i=0;i<_.length;i++)u(o=e[n=_[i]])||(t[n]=o);return t}function x(t){k(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===w&&(w=!0,o.updateOffset(this),w=!1)}function D(t){return t instanceof x||null!=t&&null!=t._isAMomentObject}function S(t){!1===o.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function C(t,e){var i=!0;return f((function(){if(null!=o.deprecationHandler&&o.deprecationHandler(null,t),i){var n,r,s,a=[];for(r=0;r=0?i?"+":"":"-")+Math.pow(10,Math.max(0,o)).toString().substr(1)+n}var R=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,Y=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,j={},H={};function z(t,e,i,n){var o=n;"string"==typeof n&&(o=function(){return this[n]()}),t&&(H[t]=o),e&&(H[e[0]]=function(){return F(o.apply(this,arguments),e[1],e[2])}),i&&(H[i]=function(){return this.localeData().ordinal(o.apply(this,arguments),t)})}function G(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function B(t){var e,i,n=t.match(R);for(e=0,i=n.length;e=0&&Y.test(t);)t=t.replace(Y,n),Y.lastIndex=0,i-=1;return t}var U={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"};function X(t){var e=this._longDateFormat[t],i=this._longDateFormat[t.toUpperCase()];return e||!i?e:(this._longDateFormat[t]=i.match(R).map((function(t){return"MMMM"===t||"MM"===t||"DD"===t||"dddd"===t?t.slice(1):t})).join(""),this._longDateFormat[t])}var q="Invalid date";function Z(){return this._invalidDate}var $="%d",K=/\d{1,2}/;function J(t){return this._ordinal.replace("%d",t)}var Q={future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"};function tt(t,e,i,n){var o=this._relativeTime[i];return E(o)?o(t,e,i,n):o.replace(/%d/i,t)}function et(t,e){var i=this._relativeTime[t>0?"future":"past"];return E(i)?i(e):i.replace(/%s/i,e)}var it={};function nt(t,e){var i=t.toLowerCase();it[i]=it[i+"s"]=it[e]=t}function ot(t){return"string"==typeof t?it[t]||it[t.toLowerCase()]:void 0}function rt(t){var e,i,n={};for(i in t)l(t,i)&&(e=ot(i))&&(n[e]=t[i]);return n}var st={};function at(t,e){st[t]=e}function lt(t){var e,i=[];for(e in t)l(t,e)&&i.push({unit:e,priority:st[e]});return i.sort((function(t,e){return t.priority-e.priority})),i}function ht(t){return t%4==0&&t%100!=0||t%400==0}function ut(t){return t<0?Math.ceil(t)||0:Math.floor(t)}function dt(t){var e=+t,i=0;return 0!==e&&isFinite(e)&&(i=ut(e)),i}function ct(t,e){return function(i){return null!=i?(ft(this,t,i),o.updateOffset(this,e),this):pt(this,t)}}function pt(t,e){return t.isValid()?t._d["get"+(t._isUTC?"UTC":"")+e]():NaN}function ft(t,e,i){t.isValid()&&!isNaN(i)&&("FullYear"===e&&ht(t.year())&&1===t.month()&&29===t.date()?(i=dt(i),t._d["set"+(t._isUTC?"UTC":"")+e](i,t.month(),te(i,t.month()))):t._d["set"+(t._isUTC?"UTC":"")+e](i))}function mt(t){return E(this[t=ot(t)])?this[t]():this}function vt(t,e){if("object"==typeof t){var i,n=lt(t=rt(t));for(i=0;i68?1900:2e3)};var ge=ct("FullYear",!0);function ye(){return ht(this.year())}function be(t,e,i,n,o,r,s){var a;return t<100&&t>=0?(a=new Date(t+400,e,i,n,o,r,s),isFinite(a.getFullYear())&&a.setFullYear(t)):a=new Date(t,e,i,n,o,r,s),a}function _e(t){var e,i;return t<100&&t>=0?((i=Array.prototype.slice.call(arguments))[0]=t+400,e=new Date(Date.UTC.apply(null,i)),isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t)):e=new Date(Date.UTC.apply(null,arguments)),e}function we(t,e,i){var n=7+e-i;return-(7+_e(t,0,n).getUTCDay()-e)%7+n-1}function ke(t,e,i,n,o){var r,s,a=1+7*(e-1)+(7+i-n)%7+we(t,n,o);return a<=0?s=ve(r=t-1)+a:a>ve(t)?(r=t+1,s=a-ve(t)):(r=t,s=a),{year:r,dayOfYear:s}}function xe(t,e,i){var n,o,r=we(t.year(),e,i),s=Math.floor((t.dayOfYear()-r-1)/7)+1;return s<1?n=s+De(o=t.year()-1,e,i):s>De(t.year(),e,i)?(n=s-De(t.year(),e,i),o=t.year()+1):(o=t.year(),n=s),{week:n,year:o}}function De(t,e,i){var n=we(t,e,i),o=we(t+1,e,i);return(ve(t)-n+o)/7}function Se(t){return xe(t,this._week.dow,this._week.doy).week}z("w",["ww",2],"wo","week"),z("W",["WW",2],"Wo","isoWeek"),nt("week","w"),nt("isoWeek","W"),at("week",5),at("isoWeek",5),Nt("w",xt),Nt("ww",xt,bt),Nt("W",xt),Nt("WW",xt,bt),zt(["w","ww","W","WW"],(function(t,e,i,n){e[n.substr(0,1)]=dt(t)}));var Ce={dow:0,doy:6};function Te(){return this._week.dow}function Me(){return this._week.doy}function Oe(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")}function Ee(t){var e=xe(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")}function Pe(t,e){return"string"!=typeof t?t:isNaN(t)?"number"==typeof(t=e.weekdaysParse(t))?t:null:parseInt(t,10)}function Ae(t,e){return"string"==typeof t?e.weekdaysParse(t)%7||7:isNaN(t)?null:t}function Ie(t,e){return t.slice(e,7).concat(t.slice(0,e))}z("d",0,"do","day"),z("dd",0,0,(function(t){return this.localeData().weekdaysMin(this,t)})),z("ddd",0,0,(function(t){return this.localeData().weekdaysShort(this,t)})),z("dddd",0,0,(function(t){return this.localeData().weekdays(this,t)})),z("e",0,0,"weekday"),z("E",0,0,"isoWeekday"),nt("day","d"),nt("weekday","e"),nt("isoWeekday","E"),at("day",11),at("weekday",11),at("isoWeekday",11),Nt("d",xt),Nt("e",xt),Nt("E",xt),Nt("dd",(function(t,e){return e.weekdaysMinRegex(t)})),Nt("ddd",(function(t,e){return e.weekdaysShortRegex(t)})),Nt("dddd",(function(t,e){return e.weekdaysRegex(t)})),zt(["dd","ddd","dddd"],(function(t,e,i,n){var o=i._locale.weekdaysParse(t,n,i._strict);null!=o?e.d=o:g(i).invalidWeekday=t})),zt(["d","e","E"],(function(t,e,i,n){e[n]=dt(t)}));var Le="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Ne="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Fe="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),Re=Lt,Ye=Lt,je=Lt;function He(t,e){var i=s(this._weekdays)?this._weekdays:this._weekdays[t&&!0!==t&&this._weekdays.isFormat.test(e)?"format":"standalone"];return!0===t?Ie(i,this._week.dow):t?i[t.day()]:i}function ze(t){return!0===t?Ie(this._weekdaysShort,this._week.dow):t?this._weekdaysShort[t.day()]:this._weekdaysShort}function Ge(t){return!0===t?Ie(this._weekdaysMin,this._week.dow):t?this._weekdaysMin[t.day()]:this._weekdaysMin}function Be(t,e,i){var n,o,r,s=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],n=0;n<7;++n)r=m([2e3,1]).day(n),this._minWeekdaysParse[n]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[n]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[n]=this.weekdays(r,"").toLocaleLowerCase();return i?"dddd"===e?-1!==(o=Bt.call(this._weekdaysParse,s))?o:null:"ddd"===e?-1!==(o=Bt.call(this._shortWeekdaysParse,s))?o:null:-1!==(o=Bt.call(this._minWeekdaysParse,s))?o:null:"dddd"===e?-1!==(o=Bt.call(this._weekdaysParse,s))||-1!==(o=Bt.call(this._shortWeekdaysParse,s))||-1!==(o=Bt.call(this._minWeekdaysParse,s))?o:null:"ddd"===e?-1!==(o=Bt.call(this._shortWeekdaysParse,s))||-1!==(o=Bt.call(this._weekdaysParse,s))||-1!==(o=Bt.call(this._minWeekdaysParse,s))?o:null:-1!==(o=Bt.call(this._minWeekdaysParse,s))||-1!==(o=Bt.call(this._weekdaysParse,s))||-1!==(o=Bt.call(this._shortWeekdaysParse,s))?o:null}function We(t,e,i){var n,o,r;if(this._weekdaysParseExact)return Be.call(this,t,e,i);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),n=0;n<7;n++){if(o=m([2e3,1]).day(n),i&&!this._fullWeekdaysParse[n]&&(this._fullWeekdaysParse[n]=new RegExp("^"+this.weekdays(o,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[n]=new RegExp("^"+this.weekdaysShort(o,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[n]=new RegExp("^"+this.weekdaysMin(o,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[n]||(r="^"+this.weekdays(o,"")+"|^"+this.weekdaysShort(o,"")+"|^"+this.weekdaysMin(o,""),this._weekdaysParse[n]=new RegExp(r.replace(".",""),"i")),i&&"dddd"===e&&this._fullWeekdaysParse[n].test(t))return n;if(i&&"ddd"===e&&this._shortWeekdaysParse[n].test(t))return n;if(i&&"dd"===e&&this._minWeekdaysParse[n].test(t))return n;if(!i&&this._weekdaysParse[n].test(t))return n}}function Ve(t){if(!this.isValid())return null!=t?this:NaN;var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=Pe(t,this.localeData()),this.add(t-e,"d")):e}function Ue(t){if(!this.isValid())return null!=t?this:NaN;var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")}function Xe(t){if(!this.isValid())return null!=t?this:NaN;if(null!=t){var e=Ae(t,this.localeData());return this.day(this.day()%7?e:e-7)}return this.day()||7}function qe(t){return this._weekdaysParseExact?(l(this,"_weekdaysRegex")||Ke.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(l(this,"_weekdaysRegex")||(this._weekdaysRegex=Re),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)}function Ze(t){return this._weekdaysParseExact?(l(this,"_weekdaysRegex")||Ke.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(l(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=Ye),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function $e(t){return this._weekdaysParseExact?(l(this,"_weekdaysRegex")||Ke.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(l(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=je),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Ke(){function t(t,e){return e.length-t.length}var e,i,n,o,r,s=[],a=[],l=[],h=[];for(e=0;e<7;e++)i=m([2e3,1]).day(e),n=Yt(this.weekdaysMin(i,"")),o=Yt(this.weekdaysShort(i,"")),r=Yt(this.weekdays(i,"")),s.push(n),a.push(o),l.push(r),h.push(n),h.push(o),h.push(r);s.sort(t),a.sort(t),l.sort(t),h.sort(t),this._weekdaysRegex=new RegExp("^("+h.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+a.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+s.join("|")+")","i")}function Je(){return this.hours()%12||12}function Qe(){return this.hours()||24}function ti(t,e){z(t,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)}))}function ei(t,e){return e._meridiemParse}function ii(t){return"p"===(t+"").toLowerCase().charAt(0)}z("H",["HH",2],0,"hour"),z("h",["hh",2],0,Je),z("k",["kk",2],0,Qe),z("hmm",0,0,(function(){return""+Je.apply(this)+F(this.minutes(),2)})),z("hmmss",0,0,(function(){return""+Je.apply(this)+F(this.minutes(),2)+F(this.seconds(),2)})),z("Hmm",0,0,(function(){return""+this.hours()+F(this.minutes(),2)})),z("Hmmss",0,0,(function(){return""+this.hours()+F(this.minutes(),2)+F(this.seconds(),2)})),ti("a",!0),ti("A",!1),nt("hour","h"),at("hour",13),Nt("a",ei),Nt("A",ei),Nt("H",xt),Nt("h",xt),Nt("k",xt),Nt("HH",xt,bt),Nt("hh",xt,bt),Nt("kk",xt,bt),Nt("hmm",Dt),Nt("hmmss",St),Nt("Hmm",Dt),Nt("Hmmss",St),Ht(["H","HH"],Xt),Ht(["k","kk"],(function(t,e,i){var n=dt(t);e[Xt]=24===n?0:n})),Ht(["a","A"],(function(t,e,i){i._isPm=i._locale.isPM(t),i._meridiem=t})),Ht(["h","hh"],(function(t,e,i){e[Xt]=dt(t),g(i).bigHour=!0})),Ht("hmm",(function(t,e,i){var n=t.length-2;e[Xt]=dt(t.substr(0,n)),e[qt]=dt(t.substr(n)),g(i).bigHour=!0})),Ht("hmmss",(function(t,e,i){var n=t.length-4,o=t.length-2;e[Xt]=dt(t.substr(0,n)),e[qt]=dt(t.substr(n,2)),e[Zt]=dt(t.substr(o)),g(i).bigHour=!0})),Ht("Hmm",(function(t,e,i){var n=t.length-2;e[Xt]=dt(t.substr(0,n)),e[qt]=dt(t.substr(n))})),Ht("Hmmss",(function(t,e,i){var n=t.length-4,o=t.length-2;e[Xt]=dt(t.substr(0,n)),e[qt]=dt(t.substr(n,2)),e[Zt]=dt(t.substr(o))}));var ni=/[ap]\.?m?\.?/i,oi=ct("Hours",!0);function ri(t,e,i){return t>11?i?"pm":"PM":i?"am":"AM"}var si,ai={calendar:L,longDateFormat:U,invalidDate:q,ordinal:$,dayOfMonthOrdinalParse:K,relativeTime:Q,months:ee,monthsShort:ie,week:Ce,weekdays:Le,weekdaysMin:Fe,weekdaysShort:Ne,meridiemParse:ni},li={},hi={};function ui(t,e){var i,n=Math.min(t.length,e.length);for(i=0;i0;){if(n=pi(o.slice(0,e).join("-")))return n;if(i&&i.length>=e&&ui(o,i)>=e-1)break;e--}r++}return si}function pi(e){var i=null;if(void 0===li[e]&&t&&t.exports)try{i=si._abbr,n("./locale/"+e),fi(i)}catch(t){li[e]=null}return li[e]}function fi(t,e){var i;return t&&((i=u(e)?gi(t):mi(t,e))?si=i:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+t+" not found. Did you forget to load it?")),si._abbr}function mi(t,e){if(null!==e){var i,n=ai;if(e.abbr=t,null!=li[t])O("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),n=li[t]._config;else if(null!=e.parentLocale)if(null!=li[e.parentLocale])n=li[e.parentLocale]._config;else{if(null==(i=pi(e.parentLocale)))return hi[e.parentLocale]||(hi[e.parentLocale]=[]),hi[e.parentLocale].push({name:t,config:e}),null;n=i._config}return li[t]=new I(A(n,e)),hi[t]&&hi[t].forEach((function(t){mi(t.name,t.config)})),fi(t),li[t]}return delete li[t],null}function vi(t,e){if(null!=e){var i,n,o=ai;null!=li[t]&&null!=li[t].parentLocale?li[t].set(A(li[t]._config,e)):(null!=(n=pi(t))&&(o=n._config),e=A(o,e),null==n&&(e.abbr=t),(i=new I(e)).parentLocale=li[t],li[t]=i),fi(t)}else null!=li[t]&&(null!=li[t].parentLocale?(li[t]=li[t].parentLocale,t===fi()&&fi(t)):null!=li[t]&&delete li[t]);return li[t]}function gi(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return si;if(!s(t)){if(e=pi(t))return e;t=[t]}return ci(t)}function yi(){return T(li)}function bi(t){var e,i=t._a;return i&&-2===g(t).overflow&&(e=i[Vt]<0||i[Vt]>11?Vt:i[Ut]<1||i[Ut]>te(i[Wt],i[Vt])?Ut:i[Xt]<0||i[Xt]>24||24===i[Xt]&&(0!==i[qt]||0!==i[Zt]||0!==i[$t])?Xt:i[qt]<0||i[qt]>59?qt:i[Zt]<0||i[Zt]>59?Zt:i[$t]<0||i[$t]>999?$t:-1,g(t)._overflowDayOfYear&&(eUt)&&(e=Ut),g(t)._overflowWeeks&&-1===e&&(e=Kt),g(t)._overflowWeekday&&-1===e&&(e=Jt),g(t).overflow=e),t}var _i=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,wi=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ki=/Z|[+-]\d\d(?::?\d\d)?/,xi=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/],["YYYYMM",/\d{6}/,!1],["YYYY",/\d{4}/,!1]],Di=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Si=/^\/?Date\((-?\d+)/i,Ci=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,Ti={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Mi(t){var e,i,n,o,r,s,a=t._i,l=_i.exec(a)||wi.exec(a);if(l){for(g(t).iso=!0,e=0,i=xi.length;eve(r)||0===t._dayOfYear)&&(g(t)._overflowDayOfYear=!0),i=_e(r,0,t._dayOfYear),t._a[Vt]=i.getUTCMonth(),t._a[Ut]=i.getUTCDate()),e=0;e<3&&null==t._a[e];++e)t._a[e]=s[e]=n[e];for(;e<7;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[Xt]&&0===t._a[qt]&&0===t._a[Zt]&&0===t._a[$t]&&(t._nextDay=!0,t._a[Xt]=0),t._d=(t._useUTC?_e:be).apply(null,s),o=t._useUTC?t._d.getUTCDay():t._d.getDay(),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[Xt]=24),t._w&&void 0!==t._w.d&&t._w.d!==o&&(g(t).weekdayMismatch=!0)}}function ji(t){var e,i,n,o,r,s,a,l,h;null!=(e=t._w).GG||null!=e.W||null!=e.E?(r=1,s=4,i=Fi(e.GG,t._a[Wt],xe(qi(),1,4).year),n=Fi(e.W,1),((o=Fi(e.E,1))<1||o>7)&&(l=!0)):(r=t._locale._week.dow,s=t._locale._week.doy,h=xe(qi(),r,s),i=Fi(e.gg,t._a[Wt],h.year),n=Fi(e.w,h.week),null!=e.d?((o=e.d)<0||o>6)&&(l=!0):null!=e.e?(o=e.e+r,(e.e<0||e.e>6)&&(l=!0)):o=r),n<1||n>De(i,r,s)?g(t)._overflowWeeks=!0:null!=l?g(t)._overflowWeekday=!0:(a=ke(i,n,o,r,s),t._a[Wt]=a.year,t._dayOfYear=a.dayOfYear)}function Hi(t){if(t._f!==o.ISO_8601)if(t._f!==o.RFC_2822){t._a=[],g(t).empty=!0;var e,i,n,r,s,a,l=""+t._i,h=l.length,u=0;for(n=V(t._f,t._locale).match(R)||[],e=0;e0&&g(t).unusedInput.push(s),l=l.slice(l.indexOf(i)+i.length),u+=i.length),H[r]?(i?g(t).empty=!1:g(t).unusedTokens.push(r),Gt(r,i,t)):t._strict&&!i&&g(t).unusedTokens.push(r);g(t).charsLeftOver=h-u,l.length>0&&g(t).unusedInput.push(l),t._a[Xt]<=12&&!0===g(t).bigHour&&t._a[Xt]>0&&(g(t).bigHour=void 0),g(t).parsedDateParts=t._a.slice(0),g(t).meridiem=t._meridiem,t._a[Xt]=zi(t._locale,t._a[Xt],t._meridiem),null!==(a=g(t).era)&&(t._a[Wt]=t._locale.erasConvertYear(a,t._a[Wt])),Yi(t),bi(t)}else Li(t);else Mi(t)}function zi(t,e,i){var n;return null==i?e:null!=t.meridiemHour?t.meridiemHour(e,i):null!=t.isPM?((n=t.isPM(i))&&e<12&&(e+=12),n||12!==e||(e=0),e):e}function Gi(t){var e,i,n,o,r,s,a=!1;if(0===t._f.length)return g(t).invalidFormat=!0,void(t._d=new Date(NaN));for(o=0;othis?this:t:b()}));function Ki(t,e){var i,n;if(1===e.length&&s(e[0])&&(e=e[0]),!e.length)return qi();for(i=e[0],n=1;nthis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function kn(){if(!u(this._isDSTShifted))return this._isDSTShifted;var t,e={};return k(e,this),(e=Vi(e))._a?(t=e._isUTC?m(e._a):qi(e._a),this._isDSTShifted=this.isValid()&&hn(e._a,t.toArray())>0):this._isDSTShifted=!1,this._isDSTShifted}function xn(){return!!this.isValid()&&!this._isUTC}function Dn(){return!!this.isValid()&&this._isUTC}function Sn(){return!!this.isValid()&&this._isUTC&&0===this._offset}o.updateOffset=function(){};var Cn=/^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/,Tn=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function Mn(t,e){var i,n,o,r=t,s=null;return an(t)?r={ms:t._milliseconds,d:t._days,M:t._months}:d(t)||!isNaN(+t)?(r={},e?r[e]=+t:r.milliseconds=+t):(s=Cn.exec(t))?(i="-"===s[1]?-1:1,r={y:0,d:dt(s[Ut])*i,h:dt(s[Xt])*i,m:dt(s[qt])*i,s:dt(s[Zt])*i,ms:dt(ln(1e3*s[$t]))*i}):(s=Tn.exec(t))?(i="-"===s[1]?-1:1,r={y:On(s[2],i),M:On(s[3],i),w:On(s[4],i),d:On(s[5],i),h:On(s[6],i),m:On(s[7],i),s:On(s[8],i)}):null==r?r={}:"object"==typeof r&&("from"in r||"to"in r)&&(o=Pn(qi(r.from),qi(r.to)),(r={}).ms=o.milliseconds,r.M=o.months),n=new sn(r),an(t)&&l(t,"_locale")&&(n._locale=t._locale),an(t)&&l(t,"_isValid")&&(n._isValid=t._isValid),n}function On(t,e){var i=t&&parseFloat(t.replace(",","."));return(isNaN(i)?0:i)*e}function En(t,e){var i={};return i.months=e.month()-t.month()+12*(e.year()-t.year()),t.clone().add(i.months,"M").isAfter(e)&&--i.months,i.milliseconds=+e-+t.clone().add(i.months,"M"),i}function Pn(t,e){var i;return t.isValid()&&e.isValid()?(e=pn(e,t),t.isBefore(e)?i=En(t,e):((i=En(e,t)).milliseconds=-i.milliseconds,i.months=-i.months),i):{milliseconds:0,months:0}}function An(t,e){return function(i,n){var o;return null===n||isNaN(+n)||(O(e,"moment()."+e+"(period, number) is deprecated. Please use moment()."+e+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),o=i,i=n,n=o),In(this,Mn(i,n),t),this}}function In(t,e,i,n){var r=e._milliseconds,s=ln(e._days),a=ln(e._months);t.isValid()&&(n=null==n||n,a&&ue(t,pt(t,"Month")+a*i),s&&ft(t,"Date",pt(t,"Date")+s*i),r&&t._d.setTime(t._d.valueOf()+r*i),n&&o.updateOffset(t,s||a))}Mn.fn=sn.prototype,Mn.invalid=rn;var Ln=An(1,"add"),Nn=An(-1,"subtract");function Fn(t){return"string"==typeof t||t instanceof String}function Rn(t){return D(t)||c(t)||Fn(t)||d(t)||jn(t)||Yn(t)||null==t}function Yn(t){var e,i,n=a(t)&&!h(t),o=!1,r=["years","year","y","months","month","M","days","day","d","dates","date","D","hours","hour","h","minutes","minute","m","seconds","second","s","milliseconds","millisecond","ms"];for(e=0;ei.valueOf():i.valueOf()9999?W(i,e?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):E(Date.prototype.toISOString)?e?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",W(i,"Z")):W(i,e?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")}function to(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var t,e,i,n,o="moment",r="";return this.isLocal()||(o=0===this.utcOffset()?"moment.utc":"moment.parseZone",r="Z"),t="["+o+'("]',e=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",i="-MM-DD[T]HH:mm:ss.SSS",n=r+'[")]',this.format(t+e+i+n)}function eo(t){t||(t=this.isUtc()?o.defaultFormatUtc:o.defaultFormat);var e=W(this,t);return this.localeData().postformat(e)}function io(t,e){return this.isValid()&&(D(t)&&t.isValid()||qi(t).isValid())?Mn({to:this,from:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()}function no(t){return this.from(qi(),t)}function oo(t,e){return this.isValid()&&(D(t)&&t.isValid()||qi(t).isValid())?Mn({from:this,to:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()}function ro(t){return this.to(qi(),t)}function so(t){var e;return void 0===t?this._locale._abbr:(null!=(e=gi(t))&&(this._locale=e),this)}o.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",o.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var ao=C("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",(function(t){return void 0===t?this.localeData():this.locale(t)}));function lo(){return this._locale}var ho=1e3,uo=60*ho,co=60*uo,po=3506328*co;function fo(t,e){return(t%e+e)%e}function mo(t,e,i){return t<100&&t>=0?new Date(t+400,e,i)-po:new Date(t,e,i).valueOf()}function vo(t,e,i){return t<100&&t>=0?Date.UTC(t+400,e,i)-po:Date.UTC(t,e,i)}function go(t){var e,i;if(void 0===(t=ot(t))||"millisecond"===t||!this.isValid())return this;switch(i=this._isUTC?vo:mo,t){case"year":e=i(this.year(),0,1);break;case"quarter":e=i(this.year(),this.month()-this.month()%3,1);break;case"month":e=i(this.year(),this.month(),1);break;case"week":e=i(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":e=i(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":e=i(this.year(),this.month(),this.date());break;case"hour":e=this._d.valueOf(),e-=fo(e+(this._isUTC?0:this.utcOffset()*uo),co);break;case"minute":e=this._d.valueOf(),e-=fo(e,uo);break;case"second":e=this._d.valueOf(),e-=fo(e,ho)}return this._d.setTime(e),o.updateOffset(this,!0),this}function yo(t){var e,i;if(void 0===(t=ot(t))||"millisecond"===t||!this.isValid())return this;switch(i=this._isUTC?vo:mo,t){case"year":e=i(this.year()+1,0,1)-1;break;case"quarter":e=i(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":e=i(this.year(),this.month()+1,1)-1;break;case"week":e=i(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":e=i(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":e=i(this.year(),this.month(),this.date()+1)-1;break;case"hour":e=this._d.valueOf(),e+=co-fo(e+(this._isUTC?0:this.utcOffset()*uo),co)-1;break;case"minute":e=this._d.valueOf(),e+=uo-fo(e,uo)-1;break;case"second":e=this._d.valueOf(),e+=ho-fo(e,ho)-1}return this._d.setTime(e),o.updateOffset(this,!0),this}function bo(){return this._d.valueOf()-6e4*(this._offset||0)}function _o(){return Math.floor(this.valueOf()/1e3)}function wo(){return new Date(this.valueOf())}function ko(){var t=this;return[t.year(),t.month(),t.date(),t.hour(),t.minute(),t.second(),t.millisecond()]}function xo(){var t=this;return{years:t.year(),months:t.month(),date:t.date(),hours:t.hours(),minutes:t.minutes(),seconds:t.seconds(),milliseconds:t.milliseconds()}}function Do(){return this.isValid()?this.toISOString():null}function So(){return y(this)}function Co(){return f({},g(this))}function To(){return g(this).overflow}function Mo(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}function Oo(t,e){var i,n,r,s=this._eras||gi("en")._eras;for(i=0,n=s.length;i=0)return l[n]}function Po(t,e){var i=t.since<=t.until?1:-1;return void 0===e?o(t.since).year():o(t.since).year()+(e-t.offset)*i}function Ao(){var t,e,i,n=this.localeData().eras();for(t=0,e=n.length;t(r=De(t,n,o))&&(e=r),Jo.call(this,t,e,i,n,o))}function Jo(t,e,i,n,o){var r=ke(t,e,i,n,o),s=_e(r.year,0,r.dayOfYear);return this.year(s.getUTCFullYear()),this.month(s.getUTCMonth()),this.date(s.getUTCDate()),this}function Qo(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)}z("N",0,0,"eraAbbr"),z("NN",0,0,"eraAbbr"),z("NNN",0,0,"eraAbbr"),z("NNNN",0,0,"eraName"),z("NNNNN",0,0,"eraNarrow"),z("y",["y",1],"yo","eraYear"),z("y",["yy",2],0,"eraYear"),z("y",["yyy",3],0,"eraYear"),z("y",["yyyy",4],0,"eraYear"),Nt("N",jo),Nt("NN",jo),Nt("NNN",jo),Nt("NNNN",Ho),Nt("NNNNN",zo),Ht(["N","NN","NNN","NNNN","NNNNN"],(function(t,e,i,n){var o=i._locale.erasParse(t,n,i._strict);o?g(i).era=o:g(i).invalidEra=t})),Nt("y",Ot),Nt("yy",Ot),Nt("yyy",Ot),Nt("yyyy",Ot),Nt("yo",Go),Ht(["y","yy","yyy","yyyy"],Wt),Ht(["yo"],(function(t,e,i,n){var o;i._locale._eraYearOrdinalRegex&&(o=t.match(i._locale._eraYearOrdinalRegex)),i._locale.eraYearOrdinalParse?e[Wt]=i._locale.eraYearOrdinalParse(t,o):e[Wt]=parseInt(t,10)})),z(0,["gg",2],0,(function(){return this.weekYear()%100})),z(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),Wo("gggg","weekYear"),Wo("ggggg","weekYear"),Wo("GGGG","isoWeekYear"),Wo("GGGGG","isoWeekYear"),nt("weekYear","gg"),nt("isoWeekYear","GG"),at("weekYear",1),at("isoWeekYear",1),Nt("G",Et),Nt("g",Et),Nt("GG",xt,bt),Nt("gg",xt,bt),Nt("GGGG",Tt,wt),Nt("gggg",Tt,wt),Nt("GGGGG",Mt,kt),Nt("ggggg",Mt,kt),zt(["gggg","ggggg","GGGG","GGGGG"],(function(t,e,i,n){e[n.substr(0,2)]=dt(t)})),zt(["gg","GG"],(function(t,e,i,n){e[n]=o.parseTwoDigitYear(t)})),z("Q",0,"Qo","quarter"),nt("quarter","Q"),at("quarter",7),Nt("Q",yt),Ht("Q",(function(t,e){e[Vt]=3*(dt(t)-1)})),z("D",["DD",2],"Do","date"),nt("date","D"),at("date",9),Nt("D",xt),Nt("DD",xt,bt),Nt("Do",(function(t,e){return t?e._dayOfMonthOrdinalParse||e._ordinalParse:e._dayOfMonthOrdinalParseLenient})),Ht(["D","DD"],Ut),Ht("Do",(function(t,e){e[Ut]=dt(t.match(xt)[0])}));var tr=ct("Date",!0);function er(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")}z("DDD",["DDDD",3],"DDDo","dayOfYear"),nt("dayOfYear","DDD"),at("dayOfYear",4),Nt("DDD",Ct),Nt("DDDD",_t),Ht(["DDD","DDDD"],(function(t,e,i){i._dayOfYear=dt(t)})),z("m",["mm",2],0,"minute"),nt("minute","m"),at("minute",14),Nt("m",xt),Nt("mm",xt,bt),Ht(["m","mm"],qt);var ir=ct("Minutes",!1);z("s",["ss",2],0,"second"),nt("second","s"),at("second",15),Nt("s",xt),Nt("ss",xt,bt),Ht(["s","ss"],Zt);var nr,or,rr=ct("Seconds",!1);for(z("S",0,0,(function(){return~~(this.millisecond()/100)})),z(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),z(0,["SSS",3],0,"millisecond"),z(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),z(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),z(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),z(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),z(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),z(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),nt("millisecond","ms"),at("millisecond",16),Nt("S",Ct,yt),Nt("SS",Ct,bt),Nt("SSS",Ct,_t),nr="SSSS";nr.length<=9;nr+="S")Nt(nr,Ot);function sr(t,e){e[$t]=dt(1e3*("0."+t))}for(nr="S";nr.length<=9;nr+="S")Ht(nr,sr);function ar(){return this._isUTC?"UTC":""}function lr(){return this._isUTC?"Coordinated Universal Time":""}or=ct("Milliseconds",!1),z("z",0,0,"zoneAbbr"),z("zz",0,0,"zoneName");var hr=x.prototype;function ur(t){return qi(1e3*t)}function dr(){return qi.apply(null,arguments).parseZone()}function cr(t){return t}hr.add=Ln,hr.calendar=Gn,hr.clone=Bn,hr.diff=$n,hr.endOf=yo,hr.format=eo,hr.from=io,hr.fromNow=no,hr.to=oo,hr.toNow=ro,hr.get=mt,hr.invalidAt=To,hr.isAfter=Wn,hr.isBefore=Vn,hr.isBetween=Un,hr.isSame=Xn,hr.isSameOrAfter=qn,hr.isSameOrBefore=Zn,hr.isValid=So,hr.lang=ao,hr.locale=so,hr.localeData=lo,hr.max=$i,hr.min=Zi,hr.parsingFlags=Co,hr.set=vt,hr.startOf=go,hr.subtract=Nn,hr.toArray=ko,hr.toObject=xo,hr.toDate=wo,hr.toISOString=Qn,hr.inspect=to,"undefined"!=typeof Symbol&&null!=Symbol.for&&(hr[Symbol.for("nodejs.util.inspect.custom")]=function(){return"Moment<"+this.format()+">"}),hr.toJSON=Do,hr.toString=Jn,hr.unix=_o,hr.valueOf=bo,hr.creationData=Mo,hr.eraName=Ao,hr.eraNarrow=Io,hr.eraAbbr=Lo,hr.eraYear=No,hr.year=ge,hr.isLeapYear=ye,hr.weekYear=Vo,hr.isoWeekYear=Uo,hr.quarter=hr.quarters=Qo,hr.month=de,hr.daysInMonth=ce,hr.week=hr.weeks=Oe,hr.isoWeek=hr.isoWeeks=Ee,hr.weeksInYear=Zo,hr.weeksInWeekYear=$o,hr.isoWeeksInYear=Xo,hr.isoWeeksInISOWeekYear=qo,hr.date=tr,hr.day=hr.days=Ve,hr.weekday=Ue,hr.isoWeekday=Xe,hr.dayOfYear=er,hr.hour=hr.hours=oi,hr.minute=hr.minutes=ir,hr.second=hr.seconds=rr,hr.millisecond=hr.milliseconds=or,hr.utcOffset=mn,hr.utc=gn,hr.local=yn,hr.parseZone=bn,hr.hasAlignedHourOffset=_n,hr.isDST=wn,hr.isLocal=xn,hr.isUtcOffset=Dn,hr.isUtc=Sn,hr.isUTC=Sn,hr.zoneAbbr=ar,hr.zoneName=lr,hr.dates=C("dates accessor is deprecated. Use date instead.",tr),hr.months=C("months accessor is deprecated. Use month instead",de),hr.years=C("years accessor is deprecated. Use year instead",ge),hr.zone=C("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",vn),hr.isDSTShifted=C("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",kn);var pr=I.prototype;function fr(t,e,i,n){var o=gi(),r=m().set(n,e);return o[i](r,t)}function mr(t,e,i){if(d(t)&&(e=t,t=void 0),t=t||"",null!=e)return fr(t,e,i,"month");var n,o=[];for(n=0;n<12;n++)o[n]=fr(t,n,i,"month");return o}function vr(t,e,i,n){"boolean"==typeof t?(d(e)&&(i=e,e=void 0),e=e||""):(i=e=t,t=!1,d(e)&&(i=e,e=void 0),e=e||"");var o,r=gi(),s=t?r._week.dow:0,a=[];if(null!=i)return fr(e,(i+s)%7,n,"day");for(o=0;o<7;o++)a[o]=fr(e,(o+s)%7,n,"day");return a}function gr(t,e){return mr(t,e,"months")}function yr(t,e){return mr(t,e,"monthsShort")}function br(t,e,i){return vr(t,e,i,"weekdays")}function _r(t,e,i){return vr(t,e,i,"weekdaysShort")}function wr(t,e,i){return vr(t,e,i,"weekdaysMin")}pr.calendar=N,pr.longDateFormat=X,pr.invalidDate=Z,pr.ordinal=J,pr.preparse=cr,pr.postformat=cr,pr.relativeTime=tt,pr.pastFuture=et,pr.set=P,pr.eras=Oo,pr.erasParse=Eo,pr.erasConvertYear=Po,pr.erasAbbrRegex=Ro,pr.erasNameRegex=Fo,pr.erasNarrowRegex=Yo,pr.months=se,pr.monthsShort=ae,pr.monthsParse=he,pr.monthsRegex=fe,pr.monthsShortRegex=pe,pr.week=Se,pr.firstDayOfYear=Me,pr.firstDayOfWeek=Te,pr.weekdays=He,pr.weekdaysMin=Ge,pr.weekdaysShort=ze,pr.weekdaysParse=We,pr.weekdaysRegex=qe,pr.weekdaysShortRegex=Ze,pr.weekdaysMinRegex=$e,pr.isPM=ii,pr.meridiem=ri,fi("en",{eras:[{since:"0001-01-01",until:1/0,offset:1,name:"Anno Domini",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"Before Christ",narrow:"BC",abbr:"BC"}],dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10;return t+(1===dt(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th")}}),o.lang=C("moment.lang is deprecated. Use moment.locale instead.",fi),o.langData=C("moment.langData is deprecated. Use moment.localeData instead.",gi);var kr=Math.abs;function xr(){var t=this._data;return this._milliseconds=kr(this._milliseconds),this._days=kr(this._days),this._months=kr(this._months),t.milliseconds=kr(t.milliseconds),t.seconds=kr(t.seconds),t.minutes=kr(t.minutes),t.hours=kr(t.hours),t.months=kr(t.months),t.years=kr(t.years),this}function Dr(t,e,i,n){var o=Mn(e,i);return t._milliseconds+=n*o._milliseconds,t._days+=n*o._days,t._months+=n*o._months,t._bubble()}function Sr(t,e){return Dr(this,t,e,1)}function Cr(t,e){return Dr(this,t,e,-1)}function Tr(t){return t<0?Math.floor(t):Math.ceil(t)}function Mr(){var t,e,i,n,o,r=this._milliseconds,s=this._days,a=this._months,l=this._data;return r>=0&&s>=0&&a>=0||r<=0&&s<=0&&a<=0||(r+=864e5*Tr(Er(a)+s),s=0,a=0),l.milliseconds=r%1e3,t=ut(r/1e3),l.seconds=t%60,e=ut(t/60),l.minutes=e%60,i=ut(e/60),l.hours=i%24,s+=ut(i/24),a+=o=ut(Or(s)),s-=Tr(Er(o)),n=ut(a/12),a%=12,l.days=s,l.months=a,l.years=n,this}function Or(t){return 4800*t/146097}function Er(t){return 146097*t/4800}function Pr(t){if(!this.isValid())return NaN;var e,i,n=this._milliseconds;if("month"===(t=ot(t))||"quarter"===t||"year"===t)switch(e=this._days+n/864e5,i=this._months+Or(e),t){case"month":return i;case"quarter":return i/3;case"year":return i/12}else switch(e=this._days+Math.round(Er(this._months)),t){case"week":return e/7+n/6048e5;case"day":return e+n/864e5;case"hour":return 24*e+n/36e5;case"minute":return 1440*e+n/6e4;case"second":return 86400*e+n/1e3;case"millisecond":return Math.floor(864e5*e)+n;default:throw new Error("Unknown unit "+t)}}function Ar(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*dt(this._months/12):NaN}function Ir(t){return function(){return this.as(t)}}var Lr=Ir("ms"),Nr=Ir("s"),Fr=Ir("m"),Rr=Ir("h"),Yr=Ir("d"),jr=Ir("w"),Hr=Ir("M"),zr=Ir("Q"),Gr=Ir("y");function Br(){return Mn(this)}function Wr(t){return t=ot(t),this.isValid()?this[t+"s"]():NaN}function Vr(t){return function(){return this.isValid()?this._data[t]:NaN}}var Ur=Vr("milliseconds"),Xr=Vr("seconds"),qr=Vr("minutes"),Zr=Vr("hours"),$r=Vr("days"),Kr=Vr("months"),Jr=Vr("years");function Qr(){return ut(this.days()/7)}var ts=Math.round,es={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function is(t,e,i,n,o){return o.relativeTime(e||1,!!i,t,n)}function ns(t,e,i,n){var o=Mn(t).abs(),r=ts(o.as("s")),s=ts(o.as("m")),a=ts(o.as("h")),l=ts(o.as("d")),h=ts(o.as("M")),u=ts(o.as("w")),d=ts(o.as("y")),c=r<=i.ss&&["s",r]||r0,c[4]=n,is.apply(null,c)}function os(t){return void 0===t?ts:"function"==typeof t&&(ts=t,!0)}function rs(t,e){return void 0!==es[t]&&(void 0===e?es[t]:(es[t]=e,"s"===t&&(es.ss=e-1),!0))}function ss(t,e){if(!this.isValid())return this.localeData().invalidDate();var i,n,o=!1,r=es;return"object"==typeof t&&(e=t,t=!1),"boolean"==typeof t&&(o=t),"object"==typeof e&&(r=Object.assign({},es,e),null!=e.s&&null==e.ss&&(r.ss=e.s-1)),n=ns(this,!o,r,i=this.localeData()),o&&(n=i.pastFuture(+this,n)),i.postformat(n)}var as=Math.abs;function ls(t){return(t>0)-(t<0)||+t}function hs(){if(!this.isValid())return this.localeData().invalidDate();var t,e,i,n,o,r,s,a,l=as(this._milliseconds)/1e3,h=as(this._days),u=as(this._months),d=this.asSeconds();return d?(t=ut(l/60),e=ut(t/60),l%=60,t%=60,i=ut(u/12),u%=12,n=l?l.toFixed(3).replace(/\.?0+$/,""):"",o=d<0?"-":"",r=ls(this._months)!==ls(d)?"-":"",s=ls(this._days)!==ls(d)?"-":"",a=ls(this._milliseconds)!==ls(d)?"-":"",o+"P"+(i?r+i+"Y":"")+(u?r+u+"M":"")+(h?s+h+"D":"")+(e||t||l?"T":"")+(e?a+e+"H":"")+(t?a+t+"M":"")+(l?a+n+"S":"")):"P0D"}var us=sn.prototype;return us.isValid=on,us.abs=xr,us.add=Sr,us.subtract=Cr,us.as=Pr,us.asMilliseconds=Lr,us.asSeconds=Nr,us.asMinutes=Fr,us.asHours=Rr,us.asDays=Yr,us.asWeeks=jr,us.asMonths=Hr,us.asQuarters=zr,us.asYears=Gr,us.valueOf=Ar,us._bubble=Mr,us.clone=Br,us.get=Wr,us.milliseconds=Ur,us.seconds=Xr,us.minutes=qr,us.hours=Zr,us.days=$r,us.weeks=Qr,us.months=Kr,us.years=Jr,us.humanize=ss,us.toISOString=hs,us.toString=hs,us.toJSON=hs,us.locale=so,us.localeData=lo,us.toIsoString=C("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",hs),us.lang=ao,z("X",0,0,"unix"),z("x",0,0,"valueOf"),Nt("x",Et),Nt("X",It),Ht("X",(function(t,e,i){i._d=new Date(1e3*parseFloat(t))})),Ht("x",(function(t,e,i){i._d=new Date(dt(t))})),//! moment.js
+o.version="2.29.1",r(qi),o.fn=hr,o.min=Ji,o.max=Qi,o.now=tn,o.utc=m,o.unix=ur,o.months=gr,o.isDate=c,o.locale=fi,o.invalid=b,o.duration=Mn,o.isMoment=D,o.weekdays=br,o.parseZone=dr,o.localeData=gi,o.isDuration=an,o.monthsShort=yr,o.weekdaysMin=wr,o.defineLocale=mi,o.updateLocale=vi,o.locales=yi,o.weekdaysShort=_r,o.normalizeUnits=ot,o.relativeTimeRounding=os,o.relativeTimeThreshold=rs,o.calendarFormat=zn,o.prototype=hr,o.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},o}()}(o);var r=o.exports;!function(t){function e(t,e,i,n){var o={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[t+" Tage",t+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[t+" Monate",t+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[t+" Jahre",t+" Jahren"]};return e?o[i][0]:o[i][1]}t.defineLocale("de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:e,mm:"%d Minuten",h:e,hh:"%d Stunden",d:e,dd:e,w:e,ww:"%d Wochen",M:e,MM:e,y:e,yy:e},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(o.exports),function(t){var e="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),i="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),n=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i],o=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;t.defineLocale("es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(t,n){return t?/-MMM-/.test(n)?i[t.month()]:e[t.month()]:e},monthsRegex:o,monthsShortRegex:o,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:n,longMonthsParse:n,shortMonthsParse:n,weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4},invalidDate:"Fecha inválida"})}(o.exports),function(t){var e=/(janv\.?|févr\.?|mars|avr\.?|mai|juin|juil\.?|août|sept\.?|oct\.?|nov\.?|déc\.?|janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre)/i,i=[/^janv/i,/^févr/i,/^mars/i,/^avr/i,/^mai/i,/^juin/i,/^juil/i,/^août/i,/^sept/i,/^oct/i,/^nov/i,/^déc/i];t.defineLocale("fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),monthsRegex:e,monthsShortRegex:e,monthsStrictRegex:/^(janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre)/i,monthsShortStrictRegex:/(janv\.?|févr\.?|mars|avr\.?|mai|juin|juil\.?|août|sept\.?|oct\.?|nov\.?|déc\.?)/i,monthsParse:i,longMonthsParse:i,shortMonthsParse:i,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd’hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",w:"une semaine",ww:"%d semaines",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|)/,ordinal:function(t,e){switch(e){case"D":return t+(1===t?"er":"");default:case"M":case"Q":case"DDD":case"d":return t+(1===t?"er":"e");case"w":case"W":return t+(1===t?"re":"e")}},week:{dow:1,doy:4}})}(o.exports),function(t){t.defineLocale("it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"domenica_lunedì_martedì_mercoledì_giovedì_venerdì_sabato".split("_"),weekdaysShort:"dom_lun_mar_mer_gio_ven_sab".split("_"),weekdaysMin:"do_lu_ma_me_gi_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:function(){return"[Oggi a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},nextDay:function(){return"[Domani a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},nextWeek:function(){return"dddd [a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},lastDay:function(){return"[Ieri a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},lastWeek:function(){switch(this.day()){case 0:return"[La scorsa] dddd [a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT";default:return"[Lo scorso] dddd [a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"}},sameElse:"L"},relativeTime:{future:"tra %s",past:"%s fa",s:"alcuni secondi",ss:"%d secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",w:"una settimana",ww:"%d settimane",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%dº",week:{dow:1,doy:4}})}(o.exports),function(t){t.defineLocale("ja",{eras:[{since:"2019-05-01",offset:1,name:"令和",narrow:"㋿",abbr:"R"},{since:"1989-01-08",until:"2019-04-30",offset:1,name:"平成",narrow:"㍻",abbr:"H"},{since:"1926-12-25",until:"1989-01-07",offset:1,name:"昭和",narrow:"㍼",abbr:"S"},{since:"1912-07-30",until:"1926-12-24",offset:1,name:"大正",narrow:"㍽",abbr:"T"},{since:"1873-01-01",until:"1912-07-29",offset:6,name:"明治",narrow:"㍾",abbr:"M"},{since:"0001-01-01",until:"1873-12-31",offset:1,name:"西暦",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"紀元前",narrow:"BC",abbr:"BC"}],eraYearOrdinalRegex:/(元|\d+)年/,eraYearOrdinalParse:function(t,e){return"元"===e[1]?1:parseInt(e[1]||t,10)},months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日 dddd HH:mm",l:"YYYY/MM/DD",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日(ddd) HH:mm"},meridiemParse:/午前|午後/i,isPM:function(t){return"午後"===t},meridiem:function(t,e,i){return t<12?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:function(t){return t.week()!==this.week()?"[来週]dddd LT":"dddd LT"},lastDay:"[昨日] LT",lastWeek:function(t){return this.week()!==t.week()?"[先週]dddd LT":"dddd LT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}日/,ordinal:function(t,e){switch(e){case"y":return 1===t?"元年":t+"年";case"d":case"D":case"DDD":return t+"日";default:return t}},relativeTime:{future:"%s後",past:"%s前",s:"数秒",ss:"%d秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}})}(o.exports),function(t){var e="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),i="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),n=[/^jan/i,/^feb/i,/^maart|mrt.?$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i],o=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i;t.defineLocale("nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(t,n){return t?/-MMM-/.test(n)?i[t.month()]:e[t.month()]:e},monthsRegex:o,monthsShortRegex:o,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:n,longMonthsParse:n,shortMonthsParse:n,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",w:"één week",ww:"%d weken",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(t){return t+(1===t||8===t||t>=20?"ste":"de")},week:{dow:1,doy:4}})}(o.exports),function(t){var e="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),i="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_"),n=[/^sty/i,/^lut/i,/^mar/i,/^kwi/i,/^maj/i,/^cze/i,/^lip/i,/^sie/i,/^wrz/i,/^paź/i,/^lis/i,/^gru/i];function o(t){return t%10<5&&t%10>1&&~~(t/10)%10!=1}function r(t,e,i){var n=t+" ";switch(i){case"ss":return n+(o(t)?"sekundy":"sekund");case"m":return e?"minuta":"minutę";case"mm":return n+(o(t)?"minuty":"minut");case"h":return e?"godzina":"godzinę";case"hh":return n+(o(t)?"godziny":"godzin");case"ww":return n+(o(t)?"tygodnie":"tygodni");case"MM":return n+(o(t)?"miesiące":"miesięcy");case"yy":return n+(o(t)?"lata":"lat")}}t.defineLocale("pl",{months:function(t,n){return t?/D MMMM/.test(n)?i[t.month()]:e[t.month()]:e},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),monthsParse:n,longMonthsParse:n,shortMonthsParse:n,weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"ndz_pon_wt_śr_czw_pt_sob".split("_"),weekdaysMin:"Nd_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:function(){switch(this.day()){case 0:return"[W niedzielę o] LT";case 2:return"[We wtorek o] LT";case 3:return"[W środę o] LT";case 6:return"[W sobotę o] LT";default:return"[W] dddd [o] LT"}},lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",ss:r,m:r,mm:r,h:r,hh:r,d:"1 dzień",dd:"%d dni",w:"tydzień",ww:r,M:"miesiąc",MM:r,y:"rok",yy:r},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})}(o.exports),function(t){function e(t,e,i){var n,o;return"m"===i?e?"минута":"минуту":t+" "+(n=+t,o={ss:e?"секунда_секунды_секунд":"секунду_секунды_секунд",mm:e?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",ww:"неделя_недели_недель",MM:"месяц_месяца_месяцев",yy:"год_года_лет"}[i].split("_"),n%10==1&&n%100!=11?o[0]:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?o[1]:o[2])}var i=[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[йя]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i];t.defineLocale("ru",{months:{format:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_"),standalone:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_")},monthsShort:{format:"янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.".split("_"),standalone:"янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.".split("_")},weekdays:{standalone:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),format:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_"),isFormat:/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?] ?dddd/},weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:i,longMonthsParse:i,shortMonthsParse:i,monthsRegex:/^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,monthsShortRegex:/^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,monthsStrictRegex:/^(январ[яь]|феврал[яь]|марта?|апрел[яь]|ма[яй]|июн[яь]|июл[яь]|августа?|сентябр[яь]|октябр[яь]|ноябр[яь]|декабр[яь])/i,monthsShortStrictRegex:/^(янв\.|февр?\.|мар[т.]|апр\.|ма[яй]|июн[ья.]|июл[ья.]|авг\.|сент?\.|окт\.|нояб?\.|дек\.)/i,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., H:mm",LLLL:"dddd, D MMMM YYYY г., H:mm"},calendar:{sameDay:"[Сегодня, в] LT",nextDay:"[Завтра, в] LT",lastDay:"[Вчера, в] LT",nextWeek:function(t){if(t.week()===this.week())return 2===this.day()?"[Во] dddd, [в] LT":"[В] dddd, [в] LT";switch(this.day()){case 0:return"[В следующее] dddd, [в] LT";case 1:case 2:case 4:return"[В следующий] dddd, [в] LT";case 3:case 5:case 6:return"[В следующую] dddd, [в] LT"}},lastWeek:function(t){if(t.week()===this.week())return 2===this.day()?"[Во] dddd, [в] LT":"[В] dddd, [в] LT";switch(this.day()){case 0:return"[В прошлое] dddd, [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd, [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd, [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",ss:e,m:e,mm:e,h:"час",hh:e,d:"день",dd:e,w:"неделя",ww:e,M:"месяц",MM:e,y:"год",yy:e},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(t){return/^(дня|вечера)$/.test(t)},meridiem:function(t,e,i){return t<4?"ночи":t<12?"утра":t<17?"дня":"вечера"},dayOfMonthOrdinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(t,e){switch(e){case"M":case"d":case"DDD":return t+"-й";case"D":return t+"-го";case"w":case"W":return t+"-я";default:return t}},week:{dow:1,doy:4}})}(o.exports),function(t){function e(t,e,i){var n,o;return"m"===i?e?"хвилина":"хвилину":"h"===i?e?"година":"годину":t+" "+(n=+t,o={ss:e?"секунда_секунди_секунд":"секунду_секунди_секунд",mm:e?"хвилина_хвилини_хвилин":"хвилину_хвилини_хвилин",hh:e?"година_години_годин":"годину_години_годин",dd:"день_дні_днів",MM:"місяць_місяці_місяців",yy:"рік_роки_років"}[i].split("_"),n%10==1&&n%100!=11?o[0]:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?o[1]:o[2])}function i(t){return function(){return t+"о"+(11===this.hours()?"б":"")+"] LT"}}t.defineLocale("uk",{months:{format:"січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня".split("_"),standalone:"січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень".split("_")},monthsShort:"січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд".split("_"),weekdays:function(t,e){var i={nominative:"неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота".split("_"),accusative:"неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу".split("_"),genitive:"неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи".split("_")};return!0===t?i.nominative.slice(1,7).concat(i.nominative.slice(0,1)):t?i[/(\[[ВвУу]\]) ?dddd/.test(e)?"accusative":/\[?(?:минулої|наступної)? ?\] ?dddd/.test(e)?"genitive":"nominative"][t.day()]:i.nominative},weekdaysShort:"нд_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY р.",LLL:"D MMMM YYYY р., HH:mm",LLLL:"dddd, D MMMM YYYY р., HH:mm"},calendar:{sameDay:i("[Сьогодні "),nextDay:i("[Завтра "),lastDay:i("[Вчора "),nextWeek:i("[У] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return i("[Минулої] dddd [").call(this);case 1:case 2:case 4:return i("[Минулого] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"за %s",past:"%s тому",s:"декілька секунд",ss:e,m:e,mm:e,h:"годину",hh:e,d:"день",dd:e,M:"місяць",MM:e,y:"рік",yy:e},meridiemParse:/ночі|ранку|дня|вечора/,isPM:function(t){return/^(дня|вечора)$/.test(t)},meridiem:function(t,e,i){return t<4?"ночі":t<12?"ранку":t<17?"дня":"вечора"},dayOfMonthOrdinalParse:/\d{1,2}-(й|го)/,ordinal:function(t,e){switch(e){case"M":case"d":case"DDD":case"w":case"W":return t+"-й";case"D":return t+"-го";default:return t}},week:{dow:1,doy:7}})}(o.exports);var s=Function.prototype,a=s.bind,l=s.call,h=a&&a.bind(l),u=a?function(t){return t&&h(l,t)}:function(t){return t&&function(){return l.apply(t,arguments)}},d=Math.ceil,c=Math.floor,p=function(t){var e=+t;return e!=e||0===e?0:(e>0?c:d)(e)},f=function(t){return t&&t.Math==Math&&t},m=f("object"==typeof globalThis&&globalThis)||f("object"==typeof window&&window)||f("object"==typeof self&&self)||f("object"==typeof e&&e)||function(){return this}()||Function("return this")(),v={exports:{}},g=m,y=Object.defineProperty,b=function(t,e){try{y(g,t,{value:e,configurable:!0,writable:!0})}catch(i){g[t]=e}return e},_="__core-js_shared__",w=m[_]||b(_,{}),k=w;(v.exports=function(t,e){return k[t]||(k[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.19.1",mode:"pure",copyright:"© 2021 Denis Pushkarev (zloirock.ru)"});var x,D,S=m.TypeError,C=function(t){if(null==t)throw S("Can't call method on "+t);return t},T=C,M=m.Object,O=function(t){return M(T(t))},E=O,P=u({}.hasOwnProperty),A=Object.hasOwn||function(t,e){return P(E(t),e)},I=u,L=0,N=Math.random(),F=I(1..toString),R=function(t){return"Symbol("+(void 0===t?"":t)+")_"+F(++L+N,36)},Y={},j=function(t){return"function"==typeof t},H=Y,z=m,G=j,B=function(t){return G(t)?t:void 0},W=function(t,e){return arguments.length<2?B(H[t])||B(z[t]):H[t]&&H[t][e]||z[t]&&z[t][e]},V=W("navigator","userAgent")||"",U=m,X=V,q=U.process,Z=U.Deno,$=q&&q.versions||Z&&Z.version,K=$&&$.v8;K&&(D=(x=K.split("."))[0]>0&&x[0]<4?1:+(x[0]+x[1])),!D&&X&&(!(x=X.match(/Edge\/(\d+)/))||x[1]>=74)&&(x=X.match(/Chrome\/(\d+)/))&&(D=+x[1]);var J=D,Q=function(t){try{return!!t()}catch(t){return!0}},tt=J,et=Q,it=!!Object.getOwnPropertySymbols&&!et((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&tt&&tt<41})),nt=it&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,ot=m,rt=v.exports,st=A,at=R,lt=it,ht=nt,ut=rt("wks"),dt=ot.Symbol,ct=dt&&dt.for,pt=ht?dt:dt&&dt.withoutSetter||at,ft=function(t){if(!st(ut,t)||!lt&&"string"!=typeof ut[t]){var e="Symbol."+t;lt&&st(dt,t)?ut[t]=dt[t]:ut[t]=ht&&ct?ct(e):pt(e)}return ut[t]},mt={};mt[ft("toStringTag")]="z";var vt="[object z]"===String(mt),gt=u,yt=gt({}.toString),bt=gt("".slice),_t=function(t){return bt(yt(t),8,-1)},wt=m,kt=vt,xt=j,Dt=_t,St=ft("toStringTag"),Ct=wt.Object,Tt="Arguments"==Dt(function(){return arguments}()),Mt=kt?Dt:function(t){var e,i,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(i=function(t,e){try{return t[e]}catch(t){}}(e=Ct(t),St))?i:Tt?Dt(e):"Object"==(n=Dt(e))&&xt(e.callee)?"Arguments":n},Ot=Mt,Et=m.String,Pt=function(t){if("Symbol"===Ot(t))throw TypeError("Cannot convert a Symbol value to a string");return Et(t)},At=u,It=p,Lt=Pt,Nt=C,Ft=At("".charAt),Rt=At("".charCodeAt),Yt=At("".slice),jt=function(t){return function(e,i){var n,o,r=Lt(Nt(e)),s=It(i),a=r.length;return s<0||s>=a?t?"":void 0:(n=Rt(r,s))<55296||n>56319||s+1===a||(o=Rt(r,s+1))<56320||o>57343?t?Ft(r,s):n:t?Yt(r,s,s+2):o-56320+(n-55296<<10)+65536}},Ht={codeAt:jt(!1),charAt:jt(!0)},zt=j,Gt=w,Bt=u(Function.toString);zt(Gt.inspectSource)||(Gt.inspectSource=function(t){return Bt(t)});var Wt=Gt.inspectSource,Vt=j,Ut=Wt,Xt=m.WeakMap,qt=Vt(Xt)&&/native code/.test(Ut(Xt)),Zt=j,$t=function(t){return"object"==typeof t?null!==t:Zt(t)},Kt=!Q((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),Jt={},Qt=$t,te=m.document,ee=Qt(te)&&Qt(te.createElement),ie=function(t){return ee?te.createElement(t):{}},ne=ie,oe=!Kt&&!Q((function(){return 7!=Object.defineProperty(ne("div"),"a",{get:function(){return 7}}).a})),re=m,se=$t,ae=re.String,le=re.TypeError,he=function(t){if(se(t))return t;throw le(ae(t)+" is not an object")},ue=Function.prototype.call,de=ue.bind?ue.bind(ue):function(){return ue.apply(ue,arguments)},ce=u({}.isPrototypeOf),pe=W,fe=j,me=ce,ve=nt,ge=m.Object,ye=ve?function(t){return"symbol"==typeof t}:function(t){var e=pe("Symbol");return fe(e)&&me(e.prototype,ge(t))},be=m.String,_e=function(t){try{return be(t)}catch(t){return"Object"}},we=j,ke=_e,xe=m.TypeError,De=function(t){if(we(t))return t;throw xe(ke(t)+" is not a function")},Se=De,Ce=function(t,e){var i=t[e];return null==i?void 0:Se(i)},Te=de,Me=j,Oe=$t,Ee=m.TypeError,Pe=de,Ae=$t,Ie=ye,Le=Ce,Ne=function(t,e){var i,n;if("string"===e&&Me(i=t.toString)&&!Oe(n=Te(i,t)))return n;if(Me(i=t.valueOf)&&!Oe(n=Te(i,t)))return n;if("string"!==e&&Me(i=t.toString)&&!Oe(n=Te(i,t)))return n;throw Ee("Can't convert object to primitive value")},Fe=ft,Re=m.TypeError,Ye=Fe("toPrimitive"),je=function(t,e){if(!Ae(t)||Ie(t))return t;var i,n=Le(t,Ye);if(n){if(void 0===e&&(e="default"),i=Pe(n,t,e),!Ae(i)||Ie(i))return i;throw Re("Can't convert object to primitive value")}return void 0===e&&(e="number"),Ne(t,e)},He=ye,ze=function(t){var e=je(t,"string");return He(e)?e:e+""},Ge=Kt,Be=oe,We=he,Ve=ze,Ue=m.TypeError,Xe=Object.defineProperty;Jt.f=Ge?Xe:function(t,e,i){if(We(t),e=Ve(e),We(i),Be)try{return Xe(t,e,i)}catch(t){}if("get"in i||"set"in i)throw Ue("Accessors not supported");return"value"in i&&(t[e]=i.value),t};var qe,Ze,$e,Ke=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},Je=Jt,Qe=Ke,ti=Kt?function(t,e,i){return Je.f(t,e,Qe(1,i))}:function(t,e,i){return t[e]=i,t},ei=v.exports,ii=R,ni=ei("keys"),oi=function(t){return ni[t]||(ni[t]=ii(t))},ri={},si=qt,ai=m,li=u,hi=$t,ui=ti,di=A,ci=w,pi=oi,fi=ri,mi="Object already initialized",vi=ai.TypeError,gi=ai.WeakMap;if(si||ci.state){var yi=ci.state||(ci.state=new gi),bi=li(yi.get),_i=li(yi.has),wi=li(yi.set);qe=function(t,e){if(_i(yi,t))throw new vi(mi);return e.facade=t,wi(yi,t,e),e},Ze=function(t){return bi(yi,t)||{}},$e=function(t){return _i(yi,t)}}else{var ki=pi("state");fi[ki]=!0,qe=function(t,e){if(di(t,ki))throw new vi(mi);return e.facade=t,ui(t,ki,e),e},Ze=function(t){return di(t,ki)?t[ki]:{}},$e=function(t){return di(t,ki)}}var xi={set:qe,get:Ze,has:$e,enforce:function(t){return $e(t)?Ze(t):qe(t,{})},getterFor:function(t){return function(e){var i;if(!hi(e)||(i=Ze(e)).type!==t)throw vi("Incompatible receiver, "+t+" required");return i}}},Di=Function.prototype,Si=Di.apply,Ci=Di.bind,Ti=Di.call,Mi="object"==typeof Reflect&&Reflect.apply||(Ci?Ti.bind(Si):function(){return Ti.apply(Si,arguments)}),Oi={},Ei={},Pi={}.propertyIsEnumerable,Ai=Object.getOwnPropertyDescriptor,Ii=Ai&&!Pi.call({1:2},1);Ei.f=Ii?function(t){var e=Ai(this,t);return!!e&&e.enumerable}:Pi;var Li=u,Ni=Q,Fi=_t,Ri=m.Object,Yi=Li("".split),ji=Ni((function(){return!Ri("z").propertyIsEnumerable(0)}))?function(t){return"String"==Fi(t)?Yi(t,""):Ri(t)}:Ri,Hi=ji,zi=C,Gi=function(t){return Hi(zi(t))},Bi=Kt,Wi=de,Vi=Ei,Ui=Ke,Xi=Gi,qi=ze,Zi=A,$i=oe,Ki=Object.getOwnPropertyDescriptor;Oi.f=Bi?Ki:function(t,e){if(t=Xi(t),e=qi(e),$i)try{return Ki(t,e)}catch(t){}if(Zi(t,e))return Ui(!Wi(Vi.f,t,e),t[e])};var Ji,Qi=Q,tn=j,en=/#|\.prototype\./,nn=function(t,e){var i=rn[on(t)];return i==an||i!=sn&&(tn(e)?Qi(e):!!e)},on=nn.normalize=function(t){return String(t).replace(en,".").toLowerCase()},rn=nn.data={},sn=nn.NATIVE="N",an=nn.POLYFILL="P",ln=nn,hn=De,un=u(u.bind),dn=function(t,e){return hn(t),void 0===e?t:un?un(t,e):function(){return t.apply(e,arguments)}},cn=m,pn=Mi,fn=u,mn=j,vn=Oi.f,gn=ln,yn=Y,bn=dn,_n=ti,wn=A,kn=function(t){var e=function(i,n,o){if(this instanceof e){switch(arguments.length){case 0:return new t;case 1:return new t(i);case 2:return new t(i,n)}return new t(i,n,o)}return pn(t,this,arguments)};return e.prototype=t.prototype,e},xn=function(t,e){var i,n,o,r,s,a,l,h,u=t.target,d=t.global,c=t.stat,p=t.proto,f=d?cn:c?cn[u]:(cn[u]||{}).prototype,m=d?yn:yn[u]||_n(yn,u,{})[u],v=m.prototype;for(o in e)i=!gn(d?o:u+(c?".":"#")+o,t.forced)&&f&&wn(f,o),s=m[o],i&&(a=t.noTargetGet?(h=vn(f,o))&&h.value:f[o]),r=i&&a?a:e[o],i&&typeof s==typeof r||(l=t.bind&&i?bn(r,cn):t.wrap&&i?kn(r):p&&mn(r)?fn(r):r,(t.sham||r&&r.sham||s&&s.sham)&&_n(l,"sham",!0),_n(m,o,l),p&&(wn(yn,n=u+"Prototype")||_n(yn,n,{}),_n(yn[n],o,r),t.real&&v&&!v[o]&&_n(v,o,r)))},Dn=Kt,Sn=A,Cn=Function.prototype,Tn=Dn&&Object.getOwnPropertyDescriptor,Mn=Sn(Cn,"name"),On={EXISTS:Mn,PROPER:Mn&&"something"===function(){}.name,CONFIGURABLE:Mn&&(!Dn||Dn&&Tn(Cn,"name").configurable)},En=p,Pn=Math.max,An=Math.min,In=function(t,e){var i=En(t);return i<0?Pn(i+e,0):An(i,e)},Ln=p,Nn=Math.min,Fn=function(t){return t>0?Nn(Ln(t),9007199254740991):0},Rn=function(t){return Fn(t.length)},Yn=Gi,jn=In,Hn=Rn,zn=function(t){return function(e,i,n){var o,r=Yn(e),s=Hn(r),a=jn(n,s);if(t&&i!=i){for(;s>a;)if((o=r[a++])!=o)return!0}else for(;s>a;a++)if((t||a in r)&&r[a]===i)return t||a||0;return!t&&-1}},Gn={includes:zn(!0),indexOf:zn(!1)},Bn=A,Wn=Gi,Vn=Gn.indexOf,Un=ri,Xn=u([].push),qn=function(t,e){var i,n=Wn(t),o=0,r=[];for(i in n)!Bn(Un,i)&&Bn(n,i)&&Xn(r,i);for(;e.length>o;)Bn(n,i=e[o++])&&(~Vn(r,i)||Xn(r,i));return r},Zn=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],$n=qn,Kn=Zn,Jn=Object.keys||function(t){return $n(t,Kn)},Qn=Jt,to=he,eo=Gi,io=Jn,no=Kt?Object.defineProperties:function(t,e){to(t);for(var i,n=eo(e),o=io(e),r=o.length,s=0;r>s;)Qn.f(t,i=o[s++],n[i]);return t},oo=W("document","documentElement"),ro=he,so=no,ao=Zn,lo=ri,ho=oo,uo=ie,co=oi("IE_PROTO"),po=function(){},fo=function(t){return"
+
+
diff --git a/situation-overview-web/src/api/user.js b/situation-overview-web/src/api/user.js
new file mode 100644
index 00000000..f03218be
--- /dev/null
+++ b/situation-overview-web/src/api/user.js
@@ -0,0 +1,21 @@
+import request from './request'
+
+export const getUserList = () => {
+ return request.get('/users')
+}
+
+export const getUserById = (id) => {
+ return request.get(`/users/${id}`)
+}
+
+export const createUser = (data) => {
+ return request.post('/users', data)
+}
+
+export const updateUser = (id, data) => {
+ return request.put(`/users/${id}`, data)
+}
+
+export const deleteUser = (id) => {
+ return request.delete(`/users/${id}`)
+}
diff --git a/situation-overview-web/src/common/actionStatus.js b/situation-overview-web/src/common/actionStatus.js
new file mode 100644
index 00000000..ed080ebf
--- /dev/null
+++ b/situation-overview-web/src/common/actionStatus.js
@@ -0,0 +1,7 @@
+export default {
+ 0: "待签收",
+ 10: "待分析",
+ 20: "CEH中",
+ 25: "CEH完成",
+ 23: '已终止',
+};
diff --git a/situation-overview-web/src/common/chatData.js b/situation-overview-web/src/common/chatData.js
new file mode 100644
index 00000000..f7c1e8a1
--- /dev/null
+++ b/situation-overview-web/src/common/chatData.js
@@ -0,0 +1,12 @@
+export const deepCopy = (data) => {
+ return JSON.parse(JSON.stringify(data));
+}
+
+export const uniqueById = (array) => {
+ return array.reduce((acc, item) => {
+ if (!acc.find(x => x.id === item.id)) {
+ acc.push(item);
+ }
+ return acc;
+ }, []);
+};
\ No newline at end of file
diff --git a/situation-overview-web/src/common/sourceType.js b/situation-overview-web/src/common/sourceType.js
new file mode 100644
index 00000000..83614d0b
--- /dev/null
+++ b/situation-overview-web/src/common/sourceType.js
@@ -0,0 +1,4 @@
+export default {
+ 0: "本系统创建",
+ 1: "来自于B3",
+};
diff --git a/situation-overview-web/src/common/taskLevel.js b/situation-overview-web/src/common/taskLevel.js
new file mode 100644
index 00000000..87479fde
--- /dev/null
+++ b/situation-overview-web/src/common/taskLevel.js
@@ -0,0 +1,5 @@
+export default {
+ 1: "一般",
+ 2: "紧急",
+ 3: "加急",
+};
diff --git a/situation-overview-web/src/common/taskStatus.js b/situation-overview-web/src/common/taskStatus.js
new file mode 100644
index 00000000..e61665c3
--- /dev/null
+++ b/situation-overview-web/src/common/taskStatus.js
@@ -0,0 +1,16 @@
+export default {
+ 0: "待签收",
+ 10: "待分析",
+ 20: "分析中",
+ 25: "分析完成已提交",
+ 30: "已分析待审核",
+ 40: "分析审核通过",
+ 50: "分析审核未通过",
+ 60: "行动已下发待签收",
+ 70: "行动已签收待策划",
+ 80: "行动策划已提交",
+ 90: "作战方案已提交",
+ 100: "作战方案待审核",
+ 110: "作战方案已审核",
+ 120: "作战方案未通过",
+};
diff --git a/situation-overview-web/src/components/AIChatExtract/index.vue b/situation-overview-web/src/components/AIChatExtract/index.vue
new file mode 100644
index 00000000..699de70d
--- /dev/null
+++ b/situation-overview-web/src/components/AIChatExtract/index.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/ActionOverview.vue b/situation-overview-web/src/components/ActionOverview.vue
new file mode 100644
index 00000000..f2d7dcb7
--- /dev/null
+++ b/situation-overview-web/src/components/ActionOverview.vue
@@ -0,0 +1,702 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/ActionPlanning.vue b/situation-overview-web/src/components/ActionPlanning.vue
new file mode 100644
index 00000000..5bbd66f0
--- /dev/null
+++ b/situation-overview-web/src/components/ActionPlanning.vue
@@ -0,0 +1,448 @@
+
+
+
+
+
+ {{ taskData.taskName }}
+
+
+ 任务时间:
+ {{
+ dayjs(taskData.createTime).format("YYYY-MM-DD HH:mm:ss")
+ }}
+ {{
+ taskLevel[taskData.taskLevel]
+ }}
+
+
+
+ {{ allShow ? "收起" : "查看全文" }}
+
+
+
+
任务行动
+
+
+
+
+ {{ actionStatus[item.actionStatus] }}
+
+
+
+ {{ item.actionName }}
+
+
+
+
+
+
+
+
+
桃色与军纪舆论裂变攻台军形象
+
+ 支撑性目的:利用桃色新闻与军纪涣散议题,瓦解台军社会形象与民众支持基础,引发线上网红、大V等对台军……
+
+
+
+
+
+ {{ index + 1 }} . {{ item.title }}
+
+
+
+
+
行为体:{{ item.actor }}
+
预期行为:{{ item.behavior }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/ActionPlanning/PerformanceEvaluation.vue b/situation-overview-web/src/components/ActionPlanning/PerformanceEvaluation.vue
new file mode 100644
index 00000000..60406bb8
--- /dev/null
+++ b/situation-overview-web/src/components/ActionPlanning/PerformanceEvaluation.vue
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/HelloWorld.vue b/situation-overview-web/src/components/HelloWorld.vue
new file mode 100644
index 00000000..546ebbc6
--- /dev/null
+++ b/situation-overview-web/src/components/HelloWorld.vue
@@ -0,0 +1,43 @@
+
+
+
+ {{ msg }}
+
+
+
count is {{ count }}
+
+ Edit
+ components/HelloWorld.vue to test HMR
+
+
+
+
+ Check out
+ create-vue , the official Vue + Vite starter
+
+
+ Learn more about IDE Support for Vue in the
+ Vue Docs Scaling up Guide .
+
+ Click on the Vite and Vue logos to learn more
+
+
+
diff --git a/situation-overview-web/src/components/InfoChatView/index.vue b/situation-overview-web/src/components/InfoChatView/index.vue
new file mode 100644
index 00000000..afc8509a
--- /dev/null
+++ b/situation-overview-web/src/components/InfoChatView/index.vue
@@ -0,0 +1,549 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ item.data['q'] }}
+
+
+
+
+
+
+ {{ i + 1 }} {{ item.title }}
+
+
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ detailData.title }}
+
+
+
+
+
+
+ 跳转链接
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ item.data['q'] }}
+
+
+
+
+
+
+ {{ i + 1 }} {{ item.title }}
+
+
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/Overview/index.vue b/situation-overview-web/src/components/Overview/index.vue
new file mode 100644
index 00000000..548294e3
--- /dev/null
+++ b/situation-overview-web/src/components/Overview/index.vue
@@ -0,0 +1,206 @@
+
+
+
+
ZZ任务
+
+
+
+
+ 任务名称任务名称任务名称任务名称任务名称任务名称任务名称任务名称任务名称任务名称任务名称任务名称任务名称任务名称任务名称
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/SentimentAnalysis.vue b/situation-overview-web/src/components/SentimentAnalysis.vue
new file mode 100644
index 00000000..550a5a46
--- /dev/null
+++ b/situation-overview-web/src/components/SentimentAnalysis.vue
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 全部
+ 我的
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/SentimentAnalysis/AssessmentContent.vue b/situation-overview-web/src/components/SentimentAnalysis/AssessmentContent.vue
new file mode 100644
index 00000000..a24e0e32
--- /dev/null
+++ b/situation-overview-web/src/components/SentimentAnalysis/AssessmentContent.vue
@@ -0,0 +1,359 @@
+
+
+
+
+
+
+ 上级意图领会
+
+
+ {{ situationData['上级意图领会'] || '暂无数据' }}
+
+
+
+
+
+
+
+ 口径指导与限制
+
+
+
+ {{ situationData['口径指导与限制']['关键口径总结'] || '暂无数据' }}
+
+
+ 观点分布
+
+
+ {{ item.type }}
+ {{ item.title }}
+
+
+
+
+
+
+
+
+ 可用资源与力量
+
+
+
+
+
+ 我方账号: {{ situationData['可用资源与力量']['我方账号情况'] || '暂无数据' }}
+
+
+
+
+
+
+
+
+
+
+
+ 媒资数量不足: {{ situationData['可用资源与力量']['媒资数量情况'] || '暂无数据' }}
+
+
+
+
+
+
+
+
+
+
+
+ 目标触达率较高: {{ situationData['可用资源与力量']['目标触达率情况'] || '暂无数据' }}
+
+
+
+
+ CBW1触达: XXX、XXX
+
+
+ CBW2触达: XXX、XXX
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/SentimentAnalysis/BattlefieldEnvironment.vue b/situation-overview-web/src/components/SentimentAnalysis/BattlefieldEnvironment.vue
new file mode 100644
index 00000000..69eaa0a4
--- /dev/null
+++ b/situation-overview-web/src/components/SentimentAnalysis/BattlefieldEnvironment.vue
@@ -0,0 +1,358 @@
+
+
+
+
+
+
+
+
+
+ 法理环境分析
+
+
+
+ {{ battlefieldEnvironmentData['法理环境分析']['总结'] }}
+
+
+
+
+
+ {{ key }}: {{ value }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 人文环境分析
+
+
+
+ {{ battlefieldEnvironmentData['人文环境分析']['总结'] }}
+
+
+
+
+
+ {{ key }}: {{ value }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 信息环境分析
+
+
+
+ {{ battlefieldEnvironmentData['信息环境分析']['总结'] }}
+
+
+
+
+
+
+ {{ key }}: {{ value }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 国际舆论分析
+
+
+
+ {{ battlefieldEnvironmentData['国际舆论分析']['总结'] }}
+
+
+
+
+
+ {{ key }}: {{ value }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/SentimentAnalysis/DeterminationContent.vue b/situation-overview-web/src/components/SentimentAnalysis/DeterminationContent.vue
new file mode 100644
index 00000000..1f906f76
--- /dev/null
+++ b/situation-overview-web/src/components/SentimentAnalysis/DeterminationContent.vue
@@ -0,0 +1,544 @@
+
+
+
+
+
+
+
+
+ {{
+ GJZQData["矛盾名称"] ||
+ GJZQData["优势名称"] ||
+ GJZQData["战机名称"]
+ }}
+
+
+ 简介描述:
+ {{
+ GJZQData["矛盾简介"] ||
+ GJZQData["优势简介"] ||
+ GJZQData["战机简介"]
+ }}
+
+
+
+ 关联方:{{ keys }}:{{ values }}
+
+
+
+ 战场:{{ GJZQData["战场"] }}
+
+
+ 总结: {{ GJZQData["总结"] }}
+
+
+ 分析依据: {{ GJZQData["分析依据"] }}
+
+
+
+
+
+
+
+
关键战情
+
+
+
+ {{ key }}{{ i + 1 }}
+ {{ v["优势名称"] || v["战机名称"] || v["矛盾名称"] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 决心建议
+
+
+
本级任务
+
+
+ {{ determinationData["决心建议"]["任务标题"] }}
+
+ {{ determinationData["决心建议"]["任务简介"] }}
+
+
+ 下发任务
+
+
+
+
+
+
作战目的
+
+
+
+ {{ index + 1 }}.{{
+ item
+ }}
+
+
+
+
+
+
打击目标
+
+
+
+
+ 目标名称: {{ DJTergetData["目标名称"] }}
+
+
+ 目标描述: {{ DJTergetData["目标描述"] }}
+
+
+ 选择理由: {{ DJTergetData["选择理由"] }}
+
+
+ 推荐依据: {{ DJTergetData["推荐依据"] }}
+
+
+
+
+
+
+
+ {{ item["目标名称"] }}
+
+
+
+
+
+
决心建议总结
+
+
+ {{ determinationData["决心建议总结"] || "暂无数据" }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/SentimentAnalysis/EnemySituation.vue b/situation-overview-web/src/components/SentimentAnalysis/EnemySituation.vue
new file mode 100644
index 00000000..4683719f
--- /dev/null
+++ b/situation-overview-web/src/components/SentimentAnalysis/EnemySituation.vue
@@ -0,0 +1,424 @@
+
+
+
+
+
+
+ 对手核心企图
+
+
+ {{ enemyData['对手核心企图'] || '暂无数据'}}
+
+
+
+
+
+
+
+ 关键叙事分析
+
+
+
+ {{ enemyData['关键叙事分析']['叙事总结'] }}
+
+
+ 叙事内容
+
+
+
+
+
+
+ 观点分布
+
+
+
+
+
+
+
+
+
{{ index + 1 }}.{{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 对手强弱点分析
+
+
+
+ {{ enemyData['对手强弱点分析']['强弱点总结'] }}
+
+
+ 对手弱点
+
+
+
+
+
+
+ 对手强点
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/SentimentAnalysis/HandleContent.vue b/situation-overview-web/src/components/SentimentAnalysis/HandleContent.vue
new file mode 100644
index 00000000..a807f8bd
--- /dev/null
+++ b/situation-overview-web/src/components/SentimentAnalysis/HandleContent.vue
@@ -0,0 +1,488 @@
+
+
+
+
+
+ 关键事态
+ (GJSTModelStatus = v)"
+ @config="() => (GJSTModelStatus = false)"
+ />
+
+
+
+
+ {{ value["类型"] }}
+ {{ value["标题"] }}
+
+
+
+ {{ value["内容"] }}
+
+
+
+
+
+ 依据:{{ value["依据"] }}
+
+
+
+
+
+
+
+
{{ row.label }}:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 处置建议总结
+
+
+
+
+ {{ handleData["处置建议总结"] }}
+
+
+
目标
+
+
+ {{ handleData["目标"] }}
+
+
+
+
+
建议
+
+
+
+
口径基调
+
+ {{ item["口径基调"].toString() }}
+
+
+
+
+
+
处置主体
+
+
+ 主责单位:
+ {{
+ item["处置主体"]["主责单位"]
+ }}
+
+
+ 配合单位:
+ {{ aItem }}
+
+
+
+
+
+
+
处置对象
+
+
+
+ 关联对象:
+ {{
+ item["处置对象"]["关联对象"]
+ }}
+
+
+ 核心对象:
+ {{
+ item["处置对象"]["核心对象"]
+ }}
+
+
+
+
+
+
+
+
+
建议内容
+
+ {{ item["建议内容"] }}
+
+
+
+
立场与原则
+
+ {{ item["立场与原则"] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/SentimentAnalysis/JudgmentConclusion.vue b/situation-overview-web/src/components/SentimentAnalysis/JudgmentConclusion.vue
new file mode 100644
index 00000000..3e1f9a8a
--- /dev/null
+++ b/situation-overview-web/src/components/SentimentAnalysis/JudgmentConclusion.vue
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/SentimentAnalysis/StrategyOverview.vue b/situation-overview-web/src/components/SentimentAnalysis/StrategyOverview.vue
new file mode 100644
index 00000000..fd0ec950
--- /dev/null
+++ b/situation-overview-web/src/components/SentimentAnalysis/StrategyOverview.vue
@@ -0,0 +1,270 @@
+
+
+
+
+
+
+
+
+ 当前态势定性
+
+
+ {{ strategyData['当前态势定性'] || '暂无数据'}}
+
+
+
+
+
+
+
+ 核心矛盾
+
+
+ {{ strategyData['核心矛盾'] || '暂无数据' }}
+
+
+
+
+
+
+
+
+ 战略机遇与威胁(SWOT)
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ {{ aItem.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/SentimentAnalysis/SuggestionContent.vue b/situation-overview-web/src/components/SentimentAnalysis/SuggestionContent.vue
new file mode 100644
index 00000000..046d08ae
--- /dev/null
+++ b/situation-overview-web/src/components/SentimentAnalysis/SuggestionContent.vue
@@ -0,0 +1,67 @@
+
+
+
+ 刷新
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/SituationAssessment.vue b/situation-overview-web/src/components/SituationAssessment.vue
new file mode 100644
index 00000000..df9bf3f0
--- /dev/null
+++ b/situation-overview-web/src/components/SituationAssessment.vue
@@ -0,0 +1,548 @@
+
+
+
+
+
+ 优劣势分析
+
+
+ 条件与限制
+
+
+ 风险及应对
+
+
+
+
+
+
+
民意RZ优劣势
+
+
+
+
+
+ {{ YLData.title }}
+
+
+ {{ YLData.content }}
+
+
+
+
+
+
+
+
+
+
ZZ力量优劣势
+
+
+
+
+
+
+
+
+ {{ key }}
+
+
+
+ {{ index + 1 }}.{{ item }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ key }}
+
+
+
+ {{ index + 1 }}.{{ item }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/TaskAnalysis.vue b/situation-overview-web/src/components/TaskAnalysis.vue
new file mode 100644
index 00000000..d201caf0
--- /dev/null
+++ b/situation-overview-web/src/components/TaskAnalysis.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/TaskAnalysis/AnalysisData.vue b/situation-overview-web/src/components/TaskAnalysis/AnalysisData.vue
new file mode 100644
index 00000000..2d92e778
--- /dev/null
+++ b/situation-overview-web/src/components/TaskAnalysis/AnalysisData.vue
@@ -0,0 +1,719 @@
+
+
+
+
+
+
{{ taskData.taskStatus < 30 ?
+ taskStatus[taskData.taskStatus] : '已完成' }}
+
{{ taskData.taskName }}
+
+ {{ taskData.taskDirection }}
+ {{ taskLevel[taskData.taskLevel] }}
+
+
+ {{ taskData.overview }}
+
+
+
+
+
+
分析结果
+
+
+ 逻辑图
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 目的 {{ aimData.zzPurpose }}
+
+
+
+
+
+
+
+
+ 行为体 {{ item.actorMap[0].actorName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+ {{ index + 1 }} . {{ item.narration }}
+
+
+
+
+
+
+ 预期行为:{{ item.behavior }}
+
+
+
+ 驱动因素:{{ aItem.drivingFactor }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/TaskAnalysis/TaskDataList.vue b/situation-overview-web/src/components/TaskAnalysis/TaskDataList.vue
new file mode 100644
index 00000000..1085aa53
--- /dev/null
+++ b/situation-overview-web/src/components/TaskAnalysis/TaskDataList.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
{{ item.taskStatus }}
+
{{ item.title }}
+
+ W方向
+ 一般
+
+
+ 利用退役飞官言论构建“T军离心力”叙事加强对T方向相关舆论热度。利用退役飞官言论构
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/TaskAnalysis/TaskDataSearch.vue b/situation-overview-web/src/components/TaskAnalysis/TaskDataSearch.vue
new file mode 100644
index 00000000..14d2654a
--- /dev/null
+++ b/situation-overview-web/src/components/TaskAnalysis/TaskDataSearch.vue
@@ -0,0 +1,367 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 全部
+ 我的
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/TaskDetails.vue b/situation-overview-web/src/components/TaskDetails.vue
new file mode 100644
index 00000000..2807006c
--- /dev/null
+++ b/situation-overview-web/src/components/TaskDetails.vue
@@ -0,0 +1,335 @@
+
+
+
+
+
+ 敌情
+
+
+ 我情
+
+
+ Z场环境
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/TaskDetails/BattlefieldEnvironment.vue b/situation-overview-web/src/components/TaskDetails/BattlefieldEnvironment.vue
new file mode 100644
index 00000000..f59c8b06
--- /dev/null
+++ b/situation-overview-web/src/components/TaskDetails/BattlefieldEnvironment.vue
@@ -0,0 +1,356 @@
+
+
+
信息环境
+
+
+
+
+
{{ item.name }}
+
{{ item.value }}
+
条
+
+
+
+
+
+
+
+
人文环境
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/TaskDetails/EnemySituation.vue b/situation-overview-web/src/components/TaskDetails/EnemySituation.vue
new file mode 100644
index 00000000..05812810
--- /dev/null
+++ b/situation-overview-web/src/components/TaskDetails/EnemySituation.vue
@@ -0,0 +1,773 @@
+
+
+
+
+
+
+
+
+
+
+ 排序:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+ {{ item.reason }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 任务重要目标
+
+
+ 更多>>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ modelStatus = v"
+ @config="() => modelStatus = false" />
+
+
+ 任务关键事件
+
+
+
+
+
+
+
+ {{ item.topic
+ }}({{ item.count }})
+
+
+
+
+
+
+
+
+ {{ item.date }}
+
+
+ {{ item.type }}
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+ 相关对手叙事
+
+
+
+
+
+
+
+ {{ item.topic }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 意图
+
+
+ {{ xushiItem['intention'] || '暂无' }}
+
+
+ 摘要
+
+
{{ xushiItem['summary'] || '暂无' }}
+
+ 话题
+
+
+ {{ xushiItem['topic'] || '暂无' }}
+
+
+ 叙事主体
+
+
+ {{ xushiItem['entity'] || '暂无' }}
+
+
+ 话题类型
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/TaskDetails/OurSituation.vue b/situation-overview-web/src/components/TaskDetails/OurSituation.vue
new file mode 100644
index 00000000..1d4fe614
--- /dev/null
+++ b/situation-overview-web/src/components/TaskDetails/OurSituation.vue
@@ -0,0 +1,548 @@
+
+
+
+
+
+
+
+
+
+
+ 总书记
+ 口径
+ 叙事
+
+
+
+
(infoModelStatus = v)"
+ @config="() => (infoModelStatus = false)"
+ />
+
+
+
+
+
+
+
+ {{
+ value.trendConsistentTitleMini ||
+ value.trendConsistentTitle ||
+ value.trendSpeechTitleMini ||
+ value.trendSpeechTitle
+ }}
+
+
+
+
+
+ {{
+ value.trendConsistentContent ||
+ value.trendConsistentLike ||
+ value.trendSpeechContent ||
+ value.trendSpeechLike
+ }}
+
+
+
+
发生主体:{{ value.xwt }}
+
+ 发生时间:{{
+ value.trendConsistentTime || value.trendSpeechTime
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ value["标题"] }}
+
+
+
+
+
标题
+
+ {{ xushiItem["标题"] }}
+
+
意图
+
+ {{ xushiItem["意图"] }}
+
+
标签
+
+
内容
+
+ {{ xushiItem["内容"] }}
+
+
+
+
+
+
+
+
+ 我方zz资源与力量
+
+
+
+
+ 资源
+ 力量
+
+
+
+
+
+
+
+ 上级要求
+
+
+
+
+ {{ upYaopqiuText.slice(0, 100) }}
+
+
+
+
+
+ 相关方案预案
+
+
+
+ {{ i.trendPlanName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/TaskDetails/Power.vue b/situation-overview-web/src/components/TaskDetails/Power.vue
new file mode 100644
index 00000000..b525b3bb
--- /dev/null
+++ b/situation-overview-web/src/components/TaskDetails/Power.vue
@@ -0,0 +1,94 @@
+
+
+
+ 正在执行中{{ tabData.taskProgressCount }}项任务,当前空闲{{ tabData.freeGroupCount }}组力量。
+
+
+
+
+
+
+
+ {{ item.progressTask?.length || 0 }}项活跃任务
+
+
+
+ {{ aIndex + 1 }}. {{ aItem.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/TaskDetails/component/Resources.vue b/situation-overview-web/src/components/TaskDetails/component/Resources.vue
new file mode 100644
index 00000000..ceaab426
--- /dev/null
+++ b/situation-overview-web/src/components/TaskDetails/component/Resources.vue
@@ -0,0 +1,514 @@
+
+
+
+
+
+ {{ v }}
+
+
+
+ 资源
+ {{ '优' }}
+
+
+
+
+
+
+
+ 任务{{ tabList[tabId] }}总数{{ 134 }}条,相似任务中平均使用{{ tabList[tabId] }}{{ 134 }}条。
+
+
+
+
+
+
{{ item.name }}
+
{{ item.value }}
+
条
+
+
+
+
+
+ 任务{{ tabList[tabId] }}总数{{ accountAndCWB.totalCount }}条。
+
+
+
+
+
+
{{ item.name }}
+
{{ item.value }}
+
条
+
+
+
+
+
+ 任务相关目标总计{{ accountAndCWB.targetCount }}个,其中{{ accountAndCWB.cbwTargetCount }}个可通过CBW触达,涉及CBW{{
+ accountAndCWB.cbwCount
+ }}张。
+
+
+
+
+
+
+
+ {{ v }}
+
+
+ {{ b3Data[k] }}
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/Template.vue b/situation-overview-web/src/components/Template.vue
new file mode 100644
index 00000000..4b3150dc
--- /dev/null
+++ b/situation-overview-web/src/components/Template.vue
@@ -0,0 +1,20 @@
+
+ task-analysis
+
+
+
+
+
diff --git a/situation-overview-web/src/components/TreeDataMap.vue b/situation-overview-web/src/components/TreeDataMap.vue
new file mode 100644
index 00000000..120ae624
--- /dev/null
+++ b/situation-overview-web/src/components/TreeDataMap.vue
@@ -0,0 +1,337 @@
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/details-dialog/DocDetails.vue b/situation-overview-web/src/components/details-dialog/DocDetails.vue
new file mode 100644
index 00000000..94ebff4e
--- /dev/null
+++ b/situation-overview-web/src/components/details-dialog/DocDetails.vue
@@ -0,0 +1,78 @@
+
+
+
+ {{ docData.m_title }}
+
+
+
+ {{ docData.source1 }}
+
+
+ {{ docData.m_publish }}
+
+
+
+ {{ docData.m_content }}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/dialog/AddTaskDialog.vue b/situation-overview-web/src/components/dialog/AddTaskDialog.vue
new file mode 100644
index 00000000..90076585
--- /dev/null
+++ b/situation-overview-web/src/components/dialog/AddTaskDialog.vue
@@ -0,0 +1,310 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ !
+ 任务描述对描述COH(CH)质量影响重大,请认真、完整填写
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/dialog/CommonInfoDialog.vue b/situation-overview-web/src/components/dialog/CommonInfoDialog.vue
new file mode 100644
index 00000000..7ed2c016
--- /dev/null
+++ b/situation-overview-web/src/components/dialog/CommonInfoDialog.vue
@@ -0,0 +1,137 @@
+
+
+
+
+ {{ modelData.title }}
+
+
+
+ 摘要:{{ modelData.trendSpeechLike || modelData.trendConsistentLike }}
+
+
+ {{ modelData.content }}
+
+
+
+ 推荐理由:{{ modelData.recommendReason }}
+
+
+ 叙事方向:{{ modelData.narrativeDirection }}
+
+
+ 叙事限制:{{ modelData.narrativeConstraints }}
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/graph/G6NetworkGraph.vue b/situation-overview-web/src/components/graph/G6NetworkGraph.vue
new file mode 100644
index 00000000..8a95ed00
--- /dev/null
+++ b/situation-overview-web/src/components/graph/G6NetworkGraph.vue
@@ -0,0 +1,1330 @@
+
+
+
+
+
+
+
+
+ {{ "行动统计" }}
+
+
+
+
+ 共
+
+ {{ actionStatusCount["全部"] }}
+
+ 个行动
+
+
+
+ 待签收
+
+ {{ actionStatusCount["待签收"] }}
+
+ 个
+
+
+
+ 策划中
+
+ {{ actionStatusCount["进行中"] }} 个
+
+
+
+ 已完成
+
+ {{ actionStatusCount["已完成"] }}
+
+ 个
+
+
+
+ 终止
+
+ {{ actionStatusCount["终止"] }}
+
+ 个
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 任务详情
+
+
+
+
+
+
+
+ {{ taskData["taskName"] }}
+
+
+
+
+
+
+
+
+ {{ taskStatusDict[taskData["taskStatus"]] }}
+
+
+ {{ taskData["taskDirection"] }}
+
+
+
+ {{ levelDataDict[taskData["taskLevel"]] }}
+
+
+
+
+
创建时间:
+
+ {{
+ dayjs(taskData.createTime).format("YYYY-MM-DD HH:mm:ss")
+ }}
+
+
+
+
+
+
任务来源:
+
+
+ {{ sourceTypeDict[taskData.sourceType] }}
+
+
+
+
+
+
+
关键词:
+
+ {{ taskData["keywords"] }}
+
+
+
+
+
+
任务简介
+
+
+ {{ taskData["overview"] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/graph/modal/components/Audience.vue b/situation-overview-web/src/components/graph/modal/components/Audience.vue
new file mode 100644
index 00000000..4165f01e
--- /dev/null
+++ b/situation-overview-web/src/components/graph/modal/components/Audience.vue
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+
+
+ 群体
+
+
+
{{ data.designName }}
+
+
+ 共涉及10个账号
+
+
+
+
+
+
+ 选项卡1的内容,使用 t-tab-panel 渲染
+
+
+
+
+
+
+
+
+
+
+
{{ data.designName }}
+
+
+
+
+
+ 粉丝量 805
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/graph/modal/components/Strategy.vue b/situation-overview-web/src/components/graph/modal/components/Strategy.vue
new file mode 100644
index 00000000..8d388402
--- /dev/null
+++ b/situation-overview-web/src/components/graph/modal/components/Strategy.vue
@@ -0,0 +1,195 @@
+
+
+
+
+ {{ "ZF策略" }}
+
+
+
+
+
+
{{ strategy.key }}:
+
+ {{ strategy?.value }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/graph/modal/components/Yqxg.vue b/situation-overview-web/src/components/graph/modal/components/Yqxg.vue
new file mode 100644
index 00000000..b371a5d7
--- /dev/null
+++ b/situation-overview-web/src/components/graph/modal/components/Yqxg.vue
@@ -0,0 +1,365 @@
+
+
+
预期效果
+
+
+
+ {{ data["撬动关键节点"] }}
+ 个
+
+
撬动关键节点
+
+
+
+ {{ numberToChinese(data["六度量等级"]) }} 级
+
+
六度量等级
+
+
+
+ {{ data["引发主流媒体报道"] }}
+
+ 个
+
+
引发主流媒体报道
+
+
+
+
+
{{ data["引发官方回应"] }}
+
引发官方回应
+
+
+
{{ data["引发线下活动"] }}
+
引发线下活动
+
+
+
+
+
+ {{ "未推演" }}
+
+ {{ data["预期效果"] }}
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/graph/modal/index.vue b/situation-overview-web/src/components/graph/modal/index.vue
new file mode 100644
index 00000000..9008bd84
--- /dev/null
+++ b/situation-overview-web/src/components/graph/modal/index.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/intelligent-helper-drawer/AnalysisTool.vue b/situation-overview-web/src/components/intelligent-helper-drawer/AnalysisTool.vue
new file mode 100644
index 00000000..0a8000f8
--- /dev/null
+++ b/situation-overview-web/src/components/intelligent-helper-drawer/AnalysisTool.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/intelligent-helper-drawer/ChatContent.vue b/situation-overview-web/src/components/intelligent-helper-drawer/ChatContent.vue
new file mode 100644
index 00000000..76e7222d
--- /dev/null
+++ b/situation-overview-web/src/components/intelligent-helper-drawer/ChatContent.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/intelligent-helper-drawer/ChatList.vue b/situation-overview-web/src/components/intelligent-helper-drawer/ChatList.vue
new file mode 100644
index 00000000..2509adb9
--- /dev/null
+++ b/situation-overview-web/src/components/intelligent-helper-drawer/ChatList.vue
@@ -0,0 +1,373 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
正在思考...
+
+
+
+
+
+ 已深度思考
+
+
+
+
+
+
+
+
+
+
+
+
停止生成
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/intelligent-helper-drawer/IntelligentHelperDrawer.vue b/situation-overview-web/src/components/intelligent-helper-drawer/IntelligentHelperDrawer.vue
new file mode 100644
index 00000000..62ea9116
--- /dev/null
+++ b/situation-overview-web/src/components/intelligent-helper-drawer/IntelligentHelperDrawer.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/intelligent-helper-drawer/InteractiveAnalysis.vue b/situation-overview-web/src/components/intelligent-helper-drawer/InteractiveAnalysis.vue
new file mode 100644
index 00000000..f20cd9cb
--- /dev/null
+++ b/situation-overview-web/src/components/intelligent-helper-drawer/InteractiveAnalysis.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/markdown/CodeData.vue b/situation-overview-web/src/components/markdown/CodeData.vue
new file mode 100644
index 00000000..3f049caa
--- /dev/null
+++ b/situation-overview-web/src/components/markdown/CodeData.vue
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/scrollingloading/index.vue b/situation-overview-web/src/components/scrollingloading/index.vue
new file mode 100644
index 00000000..cfc1cd82
--- /dev/null
+++ b/situation-overview-web/src/components/scrollingloading/index.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/components/searchTaskList/index.vue b/situation-overview-web/src/components/searchTaskList/index.vue
new file mode 100644
index 00000000..d6cf6946
--- /dev/null
+++ b/situation-overview-web/src/components/searchTaskList/index.vue
@@ -0,0 +1,391 @@
+
+
+
+
+
+
+
+
+
+ 全部
+ 我的
+
+
+
+
+
+
+
{{ handleStatus(item.taskStatus) }}
+
+
{{ item.taskName }}
+
+ {{ item.taskDirection }}
+ {{ taskLevel[item.taskLevel] }}
+ {{ item.ifCreateTask === 0 ?
+ '已创建任务分析' : '未创建任务分析'
+ }}
+
+
+ {{ item.taskContent }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/shared/Empty.vue b/situation-overview-web/src/components/shared/Empty.vue
new file mode 100644
index 00000000..c9678229
--- /dev/null
+++ b/situation-overview-web/src/components/shared/Empty.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/views/PieChart.vue b/situation-overview-web/src/components/views/PieChart.vue
new file mode 100644
index 00000000..10326129
--- /dev/null
+++ b/situation-overview-web/src/components/views/PieChart.vue
@@ -0,0 +1,279 @@
+
+
+
+
+
+ 差
+
+
+ 任务相关媒资总数134条,相似任务中平均使用媒资80条。
+
+
+
+
+
+
+ 良
+
+ 任务相关账号总共{{ accountAndCWB.totalCount }}个。
+
+
+
+
+
+ 优
+
+
+ 任务相关目标总计{{ accountAndCWB.targetCount }}个,其中{{ accountAndCWB.cbwTargetCount }}个可通过CBW触达,涉及CBW{{
+ accountAndCWB.cbwCount
+ }}张。
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/components/views/TabsContent.vue b/situation-overview-web/src/components/views/TabsContent.vue
new file mode 100644
index 00000000..768e14b2
--- /dev/null
+++ b/situation-overview-web/src/components/views/TabsContent.vue
@@ -0,0 +1,32 @@
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/main.js b/situation-overview-web/src/main.js
new file mode 100644
index 00000000..412c3ac8
--- /dev/null
+++ b/situation-overview-web/src/main.js
@@ -0,0 +1,17 @@
+import { createApp } from "vue";
+import TDesign from "tdesign-vue-next";
+import "tdesign-vue-next/es/style/index.css";
+import "leaflet/dist/leaflet.css";
+import "./style.css";
+import "./style/theme.less";
+import "./style/tdesign-style.less";
+import "./assets/markdown-css/github-markdown.css";
+import App from "./App.vue";
+import store from "./store";
+import router from "./router";
+const app = createApp(App);
+
+app.use(store);
+app.use(router);
+app.use(TDesign);
+app.mount("#app");
diff --git a/situation-overview-web/src/router/index.js b/situation-overview-web/src/router/index.js
new file mode 100644
index 00000000..60946822
--- /dev/null
+++ b/situation-overview-web/src/router/index.js
@@ -0,0 +1,91 @@
+import { path } from "d3";
+import { createRouter, createWebHashHistory } from "vue-router";
+
+const routes = [
+ {
+ path: "/",
+ redirect: "/situation-overview",
+ },
+ // {
+ // path: "/test",
+ // component: () => import("@/views/situation-overview.vue"),
+ // },
+ {
+ path: "/situation-overview",
+ component: () => import("@/views/situation-overview/index.vue"),
+ redirect: "/situation-overview/action",
+ children: [
+ {
+ path: "/situation-overview/monitoring",
+ name: "监测模式",
+ },
+ {
+ path: "action",
+ name: "行动模式",
+ redirect: "/situation-overview/action/sentiment",
+ children: [
+ {
+ path: "sentiment",
+ component: () => import("@/views/sentiment/index.vue"),
+ },
+ {
+ path: "task",
+ component: () => import("@/views/task/index.vue"),
+ },
+ {
+ path: "action",
+ component: () => import("@/views/action/index.vue"),
+ },
+ {
+ path: "reply",
+ component: () => import("@/views/reply/index.vue"),
+ },
+ {
+ path: "control",
+ component: () => import("@/views/control/index.vue"),
+ },
+ {
+ path: "homeoverview",
+ component: () => import("@/views/homeOverview/index.vue"),
+ },
+ ],
+ },
+ {
+ path: "query",
+ name: "查询模式",
+ },
+ ],
+ },
+ {
+ path: "/dashboard",
+ name: "Dashboard",
+ component: () => import("@/views/Dashboard.vue"),
+ },
+ {
+ path: "/chart",
+ name: "Chart",
+ component: () => import("../views/Chart.vue"),
+ },
+ {
+ path: "/map",
+ name: "Map",
+ component: () => import("../views/Map.vue"),
+ },
+ {
+ path: "/graph",
+ name: "Graph",
+ component: () => import("../views/Graph.vue"),
+ },
+ {
+ path: "/GroupContent",
+ name: "GroupContent",
+ component: () => import("../views/GroupContent.vue"),
+ },
+];
+
+const router = createRouter({
+ history: createWebHashHistory(),
+ routes,
+});
+
+export default router;
diff --git a/situation-overview-web/src/service/request-axios-service.js b/situation-overview-web/src/service/request-axios-service.js
new file mode 100644
index 00000000..3292d616
--- /dev/null
+++ b/situation-overview-web/src/service/request-axios-service.js
@@ -0,0 +1,47 @@
+import service from "../utils/request";
+export const requestAxiosService = {
+ get: async (endpoint, params = {}) => {
+ try {
+ const response = await service.get(endpoint, { params });
+ return response;
+ } catch (error) {
+ throw new Error(`GET request failed: ${error.message}`);
+ }
+ },
+
+ post: async (endpoint, data = {}) => {
+ try {
+ const response = await service.post(endpoint, data);
+ return response;
+ } catch (error) {
+ throw new Error(`POST request failed: ${error.message}`);
+ }
+ },
+
+ put: async (endpoint, data = {}) => {
+ try {
+ const response = await service.put(endpoint, data);
+ return response;
+ } catch (error) {
+ throw new Error(`PUT request failed: ${error.message}`);
+ }
+ },
+
+ update: async (endpoint, data = {}) => {
+ try {
+ const response = await service.patch(endpoint, data);
+ return response;
+ } catch (error) {
+ throw new Error(`patch request failed: ${error.message}`);
+ }
+ },
+
+ delete: async (endpoint) => {
+ try {
+ const response = await service.delete(endpoint);
+ return response;
+ } catch (error) {
+ throw new Error(`DELETE request failed: ${error.message}`);
+ }
+ }
+}
\ No newline at end of file
diff --git a/situation-overview-web/src/service/request-crud.service.js b/situation-overview-web/src/service/request-crud.service.js
new file mode 100644
index 00000000..35331233
--- /dev/null
+++ b/situation-overview-web/src/service/request-crud.service.js
@@ -0,0 +1,141 @@
+import { environment } from '@/environments/environment';
+import { requestCrudService } from '@/service/request-axios-service';
+
+export interface GetOneParam {
+ fields?: string[]; // 指定返回的字段 id,name
+ join?: string[]; // 指定关联表,如 ['user||id,name', 'relation||id']
+ cache?: 0 | 1; // 0直接DB,1缓存,默认1缓存
+}
+export interface GetAllParam {
+ fields?: string[] | string; // 指定返回的字段: 多个字段如 'id', 'name'
+ filter?: string[]; // 指定过滤条件,如 ['name||$eq||batman', 'id||$in||1,2,3', 'field||$isnull', 'relation.id||$eq||1']
+ or?: string[]; // 同filter
+ join?: string[]; // 指定关联表,如 ['user||id,name', 'relation||id']
+ cache?: 0 | 1; // 0直接DB,1缓存,默认1缓存
+ page?: number; // 页码(会被转换成offset)
+ offset?: number; // 偏移量
+ limit?: number; // 每页数量
+ sort?: string[]; // 排序,如 '['name,ASC', 'id,DESC]'
+ s?: string;
+ joinTaskAction?: boolean; //是否连行动表查询
+ joinTaskActionBoci?: boolean; //策略分析列表使用
+}
+
+export interface GetAllResponse {
+ data: any[];
+ page: number;
+ pageCount: number;
+ count: number;
+ total: number;
+ errMsg?: string;
+}
+let deepCopy = (str: any) => {
+ return JSON.parse(JSON.stringify(str));
+}
+export class RequestCrudService {
+
+ /**
+ * 获取单个对象
+ * @param url 地址
+ * @param id ID
+ * @param params 参数
+ * @returns 单个对象
+ */
+ async getOne(url: string, id: number, params?: GetOneParam): Promise {
+ const getUrl = `${url}/${id}`;
+ const res = await requestCrudService.get(getUrl, params);
+ if (res) return res;
+ // this.notification.create('error', getUrl, `${res.data}`);
+ return {};
+ }
+ /**
+ * 获取多个对象
+ * @param url 地址
+ * @param params 参数
+ * @returns 多个对象数组
+ */
+ async getAll(url: string, params: GetAllParam | null = null): Promise {
+ const formattedParams = deepCopy(params || {});
+ // 分页默认值
+ if (!params || !params.limit) formattedParams.limit = 999;
+ if (!params || !params.page) formattedParams.page = 1;
+ // 计算offset
+ if (params && formattedParams.page) {
+ formattedParams.offset = (formattedParams.page - 1) * formattedParams.limit!;
+ delete formattedParams.page;
+ }
+ const res = await requestCrudService.get(url, formattedParams);
+ if (res.status === 200) return res.data;
+ // this.notification.create('error', url, `${res.data}`);
+ return {
+ count: 0,
+ data: [],
+ page: 1,
+ pageCount: 1,
+ total: 0
+ };
+ }
+ /**
+ * 创建单个对象
+ * @param url 地址
+ * @param data 单个对象
+ * @returns 被创建的单个对象
+ */
+ async createOne(url: string, data: any) {
+ const res = await requestCrudService.post(url, data);
+ if (res.status === 201) return res.data;
+ // this.notification.create('error', url, `${res.data}`);
+ return {};
+ }
+ /**
+ * 创建多个对象
+ * @param url 地址
+ * @param data 多个对象
+ * @returns 被创建的多个对象
+ */
+ async createMulti(url: string, data: any[]) {
+ const res = await requestCrudService.post(url, {
+ bulk: data
+ });
+ if (res.status === 201) return res.data;
+ // this.notification.create('error', url, `${res.data}`);
+ return [];
+ }
+ /**
+ * 更新单个对象
+ * @param url 地址
+ * @param id ID
+ * @param data 单个对象部分键值对
+ * @returns 被修改的单个对象
+ */
+ async updateOne(url: string, id: number, data: any) {
+ const res = await requestCrudService.update(`${url}/${id}`, data);
+ if (res.status === 200) return res.data;
+ // this.notification.create('error', url, `${res.data}`);
+ return {};
+ }
+ /**
+ * 删除单个对象
+ * @param url 地址
+ * @param id ID
+ * @returns 操作结果
+ */
+ async deleteOne(url: string, id: number) {
+ const res = await requestCrudService.delete(`${url}/${id}`);
+ if (res.status === 200) return res.data;
+ // this.notification.create('error', url, `${res.data}`);
+ return 'error';
+ }
+ // get 请求
+ async simpleGet(url: string, params?: any): Promise {
+ const res = await requestCrudService.get(url, params);
+ if (res.status === 200) return res.data;
+ return res.data;
+ }
+ // post 请求
+ async simplePost(url: string, data: any) {
+ const res = await requestCrudService.post(url, data);
+ if (res.status === 200) return res.data;
+ return res.data;
+ }
+}
diff --git a/situation-overview-web/src/store/index.js b/situation-overview-web/src/store/index.js
new file mode 100644
index 00000000..ec78060c
--- /dev/null
+++ b/situation-overview-web/src/store/index.js
@@ -0,0 +1,43 @@
+import { createStore } from 'vuex'
+
+export default createStore({
+ state: {
+ count: 0,
+ user: null,
+ taskData: null,
+ targetList: [],
+ targetData: null,
+ actionOverview: null,
+ },
+ getters: {
+ doubleCount: (state) => state.count * 2
+ },
+ mutations: {
+ increment(state) {
+ state.count++
+ },
+ setUser(state, user) {
+ state.user = user
+ },
+ setTaskData(state, value) {
+ state.taskData = value;
+ },
+ setTargetList(state,value) {
+ state.targetList = value
+ },
+ setTargetData(state,value) {
+ state.targetData = value
+ },
+ setActionOverview(state,value) {
+ state.actionOverview = value;
+ }
+ },
+ actions: {
+ incrementAsync({ commit }) {
+ setTimeout(() => {
+ commit('increment')
+ }, 1000)
+ }
+ },
+ modules: {}
+})
diff --git a/situation-overview-web/src/style.css b/situation-overview-web/src/style.css
new file mode 100644
index 00000000..e69de29b
diff --git a/situation-overview-web/src/style/tdesign-style.less b/situation-overview-web/src/style/tdesign-style.less
new file mode 100644
index 00000000..f575bb11
--- /dev/null
+++ b/situation-overview-web/src/style/tdesign-style.less
@@ -0,0 +1,134 @@
+:root {
+ --td-brand-color-8: '#0062d9';
+}
+.t-input {
+ border-color: var(--td-brand-color);
+}
+
+.t-range-input {
+ border-color: var(--td-brand-color);
+}
+
+.t-layout, .t-layout__direction-vertical {
+ background: none;
+}
+
+.t-layout__sider {
+ background: none;
+}
+
+
+// 对话框样式
+.t-dialog {
+ background: #0f2844 !important;
+ border: 1px solid;
+ border-color: #2b8ffe99;
+ padding: 20px 25px;
+ border-radius: 4px;
+}
+
+.t-dialog__header {
+ border-radius: 4px 4px 0 0;
+ padding: 20px 25px;
+ padding-top: 0;
+ box-shadow: none !important;
+ position: relative;
+ font-weight: 500;
+ color: #fff;
+ font-size: 25px;
+ background: url('../assets/icon/title-header-logo.svg') no-repeat bottom center !important;
+}
+
+.t-dialog__header .t-dialog__header-content {
+ justify-content: center;
+}
+
+.t-button--variant-base {
+ color: #FFFFFF;
+ background: rgba(3, 38, 80, 0.8);
+ border-color: rgba(43, 143, 254, 0.6);
+}
+
+.t-button--variant-base.t-button--theme-primary {
+ background: linear-gradient(102.76deg, #2b8ffe 0%, #0062d9 100%);
+}
+
+.t-drawer__close-btn {
+ display: none;
+}
+
+// 抽屉样式
+.t-drawer__content-wrapper {
+ background: #021e3e;
+}
+.t-drawer__header {
+ background: linear-gradient(90deg, #0062d9 0%, rgba(0, 98, 217, 0) 100%);
+ border-bottom: 2px solid;
+ border-color: rgba(255, 255, 255, 0.2);
+}
+.t-drawer__body {
+ background: #021e3e;
+}
+
+// 标签页
+
+.t-tabs {
+ background-color: transparent;
+}
+
+.t-tabs__nav-item.t-is-active {
+ font-size: var(--fontSize);
+ font-family: Noto Sans S Chinese;
+ font-weight: 500;
+ color: #0062d9;
+}
+
+.t-tabs__nav-item {
+ font-size: var(--fontSize);
+}
+
+.t-tabs__bar {
+ background-color: #0062d9;
+}
+
+.t-tabs__nav-container.t-is-top::after {
+ background: rgba(85, 159, 254, 0.2);
+}
+
+.t-tabs__nav-item:not(.t-is-disabled):not(.t-is-active):hover .t-tabs__nav-item-wrapper{
+ background: rgba(255, 255, 255, 0.1);
+}
+
+.t-tabs__btn {
+ background: rgba(3, 38, 80, 0.8);
+ border-color: #053e59 !important;
+}
+
+.t-tabs__btn:hover {
+ background: rgba(3, 38, 80, 0.8);
+}
+
+.t-tabs__nav-item.t-size-m {
+ line-height: 18px;
+ height:auto;
+}
+
+.t-tabs__nav--card.t-tabs__nav-item:not(.t-is-disabled):not(.t-is-active):hover {
+ background:linear-gradient(103.13deg,#2b8ffe 0%,#0062d9 100%);
+ border-radius:24px;
+}
+
+// 单选框样式
+.t-radio-group.t-radio-group--filled{
+ background-color: #08527b;
+ border-color: #08527b;
+}
+
+.t-radio-group.t-radio-group--filled .t-radio-button.t-is-checked {
+ background-color: #07324c;
+}
+
+.markdown-body{
+ background: transparent !important;
+ color: #FFFFFF !important;
+}
\ No newline at end of file
diff --git a/situation-overview-web/src/style/theme.less b/situation-overview-web/src/style/theme.less
new file mode 100644
index 00000000..b76c69a3
--- /dev/null
+++ b/situation-overview-web/src/style/theme.less
@@ -0,0 +1,676 @@
+:root {
+ --color-main: #0062d9;
+ --bg-color: #012044;
+ --fontSize: 18px;
+ --titleSize: 20px;
+ --fontSizes: 16px;
+ --chatSize: calc(18 * var(--px-to-vw));
+ --router-bg-color: #f8ccce;
+ --three-color: #fbd8da;
+ --task-button-bgcolor: #feeded;
+ --tag-border-color: #006efe;
+ --antd-wave-shadow-color: #ce2d39;
+ --border-color: linear-gradient(180deg, #f86c73, #ce1322);
+ --theme-img: url("/assets/icon/layout-bg-theme-red.png");
+ --them-icon: url("/assets/icon/pxcook-red.png");
+ --ant-modal-header: url("/assets/overiew-logo/ant-modal-header-bg.svg");
+ --theme-area-bg: rgba(3, 38, 80, 0.6);
+ --theme-area-border-color: rgba(43, 143, 254, 0.6);
+ --theme-box-shadow: 0px 8px var(--fontSize) rgba(139, 139, 139, 0.06);
+ --theme-font-color: #ffffff;
+ --theme-font-text: rgba(255, 255, 255, 0.6);
+ --theme-font-details: rgba(255, 255, 255, 0.8);
+ --theme-card-bg: rgba(0, 98, 217, 0.2);
+ --theme-card-border: rgba(85, 166, 254, 0.1);
+ --theme-card-active: rgba(0, 98, 217, 0.4);
+ --theme-active-border: #0062d9;
+ --theme-border-color: #559ffe;
+ --index-color: #ffffff;
+ --extract-content-bg: rgba(3, 38, 80, 0.6);
+ --logo-color: #45c3f3;
+ --user-item-bg: rgba(0, 98, 217, 0.3);
+ --chat-item-bg: rgba(0, 98, 217, 0.2);
+ --chat-content-bg: rgba(0, 98, 217, 0.1);
+ --think-content-bg: rgba(0, 98, 217, 0.2);
+ --think-border-color: #0062d9;
+ --think-logo-color: #45c3f3;
+ --think-font-color: rgba(255, 255, 255, 0.6);
+ --chat-source-color: #0079fe;
+ --chat-source-bg: rgba(0, 98, 217, 0.2);
+ --extraction-content-bg: #00368c;
+ --extraction-content-border: #0056c6;
+ --extraction-content-color: rgba(255, 255, 255, 0.8);
+ --chat-input-border: #0062d9;
+ --list-item-bg: var(--chat-item-bg);
+ --list-item-border: var(--theme-card-border);
+ --list-item-active: var(--theme-card-active);
+ --list-item-active-border: var(--theme-active-border);
+ --drawer-bg-color: #021e3e;
+ --table-border-color: #1592e6;
+ --table-bg-color: #01316d;
+ --time-range-th-bg-color: #012b6e;
+ --time-range-th-font-color: #ffffff;
+ --time-range-border-color: #006efe;
+ --time-range-font-color: #2b86fe;
+ --time-range-icon-color: #022043;
+ --time-range-tag-bg-color: #02387a;
+ --grap-container-bg: var(--chat-item-bg);
+ --full-screen: var(--bg-color);
+ --xwt-linear-gradient: linear-gradient(165.07deg, #0066cc 0%, #003366 100%);
+ --card-border: rgba(43, 143, 254, 0.6);
+ --item-borer-line: rgba(85, 159, 254, 0.2);
+ --gray-color: rgba(255, 255, 255, 0.6);
+ --item-index: var(--color-main);
+ --item-active-color: #0062d9;
+ --item-expand-color: #002c57;
+ --item-expand-active: #002d61;
+ --tag-bg-color: rgba(69, 195, 243, 0.5);
+ --theme-option-color: #023064;
+ --tag-border-bg: rgba(43, 143, 254, 0.6);
+ --file-tag-color: rgba(43, 143, 254, 0.6);
+ --ant-title-color: rgba(255, 255, 255, 0.8);
+ /* --heightVh: 920px; */
+}
+
+@font-face {
+ font-family: DDIN;
+ font-weight: normal;
+ src: url("../assets/font/D-Din/D-DIN.ttf");
+ unicode-range: U+0030-0039;
+}
+
+@font-face {
+ font-family: DDINBold;
+ font-weight: bold;
+ src: url("../assets/font/D-Din/D-DIN-Bold.ttf");
+ unicode-range: U+0030-0039;
+}
+
+@font-face {
+ font-family: Douyu;
+ src: url("../assets/font/DouyuFont/douyuFont-2.otf") format("opentype");
+}
+
+@font-face {
+ font-family: Noto Sans S Chinese;
+ font-weight: 100;
+ src: url("../assets/font/NotoSansHans/NotoSansHans-Thin-Windows.otf")
+ format("opentype");
+}
+
+@font-face {
+ font-family: Noto Sans S Chinese;
+ font-weight: 300;
+ src: url("../assets/font/NotoSansHans/NotoSansHans-Light.otf")
+ format("opentype");
+}
+
+@font-face {
+ font-family: Noto Sans S Chinese;
+ font-weight: 400;
+ src: url("../assets/font/NotoSansHans/NotoSansHans-Regular.otf")
+ format("opentype");
+}
+
+@font-face {
+ font-family: Noto Sans S Chinese;
+ font-weight: 500;
+ src: url("../assets/font/NotoSansHans/NotoSansHans-Medium.otf")
+ format("opentype");
+}
+
+@font-face {
+ font-family: Noto Sans S Chinese;
+ font-weight: 700;
+ src: url("../assets/font/NotoSansHans/NotoSansHans-Bold.otf")
+ format("opentype");
+}
+
+@font-face {
+ font-family: Noto Sans S Chinese;
+ font-weight: 900;
+ src: url("../assets/font/NotoSansHans/NotoSansHans-Black.otf")
+ format("opentype");
+}
+
+@font-face {
+ font-family: DS-Digital;
+ font-weight: 300;
+ src: url("../assets/font/DS-Digital/DS-DIGI-1.ttf") format("opentype");
+}
+
+@font-face {
+ font-family: DS-Digital;
+ font-weight: 400;
+ src: url("../assets/font/DS-Digital/DS-DIGIB-2.ttf") format("opentype");
+}
+
+@font-face {
+ font-family: DS-Digital;
+ font-weight: 500;
+ src: url("../assets/font/DS-Digital/DS-DIGII-3.ttf") format("opentype");
+}
+
+@font-face {
+ font-family: DS-Digital;
+ font-weight: 700;
+ src: url("../assets/font/DS-Digital/DS-DIGIT-4.ttf") format("opentype");
+}
+
+// flex 布局
+.flex-center-center {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.flex-start-center {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+}
+
+.flex-start-baseline {
+ display: flex;
+ justify-content: flex-start;
+ align-items: baseline;
+}
+
+.flex-start-start {
+ display: flex;
+ justify-content: flex-start;
+ align-items: start;
+}
+
+.flex-end-center {
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+}
+
+.flex-end-start {
+ display: flex;
+ justify-content: flex-end;
+ align-items: start;
+}
+
+.flex-between-center {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.flex-between-baseline {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+}
+
+.flex-between-start {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+}
+
+.flex-content {
+ display: flex;
+ justify-content: space-between;
+}
+
+.font-weight-bold {
+ font-family: "Noto Sans S Chinese";
+ font-weight: 700;
+}
+
+.margin-bottom-5 {
+ margin-bottom: 5px;
+}
+
+.margin-bottom-10 {
+ margin-bottom: 10px;
+}
+
+.margin-bottom-15 {
+ margin-bottom: 15px;
+}
+
+.margin-top-5 {
+ margin-top: 5px;
+}
+
+.margin-top-10 {
+ margin-top: 10px;
+}
+
+.margin-top-15 {
+ margin-top: 15px;
+}
+
+.padding-10 {
+ padding: 10px;
+}
+
+.cursor-pointer {
+ cursor: pointer;
+}
+
+// 字体设置
+span,
+p,
+div,
+li {
+ font-family: "Noto Sans S Chinese";
+}
+
+.markdown-body table tr {
+ background: transparent !important;
+}
+
+// 单选框样式
+.tabs-box {
+ .sortBtn {
+ font-size: var(--fontSize);
+ line-height: var(--fontSize);
+ color: #fff;
+ display: inline-block;
+ padding: 5px 10px;
+ border-radius: 4px;
+ border: 1px solid #2b8ffe99 !important;
+ margin: 0 5px;
+ cursor: pointer;
+ }
+
+ .sort-active {
+ background: linear-gradient(123.69deg, #2b8ffe, #0062d9) !important;
+ }
+}
+
+// tab颜色
+.tab-green {
+ background: rgba(0, 140, 119, 0.5);
+ border-color: #008c77 !important;
+}
+
+.tab-blue {
+ background: rgba(0, 121, 254, 0.1);
+ border-color: #0079fe !important;
+}
+
+.tab-lightblue {
+ background: rgba(69, 195, 243, 0.5);
+ border-color: #45c3f3 !important;
+}
+
+.tab-yellow {
+ background: #ff990080;
+ border-color: #f90 !important;
+}
+
+.tab-red {
+ background: rgba(207, 19, 34, 0.5);
+ border-color: #cf1322 !important;
+}
+
+// tab标签页
+.tabs-list {
+ text-align: center;
+
+ .tabs-item {
+ background: rgba(0, 12, 28, 0.2);
+ color: rgba(255, 255, 255, 0.6);
+ display: inline-block;
+ font-size: var(--fontSize);
+ font-family: "Noto Sans S Chinese";
+ padding: 5px 15px;
+ cursor: pointer;
+
+ &:hover {
+ color: #ffffff;
+ background: rgba(255, 255, 255, 0.1);
+ border-bottom: 1.5px solid #ffffff;
+ }
+ }
+
+ .item-active {
+ color: #ffffff;
+ background: rgba(255, 255, 255, 0.1);
+ border-bottom: 1.5px solid #ffffff;
+ }
+}
+
+// 标题
+.title-content-item {
+ line-height: 22px;
+ font-family: Noto Sans S Chinese;
+ font-weight: 500;
+ color: #ffffff;
+ font-size: var(--titleSize);
+ border-bottom: 1px solid rgba(0, 98, 217, 0.4);
+ padding: 10px 15px;
+ position: relative;
+
+ &::after {
+ content: "";
+ display: block;
+ width: 4px;
+ height: var(--titleSize);
+ background: linear-gradient(167.47deg, #a2e1f9 0%, #45c3f3 100%);
+ border-radius: 2px 0px 2px 0px;
+ position: absolute;
+ top: 13px;
+ left: 0px;
+ }
+}
+
+.drawer-head {
+ font-size: 20px;
+ font-family: Noto Sans S Chinese;
+ font-weight: 300;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+
+ span {
+ margin-right: 5px;
+ }
+}
+
+.chat-source {
+ color: #80b7ff;
+ background-color: rgba(85, 159, 254, 0.3);
+ border-radius: 5px;
+ padding: 1px 5px;
+ text-align: center;
+ width: auto;
+ height: auto;
+ font-size: var(--fontSizes);
+ display: inline-block;
+ margin: 5px;
+ cursor: pointer;
+}
+
+.markdown-body hr {
+ height: 1.5px !important;
+ background: #559ffe !important;
+ border-color: #559ffe !important;
+}
+
+// 文字省略号
+.text-ellipsis {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ /* 限制显示的行数 */
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.ellipsis1 {
+ -webkit-line-clamp: 1;
+}
+
+.ellipsis2 {
+ -webkit-line-clamp: 2;
+}
+
+.ellipsis3 {
+ -webkit-line-clamp: 3;
+}
+
+.ellipsis4 {
+ -webkit-line-clamp: 4;
+}
+
+.ellipsis5 {
+ -webkit-line-clamp: 5;
+}
+
+// 逻辑图样式
+.treemap-darkblue {
+ width: 100%;
+ // border: 2px solid;
+ // height: 100%;
+ color: lightblue;
+ padding: 0px;
+ user-select: none;
+ // padding: 10px;
+ border: 1px solid;
+ border-radius: 4px;
+ background-color: var(--chat-item-bg);
+ box-sizing: border-box;
+ box-shadow: 0px 0px 9px rgba(252, 131, 131, 0.27);
+
+ .treemap-title {
+ border-radius: 0;
+ }
+
+ .treemap-body {
+ color: #ffffff;
+ }
+
+ .node-red {
+ background: linear-gradient(
+ 90deg,
+ rgba(245, 34, 45, 0.5) 0%,
+ rgba(138, 0, 8, 0.3) 100%
+ );
+ box-shadow: 0px 1px var(--fontSize) rgba(0, 0, 0, 0.08);
+ }
+
+ .node-orange {
+ background: rgba(255, 153, 0, 0.5);
+ border-bottom: 1px solid;
+ border-color: #ff9900;
+ }
+
+ .node-yellow {
+ background: rgba(255, 196, 0, 0.5);
+ border-bottom: 1px solid;
+ border-color: #ffc400;
+ }
+
+ .node-lightblue {
+ background: rgba(69, 195, 243, 0.5);
+ border-bottom: 1px solid;
+ border-color: #45c3f3;
+ }
+
+ .node-blue {
+ background: rgba(0, 121, 254, 0.5);
+ border-bottom: 1px solid;
+ border-color: #0079fe;
+ }
+
+ .node-purple {
+ background: rgba(125, 80, 255, 0.5);
+ border-bottom: 1px solid;
+ border-color: #7d50ff;
+ }
+}
+
+.pulse-content {
+ width: 34px;
+ height: 40px;
+ background: url("../assets/map-icon/map-point.svg") no-repeat;
+ background-size: cover;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .user-logo {
+ width: 26px;
+ height: 26px;
+ margin-top: -5px;
+ border-radius: 50%;
+ overflow: hidden;
+ img {
+ width: 100%;
+ height: 100%;
+ }
+ }
+}
+
+.overiew-content {
+ width: 400px;
+ padding: 10px;
+ box-sizing: border-box;
+ color: #ffffff;
+ border: 1px solid #2b8ffe99;
+ border-radius: 5px;
+ background-color: rgba(3, 38, 80, 0.8);
+ position: absolute;
+ top: -117px;
+ left: 310px;
+ z-index: 500;
+
+ .font-weight-bold {
+ font-size: 17px;
+ font-family: "Noto Sans S Chinese";
+ font-weight: 700;
+ }
+ .overiew-head {
+ padding: 10px 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ .user-logo {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ overflow: hidden;
+ img {
+ width: 100%;
+ height: 100%;
+ vertical-align: middle;
+ object-fit: cover;
+ }
+ }
+ .user-right {
+ flex: 1;
+ margin-left: 10px;
+ .user-title {
+ font-family: Noto Sans S Chinese;
+ font-weight: 500;
+ color: #fff;
+ font-size: 18px;
+ line-height: 24px;
+ }
+ .user-text {
+ font-family: Noto Sans S Chinese;
+ font-weight: 300;
+ color: #fff;
+ font-size: 16px;
+ line-height: 26px;
+ margin-top: 4px;
+ }
+ }
+ }
+ .overiew-body {
+ padding: 10px 0;
+ .typeLabel {
+ margin: 5px;
+ line-height: 28px;
+ font-family: Noto Sans S Chinese;
+ font-weight: 500;
+ color: #55a6fe;
+ font-size: 16px;
+ display: flex;
+ align-items: center;
+ .details-logo {
+ width: 20px;
+ height: 14px;
+ background: url("../assets/icon/make-logo.svg") no-repeat;
+ background-size: cover;
+ margin-right: 5px;
+ }
+ }
+ .text-content {
+ font-family: Noto Sans S Chinese;
+ font-size: 16px;
+ padding: 0 10px;
+ }
+ }
+
+ .item-line {
+ border-bottom: 1px solid #2b8ffe99;
+ }
+
+ &::after {
+ content: "";
+ width: 200px;
+ height: 80px;
+ display: block;
+ background: url("../assets/map-icon/line-logo.png") no-repeat;
+ z-index: 1000;
+ position: absolute;
+ top: 75px;
+ left: -112px;
+ }
+
+ .text-content {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 3;
+ overflow: hidden;
+ word-break: break-all; /* 自动换行 */
+ white-space: normal; /* 允许正常换行 */
+ }
+}
+
+.item-line {
+ height: 1px;
+ background: rgba(85, 159, 254, 0.25);
+ margin: 10px 0;
+}
+
+.leaflet-container a.leaflet-popup-close-button {
+ position: absolute;
+ top: -115px;
+ right: -305px;
+ z-index: 600;
+}
+
+.leaflet-popup-content-wrapper,
+.leaflet-popup-tip {
+ background: transparent;
+ box-shadow: none;
+}
+
+.leaflet-popup-tip {
+ background: transparent !important;
+}
+.leaflet-left {
+ z-index: 100;
+}
+// 滚动条样式
+// 滚动条整体样式
+::-webkit-scrollbar {
+ /*滚动条整体样式*/
+ width: 6px !important;
+ height: 6px;
+}
+
+::-webkit-scrollbar-thumbk {
+ /*滚动条里面小方块*/
+ background: #ddd;
+ background: transparent;
+ border-radius: 3px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ /*滚动条里面小方块*/
+ background: #555;
+ border-radius: 3px;
+}
+
+::-webkit-scrollbar-track {
+ /*滚动条里面轨道*/
+ background: transparent;
+ border-radius: 3px;
+}
+.t-dialog__body {
+ height: 70vh;
+ overflow-y: auto;
+}
+.t-dialog__ctx .t-dialog__position.t-dialog--top {
+ padding-top: 10vh;
+}
diff --git a/situation-overview-web/src/utils/request.js b/situation-overview-web/src/utils/request.js
new file mode 100644
index 00000000..4b6af133
--- /dev/null
+++ b/situation-overview-web/src/utils/request.js
@@ -0,0 +1,34 @@
+import axios from "axios";
+
+const request = axios.create({
+ baseURL: "/",
+ timeout: 10000000,
+});
+
+request.interceptors.request.use(
+ (config) => {
+ const token = localStorage.getItem("token");
+ // if (token) {
+ // config.headers.authorization = `Bearer admin`
+ // }
+ config.headers.Authorization = `Bearer admin`;
+ console.log(config.headers.authorization);
+
+ return config;
+ },
+ (error) => {
+ return Promise.reject(error);
+ }
+);
+
+request.interceptors.response.use(
+ (response) => {
+ return response.data;
+ },
+ (error) => {
+ console.error("Request error:", error);
+ return Promise.reject(error);
+ }
+);
+
+export default request;
diff --git a/situation-overview-web/src/views/Chart.vue b/situation-overview-web/src/views/Chart.vue
new file mode 100644
index 00000000..94534ce2
--- /dev/null
+++ b/situation-overview-web/src/views/Chart.vue
@@ -0,0 +1,128 @@
+
+
+
+
+ 图表演示
+
+
+ 首页
+ 仪表盘
+ 图表
+ 地图
+ 关系图
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/Dashboard.vue b/situation-overview-web/src/views/Dashboard.vue
new file mode 100644
index 00000000..56f153de
--- /dev/null
+++ b/situation-overview-web/src/views/Dashboard.vue
@@ -0,0 +1,130 @@
+
+
+
+
+ 仪表盘
+
+
+ 首页
+ 仪表盘
+ 图表
+ 地图
+ 关系图
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.status }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/Graph.vue b/situation-overview-web/src/views/Graph.vue
new file mode 100644
index 00000000..13c482cd
--- /dev/null
+++ b/situation-overview-web/src/views/Graph.vue
@@ -0,0 +1,91 @@
+
+
+
+
+ 关系图演示
+
+
+ 首页
+ 仪表盘
+ 图表
+ 地图
+ 关系图
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/GroupContent.vue b/situation-overview-web/src/views/GroupContent.vue
new file mode 100644
index 00000000..4aa8a101
--- /dev/null
+++ b/situation-overview-web/src/views/GroupContent.vue
@@ -0,0 +1,32 @@
+
+
+ 打开群聊
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/Home.vue b/situation-overview-web/src/views/Home.vue
new file mode 100644
index 00000000..c5ab0765
--- /dev/null
+++ b/situation-overview-web/src/views/Home.vue
@@ -0,0 +1,97 @@
+
+
+
+
+ Vue3 技术栈演示
+
+
+ 首页
+ 仪表盘
+ 图表
+ 地图
+ 关系图
+
+
+
+
+
+
欢迎使用 Vue3 技术栈
+
本项目集成了以下技术:
+
+
+
+ TDesign
+
+
+
+
+ ECharts
+ D3.js
+ @antv/g6
+
+
+
+
+ Leaflet
+
+
+
+
+ Axios
+
+
+
+
+ Vuex
+
+
+
+
+ Vite
+ Less
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/Map.vue b/situation-overview-web/src/views/Map.vue
new file mode 100644
index 00000000..b461cc37
--- /dev/null
+++ b/situation-overview-web/src/views/Map.vue
@@ -0,0 +1,71 @@
+
+
+
+
+ 地图演示
+
+
+ 首页
+ 仪表盘
+ 图表
+ 地图
+ 关系图
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/action/components/actionDetail.vue b/situation-overview-web/src/views/action/components/actionDetail.vue
new file mode 100644
index 00000000..4b555e03
--- /dev/null
+++ b/situation-overview-web/src/views/action/components/actionDetail.vue
@@ -0,0 +1,206 @@
+
+
+
+
+
+ 行动名称
+
+
+
+
+
+ {{ index + 1 }}.
+ {{ item.text }}
+
+
+
+
+
+
+ 行为体
+
+
+ 行为体内容内容内容内容内容内容内容内容内容内容内容内容
+
+
+
+
+ 预期行为
+
+
+ 预期行为内容内容内容内容内容内容内容内容内容内容内容内容
+
+
+
+
+ 原因分析
+
+
+
+
+ 支撑性目的:
+
+ 利用桃色新闻与军纪涣散议题利用桃色新闻与军纪涣散议题利用桃色新闻与军纪涣散议题,瓦解台军社会形象与民众支持基础,引发线上网红、大V等对台军……利用桃色新闻与军纪涣散议题利用桃色新闻与军纪涣散议题利用桃色新闻与军纪涣散议题,瓦解台军社会形象与民众支持基础,引发线上网红、大V等对台军……
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/action/components/bociDetail.vue b/situation-overview-web/src/views/action/components/bociDetail.vue
new file mode 100644
index 00000000..937de378
--- /dev/null
+++ b/situation-overview-web/src/views/action/components/bociDetail.vue
@@ -0,0 +1,260 @@
+
+
+
+
+
+
+
+ 波次信息主题
+
+
+ 修改
+
+
+
+
+
+
+ 信息主题:
+
+
+ 渴望被官方和主流舆论认可为两岸议题的权威声音理
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/action/index.vue b/situation-overview-web/src/views/action/index.vue
new file mode 100644
index 00000000..eba91b6a
--- /dev/null
+++ b/situation-overview-web/src/views/action/index.vue
@@ -0,0 +1,280 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
当前任务
+
+
+
+ 返回任务
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/control/components/g6Tp.vue b/situation-overview-web/src/views/control/components/g6Tp.vue
new file mode 100644
index 00000000..a9563656
--- /dev/null
+++ b/situation-overview-web/src/views/control/components/g6Tp.vue
@@ -0,0 +1,228 @@
+
+
+
+
+
diff --git a/situation-overview-web/src/views/control/components/timeline.vue b/situation-overview-web/src/views/control/components/timeline.vue
new file mode 100644
index 00000000..8e8b62e7
--- /dev/null
+++ b/situation-overview-web/src/views/control/components/timeline.vue
@@ -0,0 +1,144 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/control/components/zxtp.vue b/situation-overview-web/src/views/control/components/zxtp.vue
new file mode 100644
index 00000000..d7ba6f29
--- /dev/null
+++ b/situation-overview-web/src/views/control/components/zxtp.vue
@@ -0,0 +1,761 @@
+
+
+
+
+
+
+
+ {{ tpInfo.xdData?.actionNum}}:{{ tpInfo.xdData?.actionName }}
+
+
+
开始时间:{{ tpInfo.xdData?.actionStartTime || '---'}}
+
预计结束:{{ tpInfo.xdData?.actionStartTime || '---'}}
+
+
+
+
+
行动伴随评估等级
+
+
+
+
+
+ 评估时间:05-06 14:00:00
+
+
+
+
+
+
行动执行日志
+
+
+
+
+ {{ item.logType }}
+
+ {{ item.logTime }}
+ {{ item.logSource }}
+
+
+ {{ item.logContent }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/control/index.vue b/situation-overview-web/src/views/control/index.vue
new file mode 100644
index 00000000..026ea043
--- /dev/null
+++ b/situation-overview-web/src/views/control/index.vue
@@ -0,0 +1,2145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
执行中任务列表
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item.targetArea}}
+ {{item.urgencyLevel}}
+
+
{{ item.assName }}
+
+
+
+
+
+
{{child.actionNum}}
+
+
进度{{parseInt(child.actionProgress)}}%
+
+
+ >
+
+
+
+
暂无行动数据
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ taskInfo.current.assName }}
+
+
+
+
+
+
+
+ {{ item.logType }}
+
+ {{ item.logTime }}
+ {{ item.logSource }}
+
+
+ {{ item.logContent }}
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ taskInfo.current.assName }}
+
+
+
+
+ {{taskInfo.xqData?.targetArea}}
+ {{taskInfo.xqData?.urgencyLevel}}
+
+ 执行日志
+
+
+
+ {{ taskInfo.xqData?.assName }}
+
+
{{ taskInfo.xqData?.description }}
+
开始执行时间:{{ taskInfo.xqData?.startTime }}
+
计划结束时间:{{ taskInfo.xqData?.endTime }}
+
持续时间:{{ taskInfo.xqData?.duration }}
+
责任单位:{{ taskInfo.xqData?.unit || '暂无责任单位'}}
+
+
+
任务伴随评估等级
+
+
+
+
+
+ 评估时间:05-06 14:00:00
+
+
+
+
+
+
+
资源使用监控
+
+
+
+
+ 使用账号
+
+
+ 计划用:{{ taskInfo.xqData?.resourceUsed?.zhPlanned}}
+ 已使用:{{ taskInfo.xqData?.resourceUsed?.zhUsed}}
+ 被封禁:{{ taskInfo.xqData?.resourceUsed?.zhBaned}}
+
+
+
+
+
+ 信息弹药
+
+
+ 计划用:{{ taskInfo.xqData?.resourceUsed?.xxPlanned}}
+ 已使用:{{ taskInfo.xqData?.resourceUsed?.xxUsed}}
+ 被封禁:{{ taskInfo.xqData?.resourceUsed?.xxBaned}}
+
+
+
+
+
+ 线路通道
+
+
+ 正常中:{{ taskInfo.xqData?.resourceUsed?.xlNormal}}
+ 已使用:{{ taskInfo.xqData?.resourceUsed?.xlUsed}}
+ 异常中:{{ taskInfo.xqData?.resourceUsed?.xlError}}
+
+
+
+
+
+ 力量编组:{{ taskInfo.xqData?.resourceUsed?.forceUnit}}
+ 动用兵力:{{ taskInfo.xqData?.resourceUsed?.usedForce}}
+ 机动兵力:{{ taskInfo.xqData?.resourceUsed?.mobForce}}
+
+
+
+
+
+
+
任务执行情况
+
+
+
+
+
+
+
+
+
{{ item.actionNum }}
+
+
+
执行拓扑
+
+
+
+
+ {{taskInfo.xdData?.actionStatus}}
+
+
+
计划:{{ taskInfo.xdData?.actionPlanTime || '---' }}
+
实际:{{ taskInfo.xdData?.actionActualTime || '---' }}
+
+
+
+
+
+
+ {{bc.waveNum}}
+ {{bc.waveStatus}}
+ {{bc.allTime}}
+
+
+ 计划投送:{{bc.planTS}}
+ 已投:{{bc.alreadyTS}}
+ 未投:{{bc.noTS}}
+ 失败:{{bc.failTS}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item.logType}}
+
+ :{{item.logContent}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 选择接收单位:
+
+
+
+
+
+ 取消
+
+ 发送
+
+
+
+
+
+
+
+
+ 所有消息
+
+
+ 任务指令
+
+
+ 任务偏差
+
+
+ 资源告警
+
+
+
+
+
类型
+
时间
+
来源单位/账号
+
内容
+
操作
+
+
+
+
+ {{ item.logType }}
+
+
{{ item.logTime }}
+
{{ item.logSource }}
+
{{ item.logContent }}
+
--
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/homeOverview/index.vue b/situation-overview-web/src/views/homeOverview/index.vue
new file mode 100644
index 00000000..f29e00ac
--- /dev/null
+++ b/situation-overview-web/src/views/homeOverview/index.vue
@@ -0,0 +1,1337 @@
+
+
+
+
+
+
+
当前任务执行数量
+
+
+
+
+
+
+
+
+
+
+
+
{{item.title}}
+
{{item.number}}个
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{item.title}}
+
+
+
+
+
+
+
+
+
+
+ {{item.number}}
+
+
+
+
+
+
+
{{item.title}}
+
+
+ {{getStatusColor(item.status).text}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{item.number}}
+
+
+
+
+
+
+
+
+
+ 【{{popupData.alarmList[0].type}}】{{popupData.alarmList[0].content}}
+
+
+
+
+
+
+
+
+
+
+
执行任务列表
+
+
+
+
{{item.assProgress}}
+
进展
+
+
+
+
{{item.assName}}
+
+
{{item.urgencyLevel}}
+
+
{{item.targetArea}}
+
+
{{item.supportType}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{child.name}}
+
+
+
+
+
+ 伴随评估:
+ {{item.assEvaluateLevel}}
+ {{item.pgContent? item.pgContent.filter(pg=> pg.id === item.pgLevel)[0].content: ''}}
+
+
+
+
+
+
+ 发帖数量: {{item.platNum}}平台/{{ item.tipNum }}条
+
+
+
+
+
+
+
+ {{item.pgContent? item.pgContent.filter(pg=> pg.id === item.pgLevel)[0].content: '暂未开始评估'}}:
+ {{item.pgContent? item.pgContent.filter(pg=> pg.id === item.pgLevel)[0].num: ''}}
+
+
+
+
+
+
+
+ 计划开始: {{item.planStart}}
+ 实际开始: {{item.actualStart}}
+
+
+
+
+ 计划结束: {{item.planEnd}}
+ 持续时长: {{item.duration}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/G6NetworkGraph.vue b/situation-overview-web/src/views/reply/components/G6NetworkGraph.vue
new file mode 100644
index 00000000..beb484bf
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/G6NetworkGraph.vue
@@ -0,0 +1,1061 @@
+
+
+
+
+
+
+
+
+ {{ "行动统计" }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/all-asp.vue b/situation-overview-web/src/views/reply/components/all-asp.vue
new file mode 100644
index 00000000..95e1df78
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/all-asp.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/asp-echarts.vue b/situation-overview-web/src/views/reply/components/asp-echarts.vue
new file mode 100644
index 00000000..b20260fa
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/asp-echarts.vue
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
{{ item.name }}
+
{{ item.value }}
+
条
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/edit-group.vue b/situation-overview-web/src/views/reply/components/edit-group.vue
new file mode 100644
index 00000000..e47709fb
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/edit-group.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/reply/components/information-list.vue b/situation-overview-web/src/views/reply/components/information-list.vue
new file mode 100644
index 00000000..c0c2fe1a
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/information-list.vue
@@ -0,0 +1,668 @@
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/information-putin.vue b/situation-overview-web/src/views/reply/components/information-putin.vue
new file mode 100644
index 00000000..d2d51e7f
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/information-putin.vue
@@ -0,0 +1,610 @@
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/input-recomendations.vue b/situation-overview-web/src/views/reply/components/input-recomendations.vue
new file mode 100644
index 00000000..a42c2014
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/input-recomendations.vue
@@ -0,0 +1,210 @@
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/office.vue b/situation-overview-web/src/views/reply/components/office.vue
new file mode 100644
index 00000000..71a2efd5
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/office.vue
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/reply/components/part/Media-list.vue b/situation-overview-web/src/views/reply/components/part/Media-list.vue
new file mode 100644
index 00000000..44d91771
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/Media-list.vue
@@ -0,0 +1,259 @@
+
+
+
+
+
+
+
+
+
+ {{ item?.name || '-' }}
+
+
+
+ {{ item?.mediaPositionSide || '-' }}
+ 报道数:{{ item?.InfoCnt || '-' }}
+
+
+
+
+
+
+
+
+ {{ itelIndex + 1 }}. {{ itel?.title || '-' }}
+
+
+
+ 命中关键词:
+ {{ itel?.keyword || '-' }}
+
+
+ 相似度:
+ {{ itel?.relevancy || '-' }}
+
+
+
+ {{ itel?.content || '-' }}
+
+
+ {{ itel?.publishTime || '-' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/reply/components/part/action-unit.vue b/situation-overview-web/src/views/reply/components/part/action-unit.vue
new file mode 100644
index 00000000..33c0bf9f
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/action-unit.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/part/big-v.vue b/situation-overview-web/src/views/reply/components/part/big-v.vue
new file mode 100644
index 00000000..c70d4590
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/big-v.vue
@@ -0,0 +1,287 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item?.account }}
+
+
+
+
+ 平台: {{ item?.asp }}
+
+
+
+ 粉丝数:
+ {{ item?.fanCnt?.toLocaleString() }}
+
+
+
+ 参与{{
+ item?.infoCnt?.toLocaleString() || 0
+ }} 次
+
+
+
+
+ {{
+ item?.content
+ }}
+
+
+
{{ item?.publishTime }}
+
+
+ 转发量{{
+ item?.forwardCnt?.toLocaleString()
+ }}
+
+
+
+ 评论量{{
+ item?.commentCnt?.toLocaleString()
+ }}
+
+
+
+ 点赞量{{
+ item?.likeCnt?.toLocaleString()
+ }}
+
+
+
+ 阅读量{{
+ item?.readCnt?.toLocaleString()
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/part/hot-trend.vue b/situation-overview-web/src/views/reply/components/part/hot-trend.vue
new file mode 100644
index 00000000..f21f285e
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/hot-trend.vue
@@ -0,0 +1,514 @@
+
+
+
+
+ {{ content }}
+ 复制
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/part/media-port.vue b/situation-overview-web/src/views/reply/components/part/media-port.vue
new file mode 100644
index 00000000..5e8b3798
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/media-port.vue
@@ -0,0 +1,395 @@
+
+
+
+
+
+
{{ padZero(index + 1, 2) }}.
+
+ {{ item?.topicAgg || "" }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ (item?.percent || 0) + "%" }}
+
+
+
+
+ {{ item?.sourceList?.length || 0 }} 家
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/part/narr-theme.vue b/situation-overview-web/src/views/reply/components/part/narr-theme.vue
new file mode 100644
index 00000000..8d77209f
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/narr-theme.vue
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+ {{ site.extractTopic }}
+
+
+
+ {{
+ site[key]?.toLocaleString() || 0
+ }}
+ {{ name }}
+
+
+
+
+
+
+
+
+
+
+
+
新增主题
+
+
+ {{ site.extractTopic }}
+
+
+
+ {{
+ site[key]?.toLocaleString() || 0
+ }}
+ {{ name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/part/relevantActors-item.vue b/situation-overview-web/src/views/reply/components/part/relevantActors-item.vue
new file mode 100644
index 00000000..65412e88
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/relevantActors-item.vue
@@ -0,0 +1,274 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ site_.entityName }} {{
+ site_.count?.toLocaleString() || 0
+ }}
+
+
+ {{ site_?.text }}
+
+
+
+
+
+ {{ site_.name }}
+
+ 规模{{ site_.scale?.toLocaleString() || 0 }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/part/stand-area.vue b/situation-overview-web/src/views/reply/components/part/stand-area.vue
new file mode 100644
index 00000000..acb040ee
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/stand-area.vue
@@ -0,0 +1,202 @@
+
+
+
+
+
+ {{ font == "front" ? "XD前" : "XD后" }}
+
+
+
+
+
+
+ XD观点 ({{
+ "共" + (listTopic.length || 0) + "条"
+ }})
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/part/table-line.vue b/situation-overview-web/src/views/reply/components/part/table-line.vue
new file mode 100644
index 00000000..34f24414
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/table-line.vue
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/reply/components/part/trans-plat.vue b/situation-overview-web/src/views/reply/components/part/trans-plat.vue
new file mode 100644
index 00000000..d741e18c
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/trans-plat.vue
@@ -0,0 +1,302 @@
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/part/word-cloud.vue b/situation-overview-web/src/views/reply/components/part/word-cloud.vue
new file mode 100644
index 00000000..e791689d
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/part/word-cloud.vue
@@ -0,0 +1,240 @@
+
+
+
+
+ {{ font == "front" ? "XD前" : "XD后" }}
+
+
+
+
+
+
{{ (index + 1).toString().padStart(2, "0") }}
+
{{ item.name }}
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/pie-echarts.vue b/situation-overview-web/src/views/reply/components/pie-echarts.vue
new file mode 100644
index 00000000..07bfba49
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/pie-echarts.vue
@@ -0,0 +1,127 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/reply/components/pivotket.vue b/situation-overview-web/src/views/reply/components/pivotket.vue
new file mode 100644
index 00000000..a7d7d986
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/pivotket.vue
@@ -0,0 +1,348 @@
+
+
+
+ 全部({{ jdData?.count }} 个)
+ 实际撬动({{ jdData?.isTypeCount }} 个)
+ 未撬动({{ jdData?.noTypeCount }} 个)
+
+
+
+
+
+
+
+
+
+ {{
+ itm.uname || ""
+ }}
+
+ {{ dealImportType(itm.importType) }}
+ {{ itm.isType && itm.isType == 1 ? "已撬动" : "未撬动" }}
+
+
+
+
+
平台
+
+
+
{{ itm.asp }}
+
+
+
+
+
粉丝数
+
+ {{ itm.fanCnt == "-1" ? "0" : itm.fanCnt || "0" }}
+
+
+
+
+
互动量
+
{{ itm.sumCnt || "0" }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/point-list.vue b/situation-overview-web/src/views/reply/components/point-list.vue
new file mode 100644
index 00000000..af57d285
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/point-list.vue
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item.content }}
+
+
+
+
+ {{ 'TOP' + item.top }}
+
+
+
+
+ 观点占比
+ {{ item.percent + '%' }}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/reply/components/pointDialog-morelevel.vue b/situation-overview-web/src/views/reply/components/pointDialog-morelevel.vue
new file mode 100644
index 00000000..ecccbac0
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/pointDialog-morelevel.vue
@@ -0,0 +1,469 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ index + 1 }}. {{ item?.content }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/prehensiveEvaluation-fangan.vue b/situation-overview-web/src/views/reply/components/prehensiveEvaluation-fangan.vue
new file mode 100644
index 00000000..d997972d
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/prehensiveEvaluation-fangan.vue
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+ {{
+ item.name == "引发主流媒体报道"
+ ? item.name.slice(2, item.name.length)
+ : item.name
+ }}
+
+
{{ item.expectV }}/{{ item.realV }}
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/prehensiveEvaluation.vue b/situation-overview-web/src/views/reply/components/prehensiveEvaluation.vue
new file mode 100644
index 00000000..cb933570
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/prehensiveEvaluation.vue
@@ -0,0 +1,458 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
综合分析
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/radar-echart.vue b/situation-overview-web/src/views/reply/components/radar-echart.vue
new file mode 100644
index 00000000..99ef45f6
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/radar-echart.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/reachAudience.vue b/situation-overview-web/src/views/reply/components/reachAudience.vue
new file mode 100644
index 00000000..905338c8
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/reachAudience.vue
@@ -0,0 +1,344 @@
+
+
+
+
+
+
+
+
+
+ {{ index <= 8 ? "0" + (index + 1) : index + 1 }}.
+ {{ itm.audience }}
+ 实际触达
+
+
+
账号
+
+
+
+
+
+ {{ itc.accountName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/replyComparison-one.vue b/situation-overview-web/src/views/reply/components/replyComparison-one.vue
new file mode 100644
index 00000000..5083fb77
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/replyComparison-one.vue
@@ -0,0 +1,636 @@
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/replyComparison-two.vue b/situation-overview-web/src/views/reply/components/replyComparison-two.vue
new file mode 100644
index 00000000..c7752d32
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/replyComparison-two.vue
@@ -0,0 +1,864 @@
+
+
+
+
+
+
+
+ {{ nv.name }}
+
+
+
+
+
+
+
+
+
+
+ 共
+ {{
+ reportList.length
+ }}
+ 个XD
+
+
+ 对比指标
+ {{
+ getShowLength()
+ }}
+ 个
+
+
+
+
+
+
+
+
+
+
+ {{
+ leftId == itm.actionId ? "已钉住" : "钉在左侧" }}
+
+
+
+
+
+
+
+ 本XD
+
+
+
+
+ {{ itm.actionName ? itm.actionName[0] : "" }}
+
+
{{ itm.actionName }}
+
+
+
+ 结束时间:{{
+ itm.endTime
+ ? dayjs(itm.endTime).format("YYYY-MM-DD hh:mm")
+ : "-"
+ }}
+
+
+ 所属任务:{{ itm.taskName ? itm.taskName : "-" }}
+
+
+ XD等级:{{
+ itm.bslevelId ? numberList[itm.bslevelId - 1] + "级" : "-"
+ }}
+ XD类型:-
+
+
+
+
+
+
+ 移动对比
+
+
+
+
+
+
+
+
+
+
+
+ {{ itmc.cname }}
+
+
+
+
+
+ {{ cc.paramName }}
+
+
+
+
+ 指标说明:{{
+ cc.metricAbstract ? cc.metricAbstract : "无"
+ }}
+
+
+
+
+
+
+
+
+
+
+
{{ k.name ? k.name : k }}
+
+ {{ k.value }} {{ k.unit }}
+
+
+
+
+ {{ itm ? itm : "-" }}
+ {{ itm && cc.metricUnit ? cc.metricUnit : "" }}
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/reply/components/replyComparison.vue b/situation-overview-web/src/views/reply/components/replyComparison.vue
new file mode 100644
index 00000000..bc5a46e7
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/replyComparison.vue
@@ -0,0 +1,54 @@
+
+
+
+
+ 纵向对比
+
+ 横向对比
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/rzyx.vue b/situation-overview-web/src/views/reply/components/rzyx.vue
new file mode 100644
index 00000000..628e3ec7
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/rzyx.vue
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+
+
+
+ 同向
+ {{ pointData?.positiveCmtPercent || 0 }}%
+
+
+
+ 负向
+ {{ pointData?.negativeCmtPercent || 0 }}%
+
+
+
+ 中立
+ {{ pointData?.neutralityCmtPercent || 0 }}%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/svg-link.vue b/situation-overview-web/src/views/reply/components/svg-link.vue
new file mode 100644
index 00000000..77f6bb28
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/svg-link.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/reply/components/table-two.vue b/situation-overview-web/src/views/reply/components/table-two.vue
new file mode 100644
index 00000000..ae59a699
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/table-two.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/task-detail.vue b/situation-overview-web/src/views/reply/components/task-detail.vue
new file mode 100644
index 00000000..64738044
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/task-detail.vue
@@ -0,0 +1,286 @@
+
+
+
+
+
已完成
+
{{ data.taskName }}
+
+
+
+ {{ data?.bslevelName || "无评级" }}
+
+ {{ dealWithTypeNumber.rankOpt(data?.rank).name || "" }}
+ {{
+ data?.sampleAuth == 1 ? "私密" : "公开"
+ }}
+ {{
+ dealDirection[data.direction] || "--"
+ }}
+
+
+
+
+ 任务时间:
+ {{ dayjs(data.createTime).format("YYYY年MM月DD日") || "--"
+ }}{{ data.createTime ? "-" : ""
+ }}{{ dayjs(data.collectTime).format("YYYY年MM月DD日") || "--" }}
+
+
+
+
任务说明:
+
+ {{ data.coreAim || "-" }}
+
+
+
+
+
+
+
+
+
+
+ XD{{ index + 1 }}:
+ {{ item?.level || "无评级" }}
+
+
+
+ {{ item?.actionDes || "-" }}
+
+
+
+ XD时间:
+ {{ dayjs(item.assessStartTime).format("YYYY年MM月DD日") || "--"
+ }}{{ item.assessStartTime ? "-" : ""
+ }}{{
+ dayjs(item.assessEndTime).format("YYYY年MM月DD日") || "--"
+ }}
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/task-list.vue b/situation-overview-web/src/views/reply/components/task-list.vue
new file mode 100644
index 00000000..52f32955
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/task-list.vue
@@ -0,0 +1,281 @@
+
+
+
+
+
+
+
+
+
+
+ 全部
+ 我的
+
+
+
+
+
+
+
+
已完成
+
{{ item.taskName }}
+
+
+
+ {{ item.bsLevel ? item.bsLevel.name : "无评级" }}
+
+ {{ dealWithTypeNumber.rankOpt(item?.rank).name || "" }}
+
+ {{
+ item?.sampleAuth == 1 ? "私密" : "公开"
+ }}
+
+
+
+ {{
+ dealDirection[item.direction] || "--"
+ }}
+
+
+
+
+ 任务时间:
+ {{ dayjs(item.beTime).format("YYYY年MM月DD日") || "--"
+ }}{{ item.beTime ? "-" : ""
+ }}{{
+ dayjs(item.afTime).format("YYYY年MM月DD日") || "--"
+ }}
+
+
+
+
任务说明:
+
+ {{ item.coreAim||'-' }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/title-logo.vue b/situation-overview-web/src/views/reply/components/title-logo.vue
new file mode 100644
index 00000000..eaffea40
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/title-logo.vue
@@ -0,0 +1,37 @@
+
+
+ {{ title }}
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/translateEnTo.js b/situation-overview-web/src/views/reply/components/translateEnTo.js
new file mode 100644
index 00000000..1f38415e
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/translateEnTo.js
@@ -0,0 +1,37 @@
+import axios from "@/utils/request";
+
+// 翻译方法 content数组
+export default async function translateEnTo(content) {
+ if (content && content.length > 0) {
+
+ // 并行调用n个接口
+ let promises = content.map(async (item) => {
+ if(item){
+ let res = await fetchData(item)
+ return res?.data?.body?.target || null
+ }else{
+ // return item || null
+ return ''
+ }
+
+ })
+ const updatedArray = await Promise.all(promises);
+ return updatedArray
+ } else {
+ return null
+ }
+}
+
+async function fetchData(item) {
+ return new Promise((resolve) => {
+ // 需要改成实际ip和接口,这个暂时用的其他占位
+ let params = {
+ "id": "string",
+ "type": "encn",
+ content: item
+ }
+ const res = axios.post('http://127.0.0.1:18890/' + "/transfer/langDetectTrans", params)
+ resolve(res)
+ });
+}
+
diff --git a/situation-overview-web/src/views/reply/components/truncatedTooltipItem.vue b/situation-overview-web/src/views/reply/components/truncatedTooltipItem.vue
new file mode 100644
index 00000000..9ca7047d
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/truncatedTooltipItem.vue
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/reply/components/type.js b/situation-overview-web/src/views/reply/components/type.js
new file mode 100644
index 00000000..43ff2f36
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/type.js
@@ -0,0 +1,467 @@
+
+
+// 直播视频状态
+const liveVideoOpt = {
+ 0: {
+ name: '未分析',
+ color: '#c1c1c1'
+ },
+ 1: {
+ name: '分析中',
+ color: '#80bcff'
+ },
+ 2: {
+ name: '分析完成',
+ color: '#74e4bc'
+ },
+ 3: {
+ name: '分析失败',
+ color: '#bcb5b9'
+ }
+}
+
+const dealWithTypeNumber = {
+ relationShip(value) {
+ switch (value) {
+ case 0:
+ return '未研判'
+ case 1:
+ return '相关'
+ case 2:
+ return '不相关'
+ default:
+ ''
+ }
+ },
+ infoDir(value) {
+ switch (value) {
+ case 0:
+ return '同向'
+ case 1:
+ return '中立'
+ case 2:
+ return '负向'
+ default:
+ ''
+ }
+ },
+ hotLevelList: [
+ {
+ name: '热帖',
+ value: 8
+ },
+ {
+ name: '爆贴',
+ value: 9
+ },
+ {
+ name: '普通贴',
+ value: 7
+ }
+ ],
+ infoDirStyle(value) {
+ switch (value) {
+ case 0:
+ return {
+ color: 'rgb(122, 202, 164)',
+ backgroundColor: 'rgba(122, 202, 164, 0.2)'
+ }
+ case 1:
+ return {
+ color: 'rgb(200, 205, 213)',
+ backgroundColor: 'rgba(200, 205, 213,0.2)'
+ }
+ case 2:
+ return {
+ color: 'rgb(245, 164, 68)',
+ backgroundColor: 'rgba(245, 164, 68,0.2)'
+ }
+ default:
+ ''
+ }
+ },
+ infoRelationShipStyle(value) {
+ switch (value) {
+ case 0:
+ return {
+ color: 'rgb(161, 166, 182)',
+ backgroundColor: 'rgb(161, 166, 182, 0.2)'
+ }
+ case 1:
+ return {
+ color: 'rgb(122, 202, 164)',
+ backgroundColor: 'rgba(122, 202, 164, 0.2)'
+ }
+ case 2:
+ return {
+ color: 'rgb(161, 166, 182)',
+ backgroundColor: 'rgb(161, 166, 182, 0.2)'
+ }
+ }
+ },
+ infoArrData: [
+ {
+ name: '同向',
+ value: 0
+ },
+ {
+ name: '中立',
+ value: 1
+ },
+ {
+ name: '负向',
+ value: 2
+ }
+ ],
+ infoArrDataFilter: [
+ {
+ name: '全部',
+ value: null
+ },
+ {
+ name: '同向',
+ value: 0
+ },
+ {
+ name: '中立',
+ value: 1
+ },
+ {
+ name: '负向',
+ value: 2
+ }
+ ],
+ infoArrDataFilter1: [
+ {
+ name: '全部',
+ value: 4
+ },
+ {
+ name: '泛蓝',
+ value: 1
+ },
+ {
+ name: '泛绿',
+ value: 2
+ },
+ {
+ name: '中立',
+ value: 3
+ },
+ {
+ name: '其他',
+ value: 0
+ }
+ ],
+ infoArrDataFilter2(type) {
+ switch (type) {
+ case 4:
+ return '全部'
+ case 3:
+ return '中立'
+ case 2:
+ return '泛绿'
+ case 1:
+ return '泛蓝'
+ case 0:
+ return '其他'
+ }
+ },
+ relationArrData: [
+ {
+ type: '未研判',
+ value: 0
+ },
+ {
+ type: '相关',
+ value: 1
+ },
+ {
+ type: '不相关',
+ value: 2
+ }
+ ],
+ relationArrStatus: [
+ {
+ type: '已纳入评估数据池',
+ value: 1
+ },
+ {
+ type: '未纳入评估数据池',
+ value: 0
+ }
+ ],
+ relationArrDataFilter: [
+ {
+ type: '全部',
+ value: null
+ },
+ {
+ type: '未研判',
+ value: 0
+ },
+ {
+ type: '相关',
+ value: 1
+ },
+ {
+ type: '不相关',
+ value: 2
+ }
+ ],
+ shortcuts: [
+ {
+ text: '一周',
+ value: () => {
+ const end = new Date()
+ const start = new Date()
+ start.setDate(start.getDate() - 7)
+ return [start, end]
+ }
+ },
+ {
+ text: '一个月',
+ value: () => {
+ const end = new Date()
+ const start = new Date()
+ start.setMonth(start.getMonth() - 1)
+ return [start, end]
+ }
+ },
+ {
+ text: '一个季度',
+ value: () => {
+ const end = new Date()
+ const start = new Date()
+ start.setMonth(start.getMonth() - 3)
+ return [start, end]
+ }
+ },
+ {
+ text: '一年',
+ value: () => {
+ const end = new Date()
+ const start = new Date()
+ start.setFullYear(start.getFullYear() - 1)
+ return [start, end]
+ }
+ }
+ ],
+ TSstatus(status, message) {
+ if (status == -1) {
+ return '未下发'
+ } else if (status == 1) {
+ return '已下发'
+ } else {
+ if (message == null) {
+ return '采集中'
+ } else {
+ let messageObj = JSON.parse(message)
+ if (messageObj && messageObj?.data?.msg != '') {
+ return messageObj?.data?.msg
+ } else if (messageObj && messageObj?.data == null) {
+ return messageObj?.msg
+ } else {
+ return ''
+ }
+ }
+ }
+ },
+
+ TSstatus_InformationList(item) {
+ let msg = { txt: '', status: 0, msg: '' }
+ let status = item.bsLink?.status
+ let message = item.bsLink?.responseBody
+ if (status == -1) {
+ msg.txt = '未下发'
+ msg.status = 0
+ } else if (
+ (item.linkStatus == 0 && item.bsLink == null) ||
+ status == 0 ||
+ (item.linkStatus == 0 && status == 1)
+ ) {
+ msg.txt = '采集中'
+ msg.status = 1
+ } else if (
+ (item.linkStatus == 1 && item.bsLink == null) ||
+ (status == 1 && item.linkStatus == 1)
+ ) {
+ msg.txt = '已完成'
+ msg.status = 2
+ } else if (status == 2) {
+ msg.txt = '采集失败'
+ msg.status = 3
+ let messageObj = JSON.parse(message)
+ if (messageObj && messageObj?.data?.msg != '') {
+ msg.msg = messageObj?.data?.msg
+ } else if (messageObj && messageObj?.data == null) {
+ msg.msg = messageObj?.msg
+ } else {
+ msg.msg = ''
+ }
+ }
+ return msg
+ },
+
+ // 实时播报/行动脉络 infoType值的含义
+ infoType(value) {
+ const typeName = {
+ 1: 'ts信息',
+ 2: '官方回应',
+ 3: '线下活动',
+ 4: '首发大V',
+ 5: '关键节点',
+ 6: '首发媒体',
+ 7: '首发大V关键节点',
+ 8: '官方媒体',
+ // 260120 修改 增加新类型
+ 9: '首发账号'
+ }
+ return typeName[value] || ''
+ },
+
+ // 关键词类型
+ wordType(value) {
+ let opt = {
+ 0: {
+ name: '手动配置',
+ style: {
+ background: 'rgba(126, 187, 255, 0.2)',
+ 'border-color': '#7ebbff',
+ color: '#60a5e1'
+ }
+ },
+ 1: {
+ name: '自动推荐',
+ style: {
+ background: 'rgba(240, 174, 126, 0.2)',
+ 'border-color': '#f0ae7e',
+ color: '#db9265'
+ }
+ },
+ 2: {
+ name: '信息推荐',
+ style: {
+ background: 'rgba(149, 235, 204, 0.2)',
+ 'border-color': '#95ebcc',
+ color: '#7eb19b'
+ }
+ },
+ 3: {
+ name: '引接方案',
+ style: {
+ background: 'rgba(252, 163, 141, 0.2)',
+ 'border-color': '#fca38d',
+ color: '#d37d6b'
+ }
+ },
+ 4: {
+ name: '引接生成',
+ style: {
+ background: 'rgba(174, 212, 167, 0.2)',
+ 'border-color': '#b8e1b1',
+ color: '#9aba8c'
+ }
+ }
+ }
+
+ return (
+ opt[value] || {
+ name: '',
+ style: null
+ }
+ )
+ },
+
+ positionStyle(type) {
+ console.log(type)
+ const obj = {
+ 同向: ['#7acaa4', 'rgba(122,202,164,0.15)'],
+ 中立: ['#c8cdd5', 'rgba(161,166,182,0.15)'],
+ 负向: ['#f5a444', 'rgba(245,164,68,0.15)']
+ }
+ return obj[type] || ['#c8cdd5', 'rgba(161,166,182,0.15)']
+ },
+
+ // 卡片紧急程度配置
+ rankOpt(type) {
+ let opt = {
+ 0: {
+ name: '一般',
+ color:['#bdb176', 'rgba(220, 214, 152, 0.2)']
+ },
+ 1: {
+ name: '重要',
+ color:['#db9265', 'rgba(240, 174, 126, 0.2)']
+ },
+ 2: {
+ name: '紧急',
+ color:['#d37d6b', 'rgba(252, 163, 141, 0.2)']
+ },
+ 3: {
+ name: '加急',
+ color: ['#cc686f', 'rgba(250, 143, 148, 0.2)']
+ }
+ }
+ return opt[type] || { name: '暂无', color: ['', ''] }
+ },
+
+ getLiveVideoOpt(type) {
+ return liveVideoOpt[type] || { name: '--中', color: '#c1c1c1' }
+ },
+
+ liveVideoOpt: liveVideoOpt,
+
+ // 卡片等级颜色
+ levelColor(level) {
+ switch (level) {
+ case '一级':
+ return '#667dff'
+ case '二级':
+ return '#006dcc'
+ case '三级':
+ return '#00b35f'
+ case '四级':
+ return '#ccad00' //"#f7e685";
+ case '五级':
+ return '#ffa333'
+ case '六级':
+ return '#ff6666'
+ default:
+ return '#a6a6a6'
+ }
+ },
+
+ // 行动状态颜色
+ actionStatusStyle(status) {
+ let opt = {
+ 进行中: {
+ background: 'var(--warning-bg-color)',
+ border: '1px solid var(--warning-border-color)',
+ color: 'var(--warning-text-color)'
+ },
+ 未开始: {
+ 'border-radius': '2px',
+ background: 'var(--default-bg-color)',
+ border: '1px solid var(--default-border-color)',
+ color: 'var(--default-text-color)'
+ },
+ 已完成: {
+ 'border-radius': '2px',
+ background: 'var(--success-bg-color)',
+ border: '1px solid var(--success-border-color)',
+ color: 'var(--success-text-color)'
+ },
+ 已结束: {
+ 'border-radius': ' 2px',
+ background: 'var(--success-bg-color)',
+ border: '1px solid var(--success-border-color)',
+ color: 'var(--success-text-color)'
+ }
+ }
+
+ return opt[status]
+ }
+}
+
+export default dealWithTypeNumber
diff --git a/situation-overview-web/src/views/reply/components/vulnerability.vue b/situation-overview-web/src/views/reply/components/vulnerability.vue
new file mode 100644
index 00000000..ffa2fc76
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/vulnerability.vue
@@ -0,0 +1,408 @@
+
+
+
+ {{ tab }}
+
+
+
+
+
+ 列表
+
+
+
+
+
+ {{ itm.accountName || itm.name }}
+
+
+ 平台:{{ itm.platform }} 粉丝数:{{ itm.total }}
+
+
+ 标签:{{ itd }}
+
+
+
账号:
+
+ {{ itAc.accountName }}
+
+
+
+
+
+
+
+
+
+ 脆弱点
+
+
+
+
+
账号描述
+
+
+ {{
+ navList.filter((v) => v.accountId == navAccountId)[0]
+ ?.b2Description || "无"
+ }}
+
+
+
+
+
+
+ 脆弱点{{ icIndex + 1 }}.
+ {{ itm }}
+
+
+
+
+
脆弱点分析
+
+ {{ analysisData || "无" }}
+
+
+
+ {{
+ navList.filter((v) => v.id == navAccountId)[0]?.crdReason ||
+ "无"
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/xd-trend.vue b/situation-overview-web/src/views/reply/components/xd-trend.vue
new file mode 100644
index 00000000..33aded8a
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/xd-trend.vue
@@ -0,0 +1,219 @@
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/components/xdcloud.vue b/situation-overview-web/src/views/reply/components/xdcloud.vue
new file mode 100644
index 00000000..fb86023b
--- /dev/null
+++ b/situation-overview-web/src/views/reply/components/xdcloud.vue
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/reply/index.vue b/situation-overview-web/src/views/reply/index.vue
new file mode 100644
index 00000000..cd900d25
--- /dev/null
+++ b/situation-overview-web/src/views/reply/index.vue
@@ -0,0 +1,489 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
评估复盘
+
+
+ {{ taskId ? "返回任务" : "切换任务" }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 传播影响力
+
+
+
+ {{ item.name }}
+ {{ item.value }}
+
+
+
+
+ 返回
+
+
+
应用
+
+
+
+ 对比
+ 分析
+
+
+ 复盘
+ 报告
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/sentiment/index.vue b/situation-overview-web/src/views/sentiment/index.vue
new file mode 100644
index 00000000..f47ed59a
--- /dev/null
+++ b/situation-overview-web/src/views/sentiment/index.vue
@@ -0,0 +1,628 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
三情掌握
+
+
+
+ 全部
+
+
+ W方向
+
+
+ U方向
+
+
+
+
+ 返回任务
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
应用
+
+
+
+ 判断
+ 结论
+
+
+ ZZ
+ 建议
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
情况判断
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/situation-overview/index.vue b/situation-overview-web/src/views/situation-overview/index.vue
new file mode 100644
index 00000000..5f5b0378
--- /dev/null
+++ b/situation-overview-web/src/views/situation-overview/index.vue
@@ -0,0 +1,815 @@
+
+
+
+
+
+
+
+
+
+
+ HOME
+ 总览
+
+
+ 三情
+ 研判
+
+
+ 任务
+ 分析
+
+
+ 策划
+ 行动
+
+
+ 行动
+ 控制
+
+
+ 评估
+ 复盘
+
+
+
+
+
+
+
+ {{ taskData?.overview }}
+
+
+
+
+
+
+
ZZ任务
+
+
+
+
+
+ {{ taskData.overview }}
+
+
+
+
+
+
+
策划概览
+
+
+
+
+
+
+ 受众群体{{
+ actionOverview.designCount
+ }} 个
+
+
+ 关键传播节点{{
+ actionOverview.pathwayCount
+ }} 个
+
+
+
+
+
+
+ 人设数量{{
+ actionOverview.personCount
+ }} 个
+
+
+ 通道平台{{
+ actionOverview.channelCount
+ }} 个
+
+
+
+
+
+
+ 信息主题{{
+ actionOverview.respondingCount
+ }} 个
+
+
+ 投送波次{{
+ actionOverview.bociCount
+ }} 个
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/situation-overview-web/src/views/task/components/taskStatistics.vue b/situation-overview-web/src/views/task/components/taskStatistics.vue
new file mode 100644
index 00000000..58eb2b00
--- /dev/null
+++ b/situation-overview-web/src/views/task/components/taskStatistics.vue
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+ {{ labelList[i] }}:
+
+
+ {{ dataList[k][i] }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/situation-overview-web/src/views/task/index.vue b/situation-overview-web/src/views/task/index.vue
new file mode 100644
index 00000000..473dc615
--- /dev/null
+++ b/situation-overview-web/src/views/task/index.vue
@@ -0,0 +1,282 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 任务分析
+
+
+
+
+
+ 返回任务
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
任务分析
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/服务启停说明.md b/服务启停说明.md
index 348fa7e6..c7291d35 100644
--- a/服务启停说明.md
+++ b/服务启停说明.md
@@ -111,3 +111,10 @@ uv sync
```
再启动。
+
+
+{ "agent_type": "leader", "d_agent_thread_id": {
+ "查询天气的智能体": "76c95f3e-ee02-4e9b-b767-6df129a5aea5",
+ "查询黄历的智能体": "9a5536a1-5e1e-4345-b9b7-9807c31154e4",
+ "main-agent": "276eac46-1acb-47e3-ad42-8f01af51e869"
+ }, "agent_name": "main-agent", "new_message": "帮我看看今天出门合适吗", "skill_stop_names": ["查询天气的智能体","查询黄历的智能体"] }
\ No newline at end of file