Browse Source

reactor:部分报表优化详情查看名字;

zhangchong 8 months ago
parent
commit
bc6783910d

+ 2 - 2
.env.development

@@ -3,11 +3,11 @@ VITE_MODE_NAME=development
 # 防止部署多套系统到同一域名不同目录时,变量共用的问题 设置不同的前缀
 VITE_STORAGE_NAME=dev
 # 基础请求地址
-VITE_API_URL=http://110.188.24.28:50300
+VITE_API_URL=http://110.188.24.28:50100
 # 数据共享平台请求地址
 VITE_DATASHARE_API_YRL=http://ds.12345lm.cn
 # socket API
-VITE_API_SOCKET_URL=http://110.188.24.28:50300/hubs/hotline
+VITE_API_SOCKET_URL=http://110.188.24.28:50100/hubs/hotline
 # 上传 API
 VITE_API_UPLOAD_URL=http://110.188.24.28:50120
 # 文件上传地址前缀

+ 1 - 0
package.json

@@ -44,6 +44,7 @@
 		"splitpanes": "^3.1.5",
 		"vue": "^3.2.45",
 		"vue-echarts": "^6.6.9",
+		"vue-json-viewer": "3",
 		"vue-router": "^4.1.6",
 		"vue3-puzzle-vcode": "^1.1.7",
 		"vue3-seamless-scroll": "^2.0.1",

+ 0 - 1
src/App.vue

@@ -24,7 +24,6 @@ import { getImageUrl } from '@/utils/tools';
 import { useKeepALiveNames } from '@/stores/keepAliveNames';
 import { useFavicon } from '@vueuse/core';
 import { getCurrentCityConfig } from '@/utils/appConfig';
-import signalR from '@/utils/signalR';
 // 引入组件
 const LockScreen = defineAsyncComponent(() => import('@/layout/lockScreen/index.vue'));
 const SetTings = defineAsyncComponent(() => import('@/layout/navBars/breadcrumb/setings.vue'));

+ 19 - 32
src/layout/navBars/tagsView/tagsView.vue

@@ -19,7 +19,7 @@
 				>
 					<i class="iconfont icon-webicon318 layout-navbars-tagsview-ul-li-iconfont" v-if="isActive(v)"></i>
 					<SvgIcon :name="v.meta.icon" v-if="!isActive(v) && getThemeConfig.isTagsviewIcon" class="pr5" />
-					<span>{{ setTagsViewNameI18n(v) }}</span>
+					<span :title="setTagsViewNameI18n(v)" class="layout-navbars-tagsview-ul-li-text">{{ setTagsViewNameI18n(v) }}</span>
 					<template v-if="isActive(v)">
 						<SvgIcon
 							name="ele-RefreshRight"
@@ -252,28 +252,15 @@ const refreshCurrentTagsView = async (fullPath: string) => {
 		}
 	});
 	if (!item) return false;
