diff --git a/aiMap.zip b/aiMap.zip index d0336ee..48a8489 100644 Binary files a/aiMap.zip and b/aiMap.zip differ diff --git a/src/assets/ai/dropDown.png b/src/assets/ai/dropDown.png new file mode 100644 index 0000000..d337374 Binary files /dev/null and b/src/assets/ai/dropDown.png differ diff --git a/src/assets/ai/pullUp.png b/src/assets/ai/pullUp.png new file mode 100644 index 0000000..42c0e6f Binary files /dev/null and b/src/assets/ai/pullUp.png differ diff --git a/src/components/map/component/RobotDialogueBox.less b/src/components/map/component/RobotDialogueBox.less index 43b3e36..04e6476 100644 --- a/src/components/map/component/RobotDialogueBox.less +++ b/src/components/map/component/RobotDialogueBox.less @@ -92,6 +92,18 @@ } } + .moreOption{ + width: 100%; + display: flex; + align-items: center; + margin-top: 16px; + .optionItem{ + color: #1890ff!important; + cursor: pointer; + margin-right: 16px; + } + } + .style1{ color: #6590f5!important; font-weight: bold; @@ -270,5 +282,59 @@ cursor: pointer; } } + + + .moreSelect{ + width: 100%; + .filterFixed{ + width: 100%; + box-sizing: border-box; + display: flex; + justify-content: flex-end; + .filterIcon{ + width: 20px; + height: 20px; + cursor: pointer; + } + } + .filterBox{ + width: 100%; + display: flex; + .filterList{ + width: calc(100% - 28px); + box-sizing: border-box; + display: flex; + align-items: center; + flex-wrap: wrap; + .filterItem{ + cursor: pointer; + margin-right: 6px; + padding: 4px 8px; + border-radius: 14px; + border: 1px solid rgb(90, 90, 90); + font-family: PingFangSC, "PingFang SC"; + font-weight: 400; + font-size: 14px; + color: rgba(255, 255, 255, 0.85); + line-height: 20px; + text-align: left; + font-style: normal; + margin-bottom: 8px; + } + .selectFilterItem{ + color: #1890ff !important; + border-color: #1890ff; + } + + } + .filterIcon{ + width: 15px; + height: 15px; + cursor: pointer; + margin-left: 8px; + } + } + + } } } \ No newline at end of file diff --git a/src/components/map/component/RobotDialogueBox.vue b/src/components/map/component/RobotDialogueBox.vue index bf8aa97..d355dfb 100644 --- a/src/components/map/component/RobotDialogueBox.vue +++ b/src/components/map/component/RobotDialogueBox.vue @@ -9,6 +9,7 @@ import { } from "../../../options/serveice"; import request from "../../../request/requestConfig"; import { + getFieldEnumTree, getFieldGetFieEnumList, handleGetANALYSISRULEDetail, handleGetANALYSISRULEList, @@ -36,6 +37,11 @@ const thisQuestionId = ref(); const clickTabList: any = [ { label: "基础信息", value: 1 }, { label: "经营数据", value: 2 }, + { label: "在营商家", value: 3 }, + { label: "招商分析", value: 4 }, + { label: "收银监管", value: 5 }, + { label: "车辆归属", value: 6 }, + { label: "财务状况", value: 7 }, ]; // 当前选中的查询tab const selectTab = ref(0); @@ -46,6 +52,14 @@ const isPrinting = ref(false); // 预警类型的枚举对象 const descObjRef = ref(); const descListRef = ref(); +// 业态的枚举类型 +const tradeObjRef = ref(); +// 打印过程中的加载效果 +const printingLoading = ref(false); +// 打印过程中加载效果的文字 +const printingLoadingText = ref(""); +// 是否展开底部的选择标签 +const bottomSelect = ref(false); onMounted(() => { // 默认在初始显示的 @@ -216,6 +230,15 @@ const handleConfigRequest = async ( return data.Result_Data; } else if (config.responseFormat === "List") { return data.Result_Data.List; + } else if (config.responseFormat === "nestingList") { + let resultNest: any = handleSetRowKeyTable( + wrapTreeNode(data.Result_Data.List) + ); + // 如果是要计算合计值 且需要的输出对象是个对话的话 + if (config.calculateTotal) { + return handleGetListSumObj(resultNest, config.totalField); + } + return resultNest; } } else { // 用户自定义 @@ -376,6 +399,122 @@ const handleConfigRequest = async ( } }; +// 判断一下 是不是要数据的某几项合计值 写一个方法在外面 需要的时候判断 +const handleGetListSumObj = (list: any, str: any) => { + // list 从这个数据源里面拿到 合计数据 str 用,号隔开的 需要哪几个字段的合计值 + if (str) { + // 存累计值的 + let sumObj: any = {}; + for (let key in str) { + if (key === "sum") { + if (str[key].indexOf(",") === -1) { + list.forEach((item: any) => { + if (item[str[key]]) { + if (sumObj[str[key] + "sum"]) { + sumObj[str[key] + "sum"] += Number(item[str[key]]); + } else { + sumObj[str[key] + "sum"] = Number(item[str[key]]); + } + } + }); + } else { + let strList: any = str[key].split(","); + if (strList && strList.length > 0) { + strList.forEach((item: any) => { + list.forEach((subItem: any) => { + if (subItem[item]) { + if (sumObj[item + "sum"]) { + sumObj[item + "sum"] += Number(subItem[item]); + } else { + sumObj[item + "sum"] = Number(subItem[item]); + } + } + }); + }); + } + } + } else if (key === "greaterThan0") { + if (str[key].indexOf(",") === -1) { + list.forEach((item: any) => { + if (item[str[key]] > 0) { + if (sumObj[str[key] + "greaterThan0"]) { + sumObj[str[key] + "greaterThan0"] += 1; + } else { + sumObj[str[key] + "greaterThan0"] = 1; + } + } + }); + } else { + let strList: any = str[key].split(","); + if (strList && strList.length > 0) { + strList.forEach((item: any) => { + list.forEach((subItem: any) => { + if (subItem[item]) { + if (sumObj[item + "greaterThan0"]) { + sumObj[item + "greaterThan0"] += 1; + } else { + sumObj[item + "greaterThan0"] = 1; + } + } + }); + }); + } + } + } else if (key === "lessThan0") { + if (str[key].indexOf(",") === -1) { + list.forEach((item: any) => { + if (item[str[key]] < 0) { + if (sumObj[str[key] + "lessThan0"]) { + sumObj[str[key] + "lessThan0"] += 1; + } else { + sumObj[str[key] + "lessThan0"] = 1; + } + } + }); + } else { + let strList: any = str[key].split(","); + if (strList && strList.length > 0) { + strList.forEach((item: any) => { + list.forEach((subItem: any) => { + if (subItem[item]) { + if (sumObj[item + "lessThan0"]) { + sumObj[item + "lessThan0"] += 1; + } else { + sumObj[item + "lessThan0"] = 1; + } + } + }); + }); + } + } + } + } + console.log("sumObj", sumObj); + // 给输出的全部数据都格式化一下 + // for (let key in sumObj) { + // if (typeof sumObj[key] === "number") { + // sumObj[key] = formatNumber(sumObj[key]); + // } + // } + sumObj.dataLength = list.length; + + // 第二级的数量合计 + let smallSum: number = 0; + list.forEach((item: any) => { + if (item.children && item.children.length > 0) { + item.children.forEach((subItem: any) => { + smallSum += + subItem.children && subItem.children.length > 0 + ? subItem.children.length + : 0; + }); + } + }); + sumObj.smallSum = smallSum; + return sumObj; + } +}; + // 封装一下 拿到入参的方法 const handleGetReq = (configDetail: any, answer?: any) => { let req: any = {}; @@ -397,7 +536,8 @@ const handleGetReq = (configDetail: any, answer?: any) => { ProvinceCode: "340000", pushProvinceCode: "340000", }; - // formatType 0:数值 1:字段 2:取缓存 3:取年份 4: 取月份 5:取日期 6:取当年 7:取前一年 8: 取当年的月份 9:取当前的首月 10: 取选中的服务区里面的值 + // formatType 0:数值 1:字段 2:取缓存 3:取年份 4: 取月份 5:取日期 6:取当年 7:取前一年 8: 取当年的月份 9:取当前的首月 10:自定义 11: 取选中的服务区里面的值 + // 当formatType 为10的时候 now 判断是不是要现在时间 true|false afterOrBefore 往当前时间前推还是后推 after|before count 前推或后推几个月 startOrEnd start|end dateType 显示格式 1 YYYY 2 YYYY-MM 3 YYYY-MM-DD 4 YYYYMM // fieldName 就是去resData里面去字段 value formatType为0的时候直接取值 haveDefault 判断是否有默认值 如果是true即使answer中无值 也能取到默认值 一般用在日期时间上 // 当formatType为7的时候 取判断 value 1:取前一年 2:取前一个月 3:取前一天 if (realKey.formatType === 1) { @@ -438,8 +578,47 @@ const handleGetReq = (configDetail: any, answer?: any) => { } else if (realKey.formatType === 8) { req[key] = moment().format("MM"); } else if (realKey.formatType === 9) { - req[key] = moment().startOf("year").format("yyyyMM"); + req[key] = moment().startOf("year").format("YYYYMM"); } else if (realKey.formatType === 10) { + if (realKey.now) { + if (realKey.startOrEnd === "start") { + req[key] = moment().startOf("month"); + } else if (realKey.startOrEnd === "end") { + req[key] = moment().endOf("month"); + } + + if (realKey.dateType === 1) { + req[key] = moment(req[key]).format("YYYY"); + } else if (realKey.dateType === 2) { + req[key] = moment(req[key]).format("YYYY-MM"); + } else if (realKey.dateType === 3) { + req[key] = moment(req[key]).format("YYYY-MM-DD"); + } else if (realKey.dateType === 4) { + req[key] = moment(req[key]).format("YYYYMM"); + } + } else { + if (realKey.afterOrBefore === "after") { + req[key] = moment().add(realKey.count, "month"); + } else if (realKey.afterOrBefore === "before") { + req[key] = moment().subtract(realKey.count, "month"); + } + if (realKey.startOrEnd === "start") { + req[key] = moment(req[key]).startOf("month"); + } else if (realKey.startOrEnd === "end") { + req[key] = moment(req[key]).endOf("month"); + } + + if (realKey.dateType === 1) { + req[key] = moment(req[key]).format("YYYY"); + } else if (realKey.dateType === 2) { + req[key] = moment(req[key]).format("YYYY-MM"); + } else if (realKey.dateType === 3) { + req[key] = moment(req[key]).format("YYYY-MM-DD"); + } else if (realKey.dateType === 4) { + req[key] = moment(req[key]).format("YYYYMM"); + } + } + } else if (realKey.formatType === 11) { req[key] = props.currentServerPartDetail?.[realKey.fieldName] ?? ""; } } @@ -520,6 +699,7 @@ const handleAnalyzeConfig = async (configDetail: any, answer: any) => { // 根据输出结果的配置 去修改实际值 console.log("dsadsadasf", result); console.log("OUTPUT_FORMAT", OUTPUT_FORMAT); + console.log("answer", answer); let newResult: any; // 判断responseType 格式是否为 text 一般都为json @@ -714,6 +894,7 @@ const handleChangeShow = (obj: any, addObj?: any) => { isMoney: newObj.isMoney, isRate: newObj.isRate, showDic: newObj.showDic, + DicObj: newObj.DicObj, }); } return columnsList; @@ -811,8 +992,9 @@ const handleConfigItemTable = async ( console.log("configObjconfigObjconfigObjconfigObj", configObj); // 显示的时间设置 let timeStr: string = ""; + let OUTPUT_FORMAT: any; if (configObj.OUTPUT_FORMAT) { - let OUTPUT_FORMAT = JSON.parse(configObj.OUTPUT_FORMAT); + OUTPUT_FORMAT = JSON.parse(configObj.OUTPUT_FORMAT); console.log("OUTPUT_FORMAT", OUTPUT_FORMAT); if ( (OUTPUT_FORMAT.DateFormat || OUTPUT_FORMAT.DateFormat === 0) && @@ -866,12 +1048,36 @@ const handleConfigItemTable = async ( ...obj, haveTable: true, tableData: tableData, + ENABLE_CHART: configObj.ENABLE_CHART === 1, + ENABLE_PDF_EXPORT: configObj.ENABLE_PDF_EXPORT === 1, + ENABLE_VIEW_MORE: configObj.ENABLE_VIEW_MORE === 1, columns: columns, - text: `为您查找${timeStr || ""}相关数据:`, + text: + OUTPUT_FORMAT && !(tableData && tableData.length > 0) + ? OUTPUT_FORMAT.noDataText + : `为您查找${timeStr || ""}相关数据:`, }; isNewDialogLoading.value = false; } + } else { + let obj: any = JSON.parse( + JSON.stringify(dialogueList[dialogueList.length - 1]) + ); + dialogueList[dialogueList.length - 1] = { + ...obj, + ENABLE_CHART: configObj.ENABLE_CHART === 1, + ENABLE_PDF_EXPORT: configObj.ENABLE_PDF_EXPORT === 1, + ENABLE_VIEW_MORE: configObj.ENABLE_VIEW_MORE === 1, + }; + isNewDialogLoading.value = false; } + + console.log( + "configObjconfigObjconfigObjconfigObjconfigObjconfigObjconfigObj", + configObj + ); + // 是否出现 图表输出 导出为PDF “查看更多”功能 + // else { // // 不需要出现表格时 直接给最新对话框的text赋值就好 // let obj: any = JSON.parse( @@ -914,10 +1120,13 @@ const handleAnswerQuestions = async () => { let searchRes: string = ""; if (data.ServerpartId) { - // 全部服务区圆点变灰色 - emit("handleEnterySearch"); - // 查询到的服务区 变为亮黄色 - emit("handleLightServerpart", data.ServerpartId); + if (data.ShowMapPoint) { + // 全部服务区圆点变灰色 + emit("handleEnterySearch"); + // 查询到的服务区 变为亮黄色 + emit("handleLightServerpart", data.ServerpartId); + } + if (data?.ServerpartInfoList && data?.ServerpartInfoList.length > 0) { if (data?.AnalysisRuleId) { } else { @@ -1123,6 +1332,11 @@ const handleClickTab = async (value: number) => { let searchObj: any = { 1: `${props.currentServerPartDetail?.SERVERPART_NAME || ""}基础信息`, 2: `${props.currentServerPartDetail?.SERVERPART_NAME || ""}经营数据`, + 3: `${props.currentServerPartDetail?.SERVERPART_NAME || ""}在营商家`, + 4: `${props.currentServerPartDetail?.SERVERPART_NAME || ""}招商分析`, + 5: `${props.currentServerPartDetail?.SERVERPART_NAME || ""}收银监管`, + 6: `${props.currentServerPartDetail?.SERVERPART_NAME || ""}车辆归属`, + 7: `${props.currentServerPartDetail?.SERVERPART_NAME || ""}财务状况`, }; // 先添加对话内容 handleAddDialogList(searchObj[value]); @@ -1182,6 +1396,7 @@ const handleServerpartBasicInfo = async (config: any) => { console.log("req", req); // 判断入参里面是否有空值 如果有空值 就直接输出安徽驿达的基础信息 let haveNull: boolean = handleHaveNullValue(req); + // 调用接口 取数据 let data: any; if (!haveNull) { @@ -1239,44 +1454,100 @@ const handleServerpartBasicInfo = async (config: any) => { }; // 给最新的对话添加打字机效果 -const handlePrintWord = async (element: any, htmlContent: any, speed = 20) => { - // printWord; +const handlePrintWord = async ( + element: HTMLElement, + htmlContent: string, + speed = 20 +) => { return new Promise((resolve) => { let i = 0; const tempDiv = document.createElement("div"); tempDiv.innerHTML = htmlContent; printWord.value = htmlContent; - const typeCharacter = () => { + + const tagStack: HTMLElement[] = [element]; + + const typeCharacter = async () => { if (i < tempDiv.innerHTML.length && isPrinting.value) { - const char = tempDiv.innerHTML.charAt(i); - if (char === "<") { - const tagEnd = tempDiv.innerHTML.indexOf(">", i); - const tagContent = tempDiv.innerHTML.slice(i, tagEnd + 1); - element.innerHTML += tagContent; - i = tagEnd + 1; + const handleWaitRegex = /handleWait\((.*?)\)/; + const restContent = tempDiv.innerHTML.slice(i); + const match = handleWaitRegex.exec(restContent); + + if (match && match.index === 0) { + // 提取括号内的参数 + const param: string = match[1].trim().replace(/^["']|["']$/g, ""); + await handleWait(param); // 执行 handleWait 方法 + i += match[0].length; // 跳过 handleWait 字符串 } else { - element.innerHTML += char === "\n" ? "
" : char; - i++; + const char = tempDiv.innerHTML.charAt(i); + if (char === "<") { + const tagEnd = tempDiv.innerHTML.indexOf(">", i); + const tagContent = tempDiv.innerHTML.slice(i, tagEnd + 1); + const isClosingTag = tagContent.startsWith("")) { + tagStack.push(newElement); + } + } + i = tagEnd + 1; + } else { + const textNode = document.createTextNode( + char === "\n" ? "\n" : char + ); + tagStack[tagStack.length - 1].appendChild(textNode); + i++; + } } + setTimeout(typeCharacter, speed); - // 滚动到最底部 handleScrollToBottom(); } else { isPrinting.value = false; - resolve(); // 打字完成后,Promise 进入 resolved 状态 + resolve(); } }; + typeCharacter(); }); }; +// 打印过程中的加载效果 +const handleWait = async (str: string) => { + return new Promise((resolve) => { + printingLoading.value = true; + printingLoadingText.value = str; + setTimeout(() => { + printingLoading.value = false; + printingLoadingText.value = ""; + resolve(); // 确保在加载效果完成后调用 resolve + }, 2000); // 1秒延迟 + }); +}; + // 打字机延迟函数 const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + // 停止打印机事件 const handleStopPrint = () => { isPrinting.value = false; }; +// 显示数据图表 +const handleShowDataPie = () => {}; +// 跳转查看更多 +const handleGoSearchMore = () => { + window.open("http://saas.eshangtech.com/cloud/"); +}; +// 导出为pdf +const handleExportPDF = () => {}; + // 传入的数据 const props = defineProps<{ currentServerPartDetail: any; @@ -1342,6 +1613,26 @@ const handleGetFieldEnum = async () => { descListRef.value = list; console.log("descObjRef.value", descObjRef.value); } + + // 经营业态 + const BusinessTradeIdsList = await getFieldEnumTree({ + FieldExplainField: "BusinessTradeIds", + }); + + if (BusinessTradeIdsList && BusinessTradeIdsList.length > 0) { + let BusinessTradeIdsObj: any = {}; + BusinessTradeIdsList.forEach((item: any) => { + BusinessTradeIdsObj[item.value] = item.label; + if (item.children && item.children.length > 0) { + item.children.forEach((subItem: any) => { + BusinessTradeIdsObj[subItem.value] = subItem.label; + }); + } + }); + console.log("BusinessTradeIdsObj", BusinessTradeIdsObj); + + tradeObjRef.value = BusinessTradeIdsObj; + } }; // 经营业态的方法 @@ -1376,7 +1667,7 @@ const handleSetWarningData = async () => { const handleSelectWarning = async (value: number) => { handleAddDialogList( `${props.currentServerPartDetail?.SERVERPART_NAME || ""}${ - descObjRef.value[Number(value)] + descObjRef.value[Number(value)] || "预警类型" }` ); console.log("value", value); @@ -1480,6 +1771,11 @@ const handleSelectWarning = async (value: number) => { emit("handleWarningMap", warningData); }; +// 底部tab的显示 +const handleShowTabList = () => { + bottomSelect.value = !bottomSelect.value; +}; + defineExpose({ handleGetBusinessTrade: () => { handleGetBusinessTrade(); @@ -1489,6 +1785,9 @@ defineExpose({ }, descObjRef, descListRef, + handleClickTab, + selectTab, + handleSelectWarning }); @@ -1591,7 +1890,13 @@ defineExpose({