From 6bb8b8b6ec366c8f0a7eb3929738cca3ec459c86 Mon Sep 17 00:00:00 2001 From: pengGgxp <98072271+pengGgxp@users.noreply.github.com> Date: Sat, 3 May 2025 06:26:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E8=81=8A=E5=A4=A9=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E7=94=9F=E6=88=90=E5=8F=AF=E8=A7=86=E5=8C=96=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E6=9B=B4=E6=96=B0=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + pywxdump/api/remote_server.py | 100 +- pywxdump/ui/src/api/base.ts | 124 +- pywxdump/ui/src/api/chat.ts | 47 + .../ui/src/components/utils/DeepSeekSet.vue | 72 + pywxdump/ui/src/views/Chat2UiSelectVue.vue | 194 ++ pywxdump/ui/src/views/Chat2UiView.vue | 2322 +++++++++-------- pywxdump/ui/src/views/other/SettingView.vue | 7 +- 8 files changed, 1710 insertions(+), 1157 deletions(-) create mode 100644 pywxdump/ui/src/components/utils/DeepSeekSet.vue diff --git a/.gitignore b/.gitignore index 5147b19..367f488 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ dist-ssr /pywxdump/ui/web/* /pywxdump/ui/web/assets/* /pywxdump/wxdump_work +test2.py diff --git a/pywxdump/api/remote_server.py b/pywxdump/api/remote_server.py index 56e02d8..20647d0 100644 --- a/pywxdump/api/remote_server.py +++ b/pywxdump/api/remote_server.py @@ -577,6 +577,22 @@ def recursive_listdir(path,list:List): +def de_weight(l1:List,l2:List): + """ + 列表去重,针对特定对象 + """ + len1 = min(len(l1), len(l2)) + len1 = len1-1 if len1 > 0 else 0 + for i in range(len1): + if l1[i]["wxid"] == l2[i]["wxid"] and l1[i]["start_time"] == l2[i]["start_time"] and l1[i]["end_time"] == l2[i][ + "end_time"]: + l1[i]["flag"] = True + l2.pop(i) + + return l1+l2 + + + @@ -592,7 +608,7 @@ def get_ai_ui_json_list(): # 遍历json文件夹,查找最后带_ai的文件 work_path = os.path.join(gc.work_path, "export", my_wxid, "json") if not os.path.exists(work_path): - return ReJson(0,body={"ui_dict_list":[],"ai_dict_list":[]}) + os.makedirs(work_path) file_list:List[str]=[] recursive_listdir(work_path,list=file_list) @@ -602,22 +618,25 @@ def get_ai_ui_json_list(): if file.split('.')[0].split('_')[-1] == 'ai': # 可进行ai可视化的文件 ui_list.append(file) + # print(ui_list) # 构造字典对象 ui_dict_list = [] for s in ui_list: - wxid = s.split('.')[0].split('_')[0] # wxid - time_start = " ".join(s.split('.')[0].split('_')[2:4]) # time start - time_end = " ".join(s.split('.')[0].split('_')[5:7]) # time end - flag = s.split('.')[0].split('_')[-1] # flag - ui_dict_list.append({"wxid": wxid, "time_start": time_start, "time_end": time_end, "flag": flag}) + wxid = s.split('\\')[-1].split('.')[0].split('_')[0] if "@" in s.split('\\')[-1] else \ + s.split('\\')[-1].split('.')[0].split('_')[1] # wxid + time_start = " ".join(s.split('\\')[-1].split('.')[0].split('_')[2:4]) if "@" in s.split('\\')[ + -1] else " ".join(s.split('\\')[-1].split('.')[0].split('_')[3:5]) # time start + time_end = " ".join(s.split('\\')[-1].split('.')[0].split('_')[5:7]) if "@" in s.split('\\')[-1] else " ".join( + s.split('\\')[-1].split('.')[0].split('_')[6:8]) # time end + ui_dict_list.append({"wxid": wxid, "start_time": time_start, "end_time": time_end, "flag": False}) # 遍历ai_json文件夹,获取所有文件名 work_path = os.path.join(gc.work_path, "export", my_wxid, "ai_json") if not os.path.exists(work_path): - return ReJson(0,body={"ui_dict_list":ui_dict_list,"ai_dict_list":[]}) + os.makedirs(work_path) file_list:List[str]=[] recursive_listdir(work_path,list=file_list) @@ -630,14 +649,23 @@ def get_ai_ui_json_list(): # 构造字典对象 for s in ai_list: - wxid = s.split('.')[0].split('_')[0] # wxid - time_start = " ".join(s.split('.')[0].split('_')[2:4]) # time start - time_end = " ".join(s.split('.')[0].split('_')[5:7]) # time end - ai_dict_list.append({"wxid": wxid, "time_start": time_start, "time_end": time_end}) + wxid = s.split('\\')[-1].split('.')[0].split('_')[0] if "@" in s.split('\\')[-1] else \ + s.split('\\')[-1].split('.')[0].split('_')[1] # wxid + time_start = " ".join(s.split('\\')[-1].split('.')[0].split('_')[2:4]) if "@" in s.split('\\')[ + -1] else " ".join(s.split('\\')[-1].split('.')[0].split('_')[3:5]) # time start + time_end = " ".join(s.split('\\')[-1].split('.')[0].split('_')[5:7]) if "@" in s.split('\\')[-1] else " ".join( + s.split('\\')[-1].split('.')[0].split('_')[6:8]) # time end + ai_dict_list.append({"wxid": wxid, "start_time": time_start, "end_time": time_end, "flag": True}) + # # 合并两个字典列表 + # dict_list = ui_dict_list + ai_dict_list + # print(ui_dict_list) + # print(ai_dict_list) + # 去重 + dict_list = de_weight(ui_dict_list,ai_dict_list) - return ReJson(0,body={"ui_dict_list":ui_dict_list,"ai_dict_list":ai_dict_list}) + return ReJson(0,body={"items":dict_list}) @@ -646,6 +674,9 @@ def get_file_path(work_path: str, file_name: str) -> str | None: """ 获取ai_json文件路径 """ + # 遍历文件夹内的所有文件,找到对应文件名的文件路径 + + path_list = os.listdir(work_path) for path in path_list: full_path = os.path.join(work_path, path) @@ -659,21 +690,20 @@ def get_file_path(work_path: str, file_name: str) -> str | None: class FileNameRequest(BaseModel): wxid: str - start_time: int - end_time: int + start_time: str + end_time: str @rs_api.api_route('/db_to_ai_json', methods=["GET", 'POST']) def db_to_ai_json(file_name: FileNameRequest = Body(..., embed=True)): """ 导出聊天记录到ai_json """ - start_time = file_name.start_time /1000.0 - end_time = file_name.end_time /1000.0 + start_time = file_name.start_time + end_time = file_name.end_time wxid = file_name.wxid - start_time = datetime.datetime.fromtimestamp(float(start_time)).strftime("%Y-%m-%d %H:%M:%S") #转换成日期格式 - end_time = datetime.datetime.fromtimestamp(float(end_time)).strftime("%Y-%m-%d %H:%M:%S") - file_name = wxid + '_mini_' + start_time.replace(' ', '_').replace(':', '-') + '_' + end_time.replace(' ', '_').replace(':', '-') + '_ai' + + file_name = wxid + '_mini_' + start_time.replace(' ', '_').replace(':', '-') + '_to_' + end_time.replace(' ', '_').replace(':', '-') + '_ai' # file_name = wxid + '_aiyes_' + start_time.replace(' ', '_').replace(':', '-') + '_' + end_time.replace(' ', '_').replace(':', '-') file_name = file_name + '.json' @@ -682,8 +712,11 @@ def db_to_ai_json(file_name: FileNameRequest = Body(..., embed=True)): my_wxid = gc.get_conf(gc.at, "last") if not my_wxid: return ReJson(1001, body="my_wxid is required") + + result = get_file_path(os.path.join(gc.work_path, "export", my_wxid, "json"), file_name) + if result is None: return ReJson(1002, body=f"file not found: {file_name}") @@ -700,7 +733,7 @@ def db_to_ai_json(file_name: FileNameRequest = Body(..., embed=True)): if not apikey: return ReJson(1002, body="deepseek_setting.API_KEY is required") llm_api = DeepSeekApi(api_key=apikey) - json_data = llm_api.send_msg(module=0,message=json_data) + json_data = llm_api.send_msg(module=0,message=json.dumps(json_data)) # 保存到ai_json ai_json_path = os.path.join(gc.work_path, "export", my_wxid, "ai_json") @@ -708,7 +741,7 @@ def db_to_ai_json(file_name: FileNameRequest = Body(..., embed=True)): os.makedirs(ai_json_path) assert isinstance(ai_json_path, str) - file_name = wxid + '_aiyes_' + start_time.replace(' ', '_').replace(':', '-') + '_' + end_time.replace(' ', + file_name = wxid + '_aiyes_' + start_time.replace(' ', '_').replace(':', '-') + '_to_' + end_time.replace(' ', '_').replace( ':', '-') file_name = file_name + '.json' @@ -720,28 +753,35 @@ def db_to_ai_json(file_name: FileNameRequest = Body(..., embed=True)): - +class FileNameGetUiRequest(BaseModel): + wxid: str + start_time: str + end_time: str # 获取可视化界面json文件 @rs_api.api_route('/get_ui_json', methods=["GET", 'POST']) -def get_ui_json(file_name: FileNameRequest = Body(..., embed=True)): +def get_ui_json(file_name: FileNameGetUiRequest = Body(..., embed=True)): """ 获取可视化界面json文件 """ - start_time = file_name.start_time /1000.0 - end_time = file_name.end_time /1000.0 - wxid = file_name.wxid - start_time = datetime.datetime.fromtimestamp(float(start_time)).strftime("%Y-%m-%d %H:%M:%S") #转换成日期格式 - end_time = datetime.datetime.fromtimestamp(float(end_time)).strftime("%Y-%m-%d %H:%M:%S") + # print(file_name.wxid) - file_name = wxid + '_aiyes_' + start_time.replace(' ', '_').replace(':', '-') + '_' + end_time.replace(' ', '_').replace(':', '-') + start_time = file_name.start_time + end_time = file_name.end_time + wxid = file_name.wxid if "@" in file_name.wxid else "wxid_" + file_name.wxid + + + # start_time = datetime.datetime.fromtimestamp(float(start_time)).strftime("%Y-%m-%d %H:%M:%S") #转换成日期格式 + # end_time = datetime.datetime.fromtimestamp(float(end_time)).strftime("%Y-%m-%d %H:%M:%S") + + file_name = wxid + '_aiyes_' + start_time.replace(' ', '_').replace(':', '-') + '_to_' + end_time.replace(' ', '_').replace(':', '-') file_name = file_name + '.json' my_wxid = gc.get_conf(gc.at, "last") if not my_wxid: return ReJson(1001, body="my_wxid is required") - result = get_file_path(os.path.join(gc.work_path, "export", my_wxid, "json"), file_name) + result = get_file_path(os.path.join(gc.work_path, "export", my_wxid, "ai_json"), file_name) if result is None: return ReJson(1002, body=f"file not found: {file_name}") diff --git a/pywxdump/ui/src/api/base.ts b/pywxdump/ui/src/api/base.ts index f4d415d..a599c81 100644 --- a/pywxdump/ui/src/api/base.ts +++ b/pywxdump/ui/src/api/base.ts @@ -1,62 +1,100 @@ import http from "@/utils/axios.js"; - // const is_local_data = false; -const is_local_data = localStorage.getItem('isUseLocalData') === 't'; +const is_local_data = localStorage.getItem("isUseLocalData") === "t"; export const apiVersion = () => { - return http.get('/api/rs/version').then((res: any) => { - return res; - }).catch((err: any) => { - console.log(err); - return ''; + return http + .get("/api/rs/version") + .then((res: any) => { + return res; }) -} + .catch((err: any) => { + console.log(err); + return ""; + }); +}; export const api_db_init = async () => { - const t = await http.get('/api/rs/is_init') - console.log("is_db_init", !!t); - return !!t; -} + const t = await http.get("/api/rs/is_init"); + console.log("is_db_init", !!t); + return !!t; +}; export const api_img = (url: string) => { - if (is_local_data) { - return `./imgsrc?src=${url}`; - } - return `/api/rs/imgsrc?src=${url}`; -} + if (is_local_data) { + return `./imgsrc?src=${url}`; + } + return `/api/rs/imgsrc?src=${url}`; +}; export const api_audio = (url: string) => { - if (is_local_data) { - return `./audio?src=${url}`; - } - return `/api/rs/audio?src=${url}`; -} + if (is_local_data) { + return `./audio?src=${url}`; + } + return `/api/rs/audio?src=${url}`; +}; export const api_video = (url: string) => { - if (is_local_data) { - return `./video?src=${url}`; - } - return `/api/rs/video?src=${url}`; -} + if (is_local_data) { + return `./video?src=${url}`; + } + return `/api/rs/video?src=${url}`; +}; export const api_file = (url: string) => { - if (is_local_data) { - return `./file?src=${url}`; - } - return `/api/rs/file?src=${url}`; -} + if (is_local_data) { + return `./file?src=${url}`; + } + return `/api/rs/file?src=${url}`; +}; // file_info export const api_file_info = (url: string) => { - if (is_local_data) { - return `./file_info?src=${url}`; - } - return http.post('/api/rs/file_info', { - 'file_path': url, - }).then((res: any) => { - return res; - }).catch((err: any) => { - console.log(err); - return ''; + if (is_local_data) { + return `./file_info?src=${url}`; + } + return http + .post("/api/rs/file_info", { + file_path: url, }) -} \ No newline at end of file + .then((res: any) => { + return res; + }) + .catch((err: any) => { + console.log(err); + return ""; + }); +}; + +// DeepSeek设置部分 +export const apiDeepSeekSet = (key: string) => { + return http + .post("/api/rs/deepseek_setting", { + deepseek: { + api_key: key, + }, + }) + .then((res: any) => { + return res; + }) + .catch((err: any) => { + console.log(err); + return ""; + }); +}; + + +/** + * 获取DeepSeek设置 + */ +export const apiDeepSeekGet = () => { + return http + .get("/api/rs/deepseek_setting") + .then((res: any) => { + return res; + }) + .catch((err: any) => { + console.log(err); + return ""; + }); +}; diff --git a/pywxdump/ui/src/api/chat.ts b/pywxdump/ui/src/api/chat.ts index 2e80447..76c26d1 100644 --- a/pywxdump/ui/src/api/chat.ts +++ b/pywxdump/ui/src/api/chat.ts @@ -112,3 +112,50 @@ export const apiMsgs = (wxid: string, start: number, limit: number) => { return ''; }) } + +/** + * 获取ai可视化文件列表 + */ +export const apiAiList = () =>{ + return http.get('/api/rs/ai_ui_json_list' ).then((res: any) => { + return res; + }).catch((err: any) => { + console.log(err); + return ''; + }) +} + + +/** + * 获取ai可视化文件内容 + */ + +export interface AiUiJson { + + wxid: string, + start_time:string, + end_time:string, + +} + + + +export const apiAiUiJson = (file_name: AiUiJson) =>{ + return http.post('/api/rs/get_ui_json', {file_name}).then((res: any) => { + return res; + }).catch((err: any) => { + console.log(err); + return ''; + }) +} + + +export const apiAiUiCreateJson = (file_name: AiUiJson) =>{ + return http.post('/api/rs/db_to_ai_json', {file_name}).then((res: any) => { + return res; + }).catch((err: any) => { + console.log(err); + return ''; + }) +} + diff --git a/pywxdump/ui/src/components/utils/DeepSeekSet.vue b/pywxdump/ui/src/components/utils/DeepSeekSet.vue new file mode 100644 index 0000000..99fc798 --- /dev/null +++ b/pywxdump/ui/src/components/utils/DeepSeekSet.vue @@ -0,0 +1,72 @@ + + + + + \ No newline at end of file diff --git a/pywxdump/ui/src/views/Chat2UiSelectVue.vue b/pywxdump/ui/src/views/Chat2UiSelectVue.vue index e69de29..67d50a5 100644 --- a/pywxdump/ui/src/views/Chat2UiSelectVue.vue +++ b/pywxdump/ui/src/views/Chat2UiSelectVue.vue @@ -0,0 +1,194 @@ + + + + + diff --git a/pywxdump/ui/src/views/Chat2UiView.vue b/pywxdump/ui/src/views/Chat2UiView.vue index feab24a..2cf2b0c 100644 --- a/pywxdump/ui/src/views/Chat2UiView.vue +++ b/pywxdump/ui/src/views/Chat2UiView.vue @@ -1,1091 +1,1251 @@ - - - - \ No newline at end of file + +const getData = async (file_name:AiUiJson) => { + try { + console.log(file_name); + const res = await apiAiUiJson(file_name); + console.log(res); + + } + catch (error) { + console.error("获取数据失败:", error); + } +} + +const getRandomPosition = (max: number, offset: number) => { + return Math.random() * (max - offset); +}; + +const hoverWord = (index: string | number) => { + const words = document.querySelectorAll(".cloud-word"); + if (words[index]) { + words[ + index + ].style.transform = `scale(1.1) rotate(${reportData.value.sections.wordCloud.words[index].rotation}deg)`; + words[index].style.zIndex = "10"; + } +}; + +const unhoverWord = (index) => { + const words = document.querySelectorAll(".cloud-word"); + if (words[index]) { + words[ + index + ].style.transform = `scale(1) rotate(${reportData.value.sections.wordCloud.words[index].rotation}deg)`; + words[index].style.zIndex = "1"; + } +}; + +onMounted(async () => { + // 初始化词云位置 + const words = document.querySelectorAll(".cloud-word"); + words.forEach((word, index) => { + const wordData = reportData.value.sections.wordCloud.words[index]; + const left = getRandomPosition(600, word.textContent.length * 10); + const top = getRandomPosition(400, 20); + word.style.left = `${left}px`; + word.style.top = `${top}px`; + }); + + await getData(file_name.value) +}); + + + diff --git a/pywxdump/ui/src/views/other/SettingView.vue b/pywxdump/ui/src/views/other/SettingView.vue index 70f66e9..f7e7678 100644 --- a/pywxdump/ui/src/views/other/SettingView.vue +++ b/pywxdump/ui/src/views/other/SettingView.vue @@ -2,7 +2,7 @@ import DbInitView from "@/views/DbInitView.vue"; import {ref} from "vue"; import DbInitComponent from "@/components/utils/DbInitComponent.vue"; - +import deepseekSet from "@/components/utils/DeepSeekSet.vue"; const setting_selected = ref("") const MeneSelect = (val: any) => { @@ -34,14 +34,15 @@ const MeneSelect = (val: any) => { 初始化设置 - - + + DeepSeek设置 +