-	// 判断如果state.tagsViewList数组内有相同name
-	const names = state.tagsViewList.map((obj:any) => obj.name);
-	const uniqueNames = new Set(names);
-	const hasDuplicateName = names.length !== uniqueNames.size;
-	if(hasDuplicateName){
-		// 如果有相同的 关闭其他的tagView 再刷新
-		closeOtherTagsView(item.path);
-		await storesKeepALiveNames.delCachedView(item);
-		mittBus.emit('onTagsViewRefreshRouterView', fullPath);
-		if (item.meta?.isKeepAlive) await storesKeepALiveNames.addCachedView(item);
-	}else{
-		await storesKeepALiveNames.delCachedView(item);
-		mittBus.emit('onTagsViewRefreshRouterView', fullPath);
-		if (item.meta?.isKeepAlive) await storesKeepALiveNames.addCachedView(item);
-	}
+	await storesKeepALiveNames.delCachedView(item);
+	mittBus.emit('onTagsViewRefreshRouterView', fullPath);
+	if (item.meta?.isKeepAlive) storesKeepALiveNames.addCachedView(item);
 };
 // 3、关闭当前 tagsView:如果是设置了固定的(isAffix),不可以关闭
 const closeCurrentTagsView = (path: string) => {
 	state.tagsViewList.map((v: RouteItem, k: number, arr: any) => {
 		if (!v.meta?.isAffix) {
 			if (getThemeConfig.value.isShareTagsView ? v.path === path : v.url === path) {
-
 				state.tagsViewList.splice(k, 1);
 				const fi = state.tagsViewList.filter((i: any) => i.name === v.name);
 				if (!fi.length) storesKeepALiveNames.delCachedView(v); // 动态路由会有多个 如果有多个不清除缓存
@@ -622,7 +609,7 @@ watch(
 <style scoped lang="scss">
 .layout-navbars-tagsview {
 	background-color: var(--el-color-white);
-	 border-bottom: 1px solid var(--hotline-border-color-light);
+	border-bottom: 1px solid var(--hotline-border-color-light);
 	position: relative;
 	z-index: 4;
 	:deep(.el-scrollbar__wrap) {
@@ -630,15 +617,15 @@ watch(
 	}
 
 	&-ul {
-    list-style: none;
-    margin: 0;
-    height: 34px;
-    display: flex;
-    align-items: center;
-    color: var(--el-text-color-regular);
-    font-size: 12px;
-    white-space: nowrap;
-    padding: 0 15px;
+		list-style: none;
+		margin: 0;
+		height: 34px;
+		display: flex;
+		align-items: center;
+		color: var(--el-text-color-regular);
+		font-size: 12px;
+		white-space: nowrap;
+		padding: 0 15px;
 
 		&-li {
 			height: 26px;
@@ -664,7 +651,11 @@ watch(
 				left: -5px;
 				font-size: 12px;
 			}
-
+			&-text{
+				max-width: 150px;
+				overflow: hidden;
+				text-overflow: ellipsis;
+			}
 			&-icon {
 				border-radius: 100%;
 				position: relative;
@@ -763,10 +754,6 @@ watch(
 		}
 	}
 }
-
-.isShowControls {
-}
-
 .layout-navbars-tagsview-shadow {
 	box-shadow: rgb(0 21 41 / 4%) 0 1px 4px;
 }

+ 57 - 60
src/layout/routerView/parent.vue

@@ -1,20 +1,20 @@
 <template>
-  <div class="layout-parent">
-    <router-view v-slot="{ Component }">
-      <transition :name="setTransitionName" mode="out-in">
-        <keep-alive :include="getKeepAliveNames">
-          <component :is="Component" :key="state.refreshRouterViewKey" class="w100" v-show="!isIframePage" />
-        </keep-alive>
-      </transition>
-    </router-view>
-    <transition :name="setTransitionName" mode="out-in">
-      <Iframes class="w100" v-show="isIframePage" :refreshKey="state.iframeRefreshKey" :name="setTransitionName" :list="state.iframeList" />
-    </transition>
-  </div>
+	<div class="layout-parent">
+		<router-view v-slot="{ Component }">
+			<transition :name="setTransitionName" mode="out-in">
+				<keep-alive :include="getKeepAliveNames">
+					<component :is="Component" :key="state.refreshRouterViewKey" class="w100" v-show="!isIframePage" />
+				</keep-alive>
+			</transition>
+		</router-view>
+		<transition :name="setTransitionName" mode="out-in">
+			<Iframes class="w100" v-show="isIframePage" :refreshKey="state.iframeRefreshKey" :name="setTransitionName" :list="state.iframeList" />
+		</transition>
+	</div>
 </template>
 
 <script setup lang="ts" name="layoutParentView">
-import { defineAsyncComponent, computed, reactive, onBeforeMount, nextTick, watch, onMounted,onBeforeUnmount } from 'vue';
+import { defineAsyncComponent, computed, reactive, onBeforeMount, onUnmounted, nextTick, watch, onMounted } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { storeToRefs } from 'pinia';
 import { useKeepALiveNames } from '@/stores/keepAliveNames';
@@ -33,79 +33,76 @@ const storesThemeConfig = useThemeConfig();
 const { keepAliveNames, cachedViews } = storeToRefs(storesKeepAliveNames);
 const { themeConfig } = storeToRefs(storesThemeConfig);
 const state = reactive<ParentViewState>({
-  refreshRouterViewKey: '', // 非 iframe tagsview 右键菜单刷新时
-  iframeRefreshKey: '', // iframe tagsview 右键菜单刷新时
-  keepAliveNameList: [],
-  iframeList: [],
+	refreshRouterViewKey: '', // 非 iframe tagsview 右键菜单刷新时
+	iframeRefreshKey: '', // iframe tagsview 右键菜单刷新时
+	keepAliveNameList: [],
+	iframeList: [],
 });
 
 // 设置主界面切换动画
 const setTransitionName = computed(() => {
-  return themeConfig.value.animation;
+	return themeConfig.value.animation;
 });
 // 获取组件缓存列表(name值)
 const getKeepAliveNames = computed(() => {
-  return themeConfig.value.isTagsview ? cachedViews.value : state.keepAliveNameList;
+	return themeConfig.value.isTagsview ? cachedViews.value : state.keepAliveNameList;
 });
 // 设置 iframe 显示/隐藏
 const isIframePage = computed(() => {
-  return route.meta.isIframe;
+	return route.meta.isIframe;
 });
 // 获取 iframe 组件列表(未进行渲染)
 const getIframeListRoutes = async () => {
-  router.getRoutes().forEach((v) => {
-    if (v.meta.isIframe) {
-      v.meta.isIframeOpen = false;
-      v.meta.loading = true;
-      state.iframeList.push({ ...v });
-    }
-  });
+	router.getRoutes().forEach((v) => {
+		if (v.meta.isIframe) {
+			v.meta.isIframeOpen = false;
+			v.meta.loading = true;
+			state.iframeList.push({ ...v });
+		}
+	});
 };
 // 页面加载前,处理缓存,页面刷新时路由缓存处理
 onBeforeMount(() => {
-  state.keepAliveNameList = keepAliveNames.value;
-  mittBus.on('onTagsViewRefreshRouterView', (fullPath: string) => {
+	state.keepAliveNameList = keepAliveNames.value;
+	mittBus.on('onTagsViewRefreshRouterView', (fullPath: string) => {
+		state.keepAliveNameList = keepAliveNames.value.filter((name: string) => route.name !== name);
 		state.refreshRouterViewKey = '';
 		state.iframeRefreshKey = '';
-    state.keepAliveNameList = keepAliveNames.value.filter((name: string) => route.name !== name);
-    nextTick(() => {
-      state.refreshRouterViewKey = fullPath;
-      state.iframeRefreshKey = fullPath;
-			setTimeout(()=>{
-				state.keepAliveNameList = keepAliveNames.value;
-			},100)
-    });
-  });
+		nextTick(() => {
+			state.refreshRouterViewKey = fullPath;
+			state.iframeRefreshKey = fullPath;
+			state.keepAliveNameList = keepAliveNames.value;
+		});
+	});
 });
 // 页面加载时
 onMounted(() => {
-  getIframeListRoutes();
-  // https://gitee.com/lyt-top/vue-next-admin/issues/I58U75
-  // https://gitee.com/lyt-top/vue-next-admin/issues/I59RXK
-  // https://gitee.com/lyt-top/vue-next-admin/pulls/40
-  nextTick(() => {
-    setTimeout(() => {
-      if (themeConfig.value.isCacheTagsView) {
-        let tagsViewArr: RouteItem[] = Session.get('tagsViewList') || [];
-        cachedViews.value = tagsViewArr.filter((item) => item.meta?.isKeepAlive).map((item) => item.name as string);
-      }
-    }, 0);
-  });
+	getIframeListRoutes();
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I58U75
+	// https://gitee.com/lyt-top/vue-next-admin/issues/I59RXK
+	// https://gitee.com/lyt-top/vue-next-admin/pulls/40
+	nextTick(() => {
+		setTimeout(() => {
+			if (themeConfig.value.isCacheTagsView) {
+				let tagsViewArr: RouteItem[] = Session.get('tagsViewList') || [];
+				cachedViews.value = tagsViewArr.filter((item) => item.meta?.isKeepAlive).map((item) => item.name as string);
+			}
+		}, 0);
+	});
 });
 // 页面卸载时
-onBeforeUnmount(() => {
-  mittBus.off('onTagsViewRefreshRouterView', () => {});
+onUnmounted(() => {
+	mittBus.off('onTagsViewRefreshRouterView', () => {});
 });
 // 监听路由变化,防止 tagsView 多标签时,切换动画消失
 // https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/38/files
 watch(
-  () => route.fullPath,
-  () => {
-    state.refreshRouterViewKey = decodeURI(route.fullPath);
-  },
-  {
-    immediate: true,
-    deep:true
-  }
+	() => route.fullPath,
+	() => {
+		state.refreshRouterViewKey = decodeURI(route.fullPath);
+	},
+	{
+		immediate: true,
+	}
 );
 </script>

+ 3 - 1
src/main.ts

@@ -14,6 +14,8 @@ import { MotionPlugin } from '@vueuse/motion';
 
 // 注册echarts
 import { registerEcharts } from '@/utils/echarts';
+import JsonViewer from 'vue-json-viewer';
+
 
 const app = createApp(App);
 
@@ -23,4 +25,4 @@ other.elSvg(app);
 // 全局组件挂载
 app.component('ProTable', ProTable);
 registerEcharts(app);
-app.use(pinia).use(router).use(ElementPlus).use(MotionPlugin).mount('#app');
+app.use(pinia).use(router).use(ElementPlus).use(MotionPlugin).use(JsonViewer).mount('#app');

+ 4 - 4
src/router/backEnd.ts

@@ -21,7 +21,7 @@ const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
 
 /**
  * 获取目录下的 .vue、.tsx 全部文件
- * @method import.meta.glob
+ * @method import.meta
  * @link 参考:https://cn.vitejs.dev/guide/features.html#json
  */
 const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...layoutModules }, { ...viewsModules });
@@ -96,7 +96,7 @@ export async function initBackEndControlRoutes() {
 	// 无 token 停止执行下一步
 	if (!Cookie.get('token')) return false;
 
-	let resRouter = null;
+	let resRouter = <EmptyArrayType>[];
 	if (Local.get('requestOldRoutes')) {
 		//获取到缓存
 		resRouter = Local.get('requestOldRoutes');
@@ -117,7 +117,7 @@ export async function initBackEndControlRoutes() {
 		// 路由内容格式化
 		resRouter = formatRouter(routerList);
 		// 存储接口原始路由(未处理component),根据需求选择使用
-		useRequestOldRoutes().setRequestOldRoutes(other.deepClone(resRouter));
+		await useRequestOldRoutes().setRequestOldRoutes(other.deepClone(resRouter));
 		// 存入缓存
 		Local.set('requestOldRoutes', resRouter);
 		// 获取系统配置
@@ -137,7 +137,7 @@ export async function initBackEndControlRoutes() {
 	// 添加动态路由
 	await setAddRoute();
 	// 设置路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
-	await setFilterMenuAndCacheTagsViewRoutes();
+	setFilterMenuAndCacheTagsViewRoutes();
 }
 
 /**

+ 3 - 11
src/router/route.ts

@@ -73,7 +73,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
 		},
 	},
 	{
-		path: '/knowledge/index/edit/:id?/:tagsViewName?',
+		path: '/knowledge/index/edit/:tagsViewName/:id?',
 		name: 'knowledgeEdit',
 		component: () => import('@/views/knowledge/index/edit.vue'),
 		meta: {
@@ -83,7 +83,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
 		},
 	},
 	{
-		path: '/knowledge/index/preview/:tagsViewName/:id?/:isAddPv?',
+		path: '/knowledge/index/preview/:tagsViewName/:id/:isAddPv?',
 		name: 'knowledgePreview',
 		component: () => import('@/views/knowledge/index/preview.vue'),
 		meta: {
@@ -102,7 +102,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
 		},
 	},
 	{
-		path: '/system/config/workflow/edit/:id?/:tagsViewName?',
+		path: '/system/config/workflow/edit/:tagsViewName/:id?',
 		name: 'workflowAddEdit',
 		component: () => import('@/views/system/config/workflow/component/workflowEdit.vue'),
 		meta: {
@@ -519,14 +519,6 @@ export const staticRoutes: Array<RouteRecordRaw> = [
 			title: '登录',
 		},
 	},
-	{
-		path: '/forgetPwd',
-		name: 'forgetPwd',
-		component: () => import('@/views/forgetPwd/index.vue'),
-		meta: {
-			title: '修改密码',
-		},
-	},
 	{
 		path: '/resetPwd',
 		name: 'resetPwd',

+ 3 - 3
src/utils/callCenter.ts

@@ -1,9 +1,9 @@
-import { reactive, ref } from 'vue';
+import { reactive } from 'vue';
 import { getCurrentCityConfig } from '@/utils/appConfig';
 import { getNowDateTime } from '@/utils/constants';
 import { ElMessage } from 'element-plus';
 import { dutyOff } from '@/api/public/wex';
-import { createGlobalState, useStorage } from '@vueuse/core';
+import { createGlobalState } from '@vueuse/core';
 
 /**
  * @description 呼叫中心配置 方法
@@ -32,7 +32,7 @@ export const useGlobalState = createGlobalState(
 				telNo: null, // 分机号
 				jobNum: null, //工号
 				telGroup: null, // 技能组ID
-			}
+			},
 		});
 	},
 )

+ 1 - 1
src/utils/request.ts

@@ -129,7 +129,7 @@ function httpErrorStatusHandle(error: any) {
 					// 设置定时器,确保下次异常时弹出框正常弹出
 					setTimeout(() => {
 						tokenAbnormal = false;
-					}, 3000);
+					}, 2000);
 				}
 				break;
 			case 403:

+ 16 - 0
src/utils/tools.ts

@@ -416,3 +416,19 @@ export function htmlExportPdf(fileName: string, className:string) {
 		toCanvas();
 	});
 }
+/**
+ * @description  判断是否是json
+ * @returns {*}
+ * @param str
+ * */
+export function isJSON(str:string) {
+	try {
+		const jsonStr = JSON.parse(str);
+		const jsonObj = JSON.parse(jsonStr);
+		return !!(typeof jsonObj == 'object' && jsonObj);
+
+	} catch (e) {
+		console.log('error:' + str + '!!!' + e);
+		return false;
+	}
+}

+ 0 - 266
src/views/forgetPwd/component/Forget-password.vue

@@ -1,266 +0,0 @@
-<template>
-	<el-form class="forgetPwd-content-form" ref="forgetRef" :model="state.ruleForm" :rules="rules" label-position="top" label-width="100px">
-		<el-form-item prop="currentPassword" class="login-animation1" label="旧密码">
-			<el-input
-				class="inputDeep"
-				clearable
-				type="password"
-				show-password
-				placeholder="请输旧就密码"
-				v-model="state.ruleForm.currentPassword"
-				autocomplete="off"
-			>
-				<template #prefix>
-					<el-icon class="el-input__icon">
-						<ele-Unlock />
-					</el-icon>
-				</template>
-			</el-input>
-		</el-form-item>
-		<el-form-item prop="newPassword" label="新密码" class="login-animation2">
-			<el-input
-				class="inputDeep"
-				clearable
-				type="password"
-				show-password
-				placeholder="请填写新密码"
-				v-model="state.ruleForm.newPassword"
-				autocomplete="off"
-			>
-				<template #prefix>
-					<el-icon class="el-input__icon">
-						<ele-Unlock />
-					</el-icon>
-				</template>
-			</el-input>
-			<div class="intensity">
-				<span class="psdText">密码强度:{{ modes === 1 ? '弱' : modes === 2 ? '中' : modes === 3 ? '强' : '' }}</span>
-				<span class="line lowLine" :class="modes === 1 ? 'low' : ''"></span>
-				<span class="line middleLine" :class="modes === 2 ? 'middle' : ''"></span>
-				<span class="line highLine" :class="modes === 3 ? 'high' : ''"></span>
-			</div>
-		</el-form-item>
-		<el-form-item prop="confirmPassword" label="确认密码" class="login-animation3">
-			<el-input
-				class="inputDeep"
-				clearable
-				type="password"
-				show-password
-				placeholder="请再次填写密码"
-				v-model="state.ruleForm.confirmPassword"
-				autocomplete="off"
-			>
-				<template #prefix>
-					<el-icon class="el-input__icon">
-						<ele-Unlock />
-					</el-icon>
-				</template>
-			</el-input>
-		</el-form-item>
-		<el-button
-			type="primary"
-			class="forgetPwd-content-submit login-animation3"
-			round
-			@click="onChangeConfirm(forgetPwdRef)"
-			v-waves="'light'"
-			:loading="state.loading"
-			>确认修改</el-button
-		>
-		<div class="font12 mt10 forgetPwd-msg login-animation4">提示:密码不得少于8位数,且必须包含数字、字母大小写和特殊字符</div>
-		<div class="login-msg login-animation4">
-			<span></span>
-			<el-button link type="primary" class="font16" @click="goLogin">返回登录</el-button>
-		</div>
-	</el-form>
-</template>
-
-<script setup lang="ts" name="forgetPwdComponent">
-import { reactive, ref } from 'vue';
-import { useRouter } from 'vue-router';
-import { ElNotification } from 'element-plus';
-import type { FormInstance } from 'element-plus';
-import {Cookie, Session} from '@/utils/storage';
-import { changePwd } from '@/api/login/user';
-// 修改密码参数类型
-interface ChangePwdState {
-	currentPassword: string;
-	newPassword: string;
-	confirmPassword: string;
-}
-// 定义变量
-const router = useRouter();
-const state = reactive({
-	ruleForm: <ChangePwdState>{
-		currentPassword: '',  // 旧密码
-		newPassword: '',  // 新密码
-		confirmPassword: '',  // 确认密码
-	},
-	loading: false, // 确认修改按钮loading
-});
-const forgetPwdRef = ref<FormInstance>(); // 表单ref
-// 确认重置
-const onChangeConfirm = async (formEl: FormInstance | undefined) => {
-	if (!formEl) return;
-	await formEl.validate((valid: boolean) => {
-		if (!valid) return;
-		state.loading = true;
-		changePwd(state.ruleForm)
-			.then(async () => {
-				// 清理清理缓存
-				Session.clear();
-        Cookie.clear();
-				await router.push('/');
-				ElNotification({
-					title: '成功',
-					type: 'success',
-					message: '密码重置成功,请重新登录',
-				});
-			})
-			.catch(() => {
-				state.loading = false;
-			});
-	});
-};
-//  检查密码强度
-let modes = ref<number>(0);
-const checkPassword = (rule: any, value: string, callback: any) => {
-	if (!value) {
-		modes.value = 0;
-		return callback('新密码不能为空');
-	}
-	if (value.length < 8) {
-		modes.value = 0;
-		return callback('新密码不少于8位');
-	}
-	/*
-    最短8位, {6,}
-    可以包含小写大母 [a-z] 和大写字母 [A-Z]
-    可以包含数字 [0-9]
-    可以包含下划线 [ _ ] 和减号 [ - ]
-  */
-	if (/^[\w_-]{6,}$/.test(state.ruleForm.newPassword)) {
-		modes.value = 1;
-	}
-	/*
-    最短8位, {8,}
-    必须包含1个数字
-    必须包含1个小写字母
-    必须包含1个大写字母
-    必须包含1个特殊字符
-  */
-	if (/^\S*(?=\S{8,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*.?])\S*$/.test(state.ruleForm.newPassword)) modes.value = 2; //中等
-	/*
-    最短8位, {8,}
-    必须包含1个数字
-    必须包含1个小写字母
-    必须包含1个大写字母
-    必须包含2个特殊字符
-  */
-	if (/^\S*(?=\S{8,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*.?]{2,})\S*$/.test(state.ruleForm.newPassword)) modes.value = 3; //强密码
-	if (modes.value == 0 || modes.value == 1) {
-		return callback('提示:密码不得少于8位数,且必须包含字母大小写和特殊字符'); //弱密码
-	}
-	if (value === state.ruleForm.currentPassword) {
-		return callback('新密码不能与旧密码一致,请重新填写');
-	}
-	return callback();
-};
-// 检查填写密码是否一致
-const checkConfirmPassword = (rule: any, value: any, callback: any) => {
-	if (!value) {
-		return callback('请再次填写密码');
-	}
-	if (value != state.ruleForm.newPassword) {
-		return callback('两次密码填写不一致,请重新填写');
-	}
-	return callback();
-};
-const rules = reactive({
-	currentPassword: [{ required: true, message: '请填写旧密码', trigger: ['change', 'blur'] }],
-	newPassword: [{ required: true, validator: checkPassword, trigger: ['change', 'blur'] }],
-	confirmPassword: [{ required: true, validator: checkConfirmPassword, trigger: 'blur' }],
-});
-// 返回登录
-const goLogin = () => {
-	router.push({
-		path: '/login',
-	});
-};
-</script>
-
-<style scoped lang="scss">
-.forgetPwd-content-form {
-	@for $i from 1 through 4 {
-		.login-animation#{$i} {
-			opacity: 0;
-			animation-name: error-num;
-			animation-duration: 0.5s;
-			animation-fill-mode: forwards;
-			animation-delay: calc($i/10) + s;
-		}
-	}
-	.forgetPwd-content-submit {
-		width: 100%;
-		font-weight: 300;
-		background: linear-gradient(-90deg, #3c7ee0 0%, #3c50e0 100%);
-		border: none;
-		border-radius: 8px;
-		height: 40px;
-		margin-top: 20px;
-	}
-
-	.forgetPwd-msg {
-		color: var(--el-text-color-placeholder);
-	}
-	.login-msg {
-		margin-top: 10px;
-		display: flex;
-		justify-content: space-between;
-		align-items: center;
-		color: var(--el-color-primary);
-		b {
-			color: #999;
-			padding-left: 4px;
-		}
-	}
-	.intensity {
-		.psdText {
-			font-size: 14px;
-			margin-right: 10px;
-			color: #5a5a5a;
-		}
-
-		.line {
-			display: inline-block;
-			width: 48px;
-			height: 10px;
-			background: #d8d8d8;
-			margin-right: 2px;
-
-			&.lowLine {
-				border-radius: 6px 0 0 6px;
-			}
-
-			&.low {
-				background: #bfcdff;
-			}
-
-			&.middle {
-				background: #93a6fa;
-			}
-
-			&.high {
-				background: #3c50e0;
-			}
-
-			&.highLine {
-				border-radius: 0 6px 6px 0;
-			}
-		}
-
-		.level {
-			margin: 0 16px 0 8px;
-		}
-	}
-}
-</style>

+ 0 - 121
src/views/forgetPwd/index.vue

@@ -1,121 +0,0 @@
-<template>
-	<div class="login-container w100 h100">
-		<div class="login-content">
-			<div class="login-content-main">
-				<h4 class="login-content-title">修改密码</h4>
-				<div v-if="!state.isScan">
-					<el-tabs v-model="state.tabsActiveName">
-						<forget-pwd />
-					</el-tabs>
-				</div>
-			</div>
-		</div>
-	</div>
-</template>
-
-<script setup lang="ts" name="forgetPwd">
-import { defineAsyncComponent, reactive, onMounted } from 'vue';
-import { storeToRefs } from 'pinia';
-import { useThemeConfig } from '@/stores/themeConfig';
-import { NextLoading } from '@/utils/loading';
-import { getImageUrl } from '@/utils/tools';
-// 定义接口来定义对象的类型
-interface LoginState {
-	tabsActiveName: string;
-	isScan: boolean;
-}
-
-// 引入组件
-const ForgetPwd = defineAsyncComponent(() => import('@/views/forgetPwd/component/Forget-password.vue')); // 忘记密码
-
-const storesThemeConfig = useThemeConfig(); // 主题配置
-const { themeConfig } = storeToRefs(storesThemeConfig);
-const state = reactive<LoginState>({
-	tabsActiveName: 'account',  // 账号密码登录
-	isScan: false,  // 是否扫码登录
-});
-let bgImg = themeConfig.value.loginImage ?? `url(${getImageUrl('login/login_bg.png')})`;
-// 页面加载时
-onMounted(async () => {
-	NextLoading.done();
-});
-</script>
-
-<style scoped lang="scss">
-.login-container {
-	position: relative;
-	background-image: v-bind(bgImg);
-	background-repeat: no-repeat;
-	background-size: calc(100vw + 1px) calc(100vh + 1px);
-
-	.login-content {
-		width: 500px;
-		padding: 83px 80px 60px 80px;
-		position: absolute;
-		right: 10vw;
-		top: 50vh;
-		transform: translateY(-50%) translate3d(0, 0, 0);
-		background-color: var(--el-color-white);
-		border-radius: 20px;
-		overflow: hidden;
-		z-index: 1;
-		box-shadow: 0 0 20px 0 rgba(26, 64, 144, 0.46);
-
-		.login-content-main {
-			margin: 0 auto;
-
-			.login-content-title {
-				color: var(--el-color-primary);
-				font-size: 28px;
-				font-weight: 500;
-				line-height: 48px;
-				text-align: center;
-				letter-spacing: 4px;
-				white-space: nowrap;
-				z-index: 5;
-				position: relative;
-				transition: all 0.3s ease;
-			}
-		}
-
-		.login-content-main-scan {
-			position: absolute;
-			top: 0;
-			right: 0;
-			width: 50px;
-			height: 50px;
-			overflow: hidden;
-			cursor: pointer;
-			transition: all ease 0.3s;
-			color: var(--el-text-color-primary);
-
-			&-delta {
-				position: absolute;
-				width: 35px;
-				height: 70px;
-				z-index: 2;
-				top: 2px;
-				right: 21px;
-				background: var(--el-color-white);
-				transform: rotate(-45deg);
-			}
-
-			&:hover {
-				opacity: 1;
-				transition: all ease 0.3s;
-				color: var(--el-color-primary) !important;
-			}
-
-			i {
-				width: 47px;
-				height: 50px;
-				display: inline-block;
-				font-size: 48px;
-				position: absolute;
-				right: 2px;
-				top: -1px;
-			}
-		}
-	}
-}
-</style>

+ 1 - 0
src/views/knowledge/index/edit.vue

@@ -528,6 +528,7 @@ const onPreview = () => {
 		name: 'knowledgePreview',
 		params: {
 			tagsViewName: '知识预览',
+			id:'0',
 		},
 	});
 };

+ 3 - 3
src/views/knowledge/index/index.vue

@@ -187,7 +187,7 @@
 							</el-form>
 						</template>
 						<template #tableHeader="scope">
-							<el-button type="primary" @click="onOpenAddUser" v-auth="'knowledge:index:add'" :loading="state.loading">
+							<el-button type="primary" @click="onAddKnowledge" v-auth="'knowledge:index:add'" :loading="state.loading">
 								<SvgIcon name="ele-Plus" class="mr5" />创建知识
 							</el-button>
 						</template>
@@ -298,7 +298,7 @@
 							</el-form>
 						</template>
 						<template #tableHeader="scope">
-							<el-button type="primary" @click="onOpenAddUser" v-auth="'knowledge:index:add'" :loading="state.loading">
+							<el-button type="primary" @click="onAddKnowledge" v-auth="'knowledge:index:add'" :loading="state.loading">
 								<SvgIcon name="ele-Plus" class="mr5" />创建知识
 							</el-button>
 						</template>
@@ -672,7 +672,7 @@ const resetNode = () => {
 	handleQuery();
 };
 // 新增知识
-const onOpenAddUser = () => {
+const onAddKnowledge = () => {
 	router.push({
 		name: 'knowledgeEdit',
 		params: {

+ 1 - 1
src/views/knowledge/index/preview.vue

@@ -152,7 +152,7 @@ const onPreview = (row: any) => {
 // 查询详情
 const getInfo = async () => {
 	loading.value = true;
-	if (route.params.id) {
+	if (route.params.id && route.params.id !== '0') {
 		try {
 			const { isAddPv } = route.params;
 			let IsAddPv = isAddPv ? 'true' : false;

+ 2 - 1
src/views/statistics/order/hotspotAccept.vue

@@ -317,7 +317,8 @@ const linkDetail = (data) => {
       HotspotId: data.rowData.HotspotId,
       StartTime: requestParams.value.StartTime,
       EndTime: requestParams.value.EndTime,
-      type: 'hotspotAccept'
+      type: 'hotspotAccept',
+			tagsViewName:`${data.rowData.HotspotName}-${data.column.title}`
     },
   });
 };

+ 1 - 0
src/views/statistics/order/hotspotArea.vue

@@ -318,6 +318,7 @@ const linkDetail = (data) => {
 			StartTime: requestParams.value.StartTime,
 			EndTime: requestParams.value.EndTime,
 			type: 'hotspotArea',
+			tagsViewName:`${data.rowData.HotspotName}-${data.column.title}`
 		},
 	});
 };

+ 1 - 0
src/views/statistics/order/hotspotSatisfied.vue

@@ -351,6 +351,7 @@ const onDetail = (data) => {
 			EndTime: requestParams.value.EndTime,
 			TypeId: requestParams.value.TypeId,
 			TitleCode: data.column.property,
+			tagsViewName:`${data.row.hotspotName}-${data.column.label}`
 		},
 	});
 };

+ 38 - 11
src/views/system/log/components/Detail.vue

@@ -25,14 +25,19 @@
 						{{ state.ruleForm.executeUrl }}
 					</el-form-item>
 				</el-col>
-        <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-          <el-form-item label="操作IP:">
-            {{ state.ruleForm.ipUrl }}
-          </el-form-item>
-        </el-col>
+				<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+					<el-form-item label="操作IP:">
+						{{ state.ruleForm.ipUrl }}
+					</el-form-item>
+				</el-col>
 				<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
 					<el-form-item label="请求参数:">
-						<el-input type="textarea" v-model="state.ruleForm.executeParam" readonly :autosize="{ minRows: 6, maxRows: 10 }"> </el-input>
+						<json-viewer
+							:value="state.ruleForm.executeParam"
+							class="w100"
+							boxed
+							:copyable="{ copyText: '复制', copiedText: '复制成功' }"
+						></json-viewer>
 					</el-form-item>
 				</el-col>
 			</el-row>
@@ -47,16 +52,21 @@
 				</el-col>
 				<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
 					<el-form-item label="返回结果:">
-						<el-input type="textarea" v-model="state.ruleForm.executeResult" readonly :autosize="{ minRows: 6, maxRows: 10 }"> </el-input>
+						<json-viewer
+							:value="state.ruleForm.executeResult"
+							class="w100"
+							boxed
+							:copyable="{ copyText: '复制', copiedText: '复制成功' }"
+						></json-viewer>
 					</el-form-item>
 				</el-col>
 			</el-row>
-      <el-divider content-position="left" class="mb20">
+			<el-divider content-position="left" class="mb20">
 				<el-text tag="b" size="large"> 备注信息 </el-text>
 			</el-divider>
-      <el-form-item label="备注:">
-        {{ state.ruleForm.remark }}
-      </el-form-item>
+			<el-form-item label="备注:">
+				{{ state.ruleForm.remark }}
+			</el-form-item>
 		</el-form>
 	</el-dialog>
 </template>
@@ -65,6 +75,7 @@
 import { reactive } from 'vue';
 import { logDetail } from '@/api/system/log';
 import { formatDate } from '@/utils/formatTime';
+import { isJSON } from '@/utils/tools';
 
 const props = defineProps({
 	specialFlagList: {
@@ -88,6 +99,22 @@ const openDialog = async (row: any) => {
 	try {
 		const res = await logDetail(row.id);
 		state.ruleForm = res.result;
+		if (state.ruleForm.executeParam) {
+			if (isJSON(state.ruleForm.executeParam)) {
+				const jsonStr = JSON.parse(state.ruleForm.executeParam);
+				state.ruleForm.executeParam = JSON.parse(jsonStr);
+			}
+		} else {
+			state.ruleForm.executeParam = '';
+		}
+		if (state.ruleForm.executeResult) {
+			if (isJSON(state.ruleForm.executeResult)) {
+				const jsonStr = JSON.parse(state.ruleForm.executeResult);
+				state.ruleForm.executeResult = JSON.parse(jsonStr);
+			}
+		} else {
+			state.ruleForm.executeResult = '';
+		}
 		state.dialogVisible = true;
 	} catch (error) {
 		console.log(error);

+ 15 - 3
src/views/todo/seats/accept/Voice-assistant.vue

@@ -37,7 +37,7 @@
 	</div>
 </template>
 <script setup lang="ts" name="orderAcceptVoiceAssistant">
-import { nextTick, onActivated, onDeactivated, onMounted, ref, watch } from 'vue';
+import { nextTick, onActivated, onDeactivated, onMounted, reactive, ref, watch } from 'vue';
 import { getImageUrl } from '@/utils/tools';
 import { useRoute } from 'vue-router';
 import { formatDate } from '@/utils/formatTime';
@@ -47,6 +47,7 @@ import { submitLog } from '@/api/public/log';
 import { getNowDateTime } from '@/utils/constants';
 import { useTelStatus } from '@/stores/telStatus';
 import { storeToRefs } from 'pinia';
+import { Session } from '@/utils/storage';
 // 消息列表
 const messageList = ref<any>([
 	/*	{
@@ -246,10 +247,11 @@ watch(
 );
 // 订阅消息
 const subscribe = () => {
+	const currentRoute = tagsViewList.value.find((v: any) => v.query?.callId === state.ruleForm.callId);
 	// 接受消息
 	mittBus.on('wsReceive', (message: any) => {
 		const data = JSON.parse(message.data);
-		if (data.body.content.callId === route.query.callId) {
+		if (currentRoute && data.body.content.callId === currentRoute?.query?.callId) {
 			// 判断是否是当前通话
 			wsReceive(message);
 		}
@@ -295,7 +297,17 @@ const filterMessage = (type: string) => {
 			break;
 	}
 };
-onMounted(() => {
+const tagsViewList = ref<EmptyArrayType>([]);
+const state = reactive({
+	ruleForm: {
+		callId: '',
+	},
+})
+onMounted( async () => {
+	tagsViewList.value = await Session.get('tagsViewList');
+	if (route.query.callId) {
+		state.ruleForm.callId = route.query?.callId as string; // 通话id
+	}
 	// 进入页面订阅
 	subscribe();
 });

+ 1 - 0
src/views/todo/seats/index.vue

@@ -434,6 +434,7 @@ const onOrderEdit = (row: any) => {
 		name: 'orderAccept',
 		query: {
 			id: row.id,
+			tagsViewName:`编辑工单-${row.title}`
 		},
 	});
 };

+ 38 - 0
yarn.lock

@@ -2061,6 +2061,15 @@ cjs-module-lexer@^1.0.0:
   resolved "https://registry.npmmirror.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c"
   integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==
 
+clipboard@^2.0.4:
+  version "2.0.11"
+  resolved "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5"
+  integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==
+  dependencies:
+    good-listener "^1.2.2"
+    select "^1.1.2"
+    tiny-emitter "^2.0.0"
+
 cliui@^7.0.2:
   version "7.0.4"
   resolved "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
@@ -2272,6 +2281,11 @@ delayed-stream@~1.0.0:
   resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
   integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
 
+delegate@^3.1.2:
+  version "3.2.0"
+  resolved "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
+  integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
+
 destr@^2.0.3:
   version "2.0.3"
   resolved "https://registry.npmmirror.com/destr/-/destr-2.0.3.tgz#7f9e97cb3d16dbdca7be52aca1644ce402cfe449"
@@ -3001,6 +3015,13 @@ globby@^14.0.2:
     slash "^5.1.0"
     unicorn-magic "^0.1.0"
 
+good-listener@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
+  integrity sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==
+  dependencies:
+    delegate "^3.1.2"
+
 gopd@^1.0.1:
   version "1.0.1"
   resolved "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
@@ -4760,6 +4781,11 @@ scule@^1.2.0, scule@^1.3.0:
   resolved "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz#6efbd22fd0bb801bdcc585c89266a7d2daa8fbd3"
   integrity sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==
 
+select@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
+  integrity sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==
+
 semver@^6.3.0, semver@^6.3.1:
   version "6.3.1"
   resolved "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
@@ -5124,6 +5150,11 @@ throttle-debounce@5.0.0:
   resolved "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-5.0.0.tgz#a17a4039e82a2ed38a5e7268e4132d6960d41933"
   integrity sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==
 
+tiny-emitter@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
+  integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
+
 tiny-warning@^1.0.3:
   version "1.0.3"
   resolved "https://registry.npmmirror.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
@@ -5416,6 +5447,13 @@ vue-eslint-parser@^9.1.0, vue-eslint-parser@^9.4.3:
     lodash "^4.17.21"
     semver "^7.3.6"
 
+vue-json-viewer@3:
+  version "3.0.4"
+  resolved "https://registry.npmmirror.com/vue-json-viewer/-/vue-json-viewer-3.0.4.tgz#c1d65515e57d4036defbbc18fa942d7fd5fb9a8b"
+  integrity sha512-pnC080rTub6YjccthVSNQod2z9Sl5IUUq46srXtn6rxwhW8QM4rlYn+CTSLFKXWfw+N3xv77Cioxw7B4XUKIbQ==
+  dependencies:
+    clipboard "^2.0.4"
+
 vue-router@^4.1.6:
   version "4.4.0"
   resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.4.0.tgz#128e3fc0c84421035a9bd26027245e6bd68f69ab"