波形解析相关

This commit is contained in:
2026-04-16 08:11:38 +08:00
parent 8e2c044381
commit 5596d57409
23 changed files with 162 additions and 78 deletions

View File

@@ -3,7 +3,7 @@ import { CustomAxiosRequestConfig } from "../index";
import qs from "qs";
// 声明一个 Map 用于存储每个请求的标识 和 取消函数
let pendingMap = new Map<string, AbortController>();
const pendingMap = new Map<string, AbortController>();
// 序列化参数
export const getPendingUrl = (config: CustomAxiosRequestConfig) =>

View File

@@ -37,7 +37,7 @@ const shouldHiddenIndex = inject<Ref<number>>('shouldHiddenIndex', ref(-1))
watch(
() => [shouldHiddenIndex.value, breakPoint.value],
n => {
if (!!attrs.index) {
if (attrs.index) {
isShow.value = !(n[0] !== -1 && parseInt(attrs.index) >= Number(n[0]))
}
},

View File

@@ -28,7 +28,7 @@ const color = [
const chartRef = ref<HTMLDivElement>()
const props = defineProps(['options', 'isInterVal', 'pieInterVal'])
const props = defineProps(['options', 'isInterVal', 'pieInterVal', 'group'])
let chart: echarts.ECharts | any = null
const resizeHandler = () => {
// 不在视野中的时候不进行resize
@@ -47,6 +47,10 @@ const initChart = () => {
}
// chart?.dispose()
chart = echarts.init(chartRef.value as HTMLDivElement)
if (props.group) {
chart.group = props.group
echarts.connect(props.group)
}
const options = {
title: {

View File

@@ -19,13 +19,13 @@ const draggable: Directive = {
el.style.cursor = "move";
el.style.position = "absolute";
el.onmousedown = function (e) {
let disX = e.pageX - el.offsetLeft;
let disY = e.pageY - el.offsetTop;
const disX = e.pageX - el.offsetLeft;
const disY = e.pageY - el.offsetTop;
document.onmousemove = function (e) {
let x = e.pageX - disX;
let y = e.pageY - disY;
let maxX = el.parentNode.offsetWidth - el.offsetWidth;
let maxY = el.parentNode.offsetHeight - el.offsetHeight;
const maxX = el.parentNode.offsetWidth - el.offsetWidth;
const maxY = el.parentNode.offsetHeight - el.offsetHeight;
if (x < 0) {
x = 0;
} else if (x > maxX) {

View File

@@ -12,12 +12,12 @@
import type { Directive, DirectiveBinding } from "vue";
const addWaterMarker: Directive = (str: string, parentNode: any, font: any, textColor: string) => {
// 水印文字,父元素,字体,文字颜色
let can: HTMLCanvasElement = document.createElement("canvas");
const can: HTMLCanvasElement = document.createElement("canvas");
parentNode.appendChild(can);
can.width = 205;
can.height = 140;
can.style.display = "none";
let cans = can.getContext("2d") as CanvasRenderingContext2D;
const cans = can.getContext("2d") as CanvasRenderingContext2D;
cans.rotate((-20 * Math.PI) / 180);
cans.font = font || "16px Microsoft JhengHei";
cans.fillStyle = textColor || "rgba(180, 180, 180, 0.3)";

View File

@@ -10,7 +10,7 @@ export const useAuthButtons = () => {
const authStore = useAuthStore()
const authButtons = authStore.authButtonListGet[route.name as string] || []
const BUTTONS = computed(() => {
let currentPageAuthButton: { [key: string]: boolean } = {}
const currentPageAuthButton: { [key: string]: boolean } = {}
authButtons.forEach(item => (currentPageAuthButton[item] = true))
// currentPageAuthButton.status = true
return currentPageAuthButton

View File

@@ -10,7 +10,7 @@ export const useSelection = (rowKey: string = "id") => {
// 当前选中的所有 ids 数组
const selectedListIds = computed((): string[] => {
let ids: string[] = [];
const ids: string[] = [];
selectedList.value.forEach(item => ids.push(item[rowKey]));
return ids;
});

View File

@@ -98,9 +98,9 @@ export const useTable = (
const updatedTotalParam = () => {
state.totalParam = {};
// 处理查询参数,可以给查询参数加自定义前缀操作
let nowSearchParam: Table.StateProps["searchParam"] = {};
const nowSearchParam: Table.StateProps["searchParam"] = {};
// 防止手动清空输入框携带参数(这里可以自定义查询参数前缀)
for (let key in state.searchParam) {
for (const key in state.searchParam) {
// 某些情况下参数为 false/0 也应该携带参数
if (
state.searchParam[key] ||

View File

@@ -83,7 +83,10 @@ export const resetRouter = () => {
router.onError(error => {
NProgress.done()
console.warn('Route error', error.message)
console.error('[router] route error', {
message: error.message,
currentPath: router.currentRoute.value.fullPath
})
})
router.afterEach(to => {

View File

@@ -7,7 +7,7 @@ import { useAuthStore } from '@/stores/modules/auth'
// 引入 views 文件夹下所有 vue 文件
const modules = import.meta.glob('@/views/**/*.vue')
const STATIC_ROUTE_NAMES = new Set(['layout', 'login', 'home', '403', '404', '500'])
const STATIC_ROUTE_NAMES = new Set(['layout', 'login', 'home', 'tools', 'toolWaveform', 'toolMmsMapping', '403', '404', '500'])
let isInitializing = false
@@ -24,11 +24,12 @@ const clearDynamicRoutes = () => {
}
/**
* 根据 component 路径查找对应的模块
* @param path 组件路径
* 根据菜单 component 路径查找对应的页面模块
* 兼容两种仓库写法:
* 1. /foo/bar.vue
* 2. /foo/bar/index.vue
*/
const resolveComponentModule = async (path: string) => {
// 规范化路径,去除首尾斜杠
const resolveComponentModule = (path: string) => {
let normalizedPath = path.trim()
if (!normalizedPath.startsWith('/')) {
normalizedPath = '/' + normalizedPath
@@ -36,9 +37,26 @@ const resolveComponentModule = async (path: string) => {
if (normalizedPath.endsWith('.vue')) {
normalizedPath = normalizedPath.slice(0, -4)
}
if (normalizedPath.length > 1 && normalizedPath.endsWith('/')) {
normalizedPath = normalizedPath.slice(0, -1)
}
const fullPath = `/src/views${normalizedPath}.vue`
return modules[fullPath]
const candidatePaths = [`/src/views${normalizedPath}.vue`, `/src/views${normalizedPath}/index.vue`]
for (const candidatePath of candidatePaths) {
const moduleLoader = modules[candidatePath]
if (moduleLoader) {
return {
moduleLoader,
resolvedPath: candidatePath
}
}
}
return {
moduleLoader: undefined,
resolvedPath: candidatePaths
}
}
/**
@@ -50,6 +68,7 @@ export const initDynamicRouter = async () => {
isInitializing = true
const userStore = useUserStore()
const authStore = useAuthStore()
const unresolvedRoutes: Array<{ name?: string; path?: string; component?: string; candidates: string[] }> = []
try {
// 1. 获取菜单列表 && 按钮权限列表
@@ -81,11 +100,17 @@ export const initDynamicRouter = async () => {
// 处理组件映射
if (item.component && typeof item.component === 'string') {
const moduleLoader = await resolveComponentModule(item.component)
const { moduleLoader, resolvedPath } = resolveComponentModule(item.component)
if (moduleLoader) {
item.component = moduleLoader
} else {
console.warn(`未能找到组件: ${item.component}`)
// 动态路由组件一旦解析失败,对应菜单会落入 404这里必须打印清楚候选路径。
unresolvedRoutes.push({
name: item.name,
path: item.path,
component: item.component,
candidates: resolvedPath
})
continue
}
}
@@ -96,7 +121,7 @@ export const initDynamicRouter = async () => {
(typeof item.component === 'function' || typeof item.redirect === 'string')
) {
const routeItem = item as unknown as RouteRecordRaw
if (item.meta.isFull) {
if (item.meta?.isFull) {
router.addRoute(routeItem)
} else {
router.addRoute('layout', routeItem)
@@ -105,6 +130,10 @@ export const initDynamicRouter = async () => {
console.warn('Invalid route item:', item)
}
}
if (unresolvedRoutes.length) {
console.error('[dynamic-router] unresolved route components', unresolvedRoutes)
}
} catch (error) {
// 当按钮 || 菜单请求出错时,重定向到登陆页
userStore.setAccessToken('')

View File

@@ -34,6 +34,31 @@ export const staticRouter: RouteRecordRaw[] = [
isKeepAlive: false
}
},
{
path: '/tools',
name: 'tools',
component: () => import('@/views/tools/index.vue'),
meta: {
title: '工具中心'
}
},
{
path: '/tools/waveform',
name: 'toolWaveform',
component: () => import('@/views/tools/waveform/index.vue'),
meta: {
title: '波形查看'
}
},
{
path: '/tools/mmsMapping',
name: 'toolMmsMapping',
alias: ['/tools/mmsmapping', '/tools/mms-mapping'],
component: () => import('@/views/tools/mmsMapping/index.vue'),
meta: {
title: 'MMS 映射'
}
},
{
path: '/403',
name: '403',

View File

@@ -62,7 +62,7 @@ export function isType(val: any) {
export function generateUUID() {
let uuid = "";
for (let i = 0; i < 32; i++) {
let random = (Math.random() * 16) | 0;
const random = (Math.random() * 16) | 0;
if (i === 8 || i === 12 || i === 16 || i === 20) uuid += "-";
uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16);
}
@@ -77,13 +77,13 @@ export function generateUUID() {
*/
export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]: any }) {
if (!a || !b) return false;
let aProps = Object.getOwnPropertyNames(a);
let bProps = Object.getOwnPropertyNames(b);
const aProps = Object.getOwnPropertyNames(a);
const bProps = Object.getOwnPropertyNames(b);
if (aProps.length != bProps.length) return false;
for (let i = 0; i < aProps.length; i++) {
let propName = aProps[i];
let propA = a[propName];
let propB = b[propName];
const propName = aProps[i];
const propA = a[propName];
const propB = b[propName];
if (!b.hasOwnProperty(propName)) return false;
if (propA instanceof Object) {
if (!isObjectValueEqual(propA, propB)) return false;
@@ -101,7 +101,7 @@ export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]
* @returns {Number}
*/
export function randomNum(min: number, max: number): number {
let num = Math.floor(Math.random() * (min - max) + max);
const num = Math.floor(Math.random() * (min - max) + max);
return num;
}
@@ -110,8 +110,8 @@ export function randomNum(min: number, max: number): number {
* @returns {String}
*/
export function getTimeState() {
let timeNow = new Date();
let hours = timeNow.getHours();
const timeNow = new Date();
const hours = timeNow.getHours();
if (hours >= 6 && hours <= 10) return `早上好 ⛅`;
if (hours >= 10 && hours <= 14) return `中午好 🌞`;
if (hours >= 14 && hours <= 18) return `下午好 🌞`;
@@ -124,7 +124,7 @@ export function getTimeState() {
* @returns {String}
*/
export function getBrowserLang() {
let browserLang = navigator.language ? navigator.language : navigator.browserLanguage;
const browserLang = navigator.language ? navigator.language : navigator.browserLanguage;
let defaultBrowserLang = "";
if (["cn", "zh", "zh-cn"].includes(browserLang.toLowerCase())) {
defaultBrowserLang = "zh";
@@ -152,7 +152,7 @@ export function getUrlWithParams() {
* @returns {Array}
*/
export function getFlatMenuList(menuList: Menu.MenuOptions[]): Menu.MenuOptions[] {
let newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList));
const newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList));
return newMenuList.flatMap(item => [item, ...(item.children ? getFlatMenuList(item.children) : [])]);
}
@@ -162,7 +162,7 @@ export function getFlatMenuList(menuList: Menu.MenuOptions[]): Menu.MenuOptions[
* @returns {Array}
* */
export function getShowMenuList(menuList: Menu.MenuOptions[]) {
let newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList));
const newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList));
return newMenuList.filter(item => {
item.children?.length && (item.children = getShowMenuList(item.children));
return !item.meta?.isHide;

View File

@@ -1,2 +1,34 @@
/* global css variable */
$primary-color: var(--el-color-primary);
:root {
/* 波形图三相颜色 */
--cn-color-phase-a: #daa520;
--cn-color-phase-b: #2e8b57;
--cn-color-phase-c: #a52a2a;
/* 波形状态与常用业务颜色 */
--cn-color-run: #20b2aa;
--cn-color-breaks: #f4a460;
--cn-color-grey: #696969;
--cn-color-blue: #87ceeb;
--cn-color-orange: #ff7e50;
/* 画布与基础展示颜色 */
--cn-color-canvas-bg: #f9f9f9;
--cn-color-white: #ffffff;
--cn-color-purple: #800080;
--cn-color-lightgray: #cccccc;
--cn-color-dark-red: #a0522d;
/* ITIC 与 F47 曲线颜色 */
--cn-color-itic-top: #ff7e50;
--cn-color-itic-bottom: #00e3e3;
/* 电网主题与无数据状态颜色 */
--cn-color-guowang: #006565;
--cn-color-nanwang: #003078;
--cn-color-theme: #003078;
--cn-color-no-monitor: #cccccc;
--cn-color-no-data: #808080;
}

View File

@@ -7,7 +7,7 @@ import { ElMessage } from "element-plus";
*/
export function hexToRgb(str: any) {
let hexs: any = "";
let reg = /^\#?[0-9A-Fa-f]{6}$/;
const reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(str)) return ElMessage.warning("输入错误的hex");
str = str.replace("#", "");
hexs = str.match(/../g);
@@ -23,9 +23,9 @@ export function hexToRgb(str: any) {
* @returns {String} 返回处理后的颜色值
*/
export function rgbToHex(r: any, g: any, b: any) {
let reg = /^\d{1,3}$/;
const reg = /^\d{1,3}$/;
if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage.warning("输入错误的rgb颜色值");
let hexs = [r.toString(16), g.toString(16), b.toString(16)];
const hexs = [r.toString(16), g.toString(16), b.toString(16)];
for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
return `#${hexs.join("")}`;
}
@@ -38,9 +38,9 @@ export function rgbToHex(r: any, g: any, b: any) {
*/
export function getDarkColor(color: string, level: number) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
const reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
let rgb = hexToRgb(color);
const rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.round(20.5 * level + rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
@@ -53,9 +53,9 @@ export function getDarkColor(color: string, level: number) {
*/
export function getLightColor(color: string, level: number) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
const reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
let rgb = hexToRgb(color);
const rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.round(255 * level + rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}

View File

@@ -62,7 +62,7 @@ export function isType(val: any) {
export function generateUUID() {
let uuid = ''
for (let i = 0; i < 32; i++) {
let random = (Math.random() * 16) | 0
const random = (Math.random() * 16) | 0
if (i === 8 || i === 12 || i === 16 || i === 20) uuid += '-'
uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16)
}
@@ -77,13 +77,13 @@ export function generateUUID() {
*/
export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]: any }) {
if (!a || !b) return false
let aProps = Object.getOwnPropertyNames(a)
let bProps = Object.getOwnPropertyNames(b)
const aProps = Object.getOwnPropertyNames(a)
const bProps = Object.getOwnPropertyNames(b)
if (aProps.length != bProps.length) return false
for (let i = 0; i < aProps.length; i++) {
let propName = aProps[i]
let propA = a[propName]
let propB = b[propName]
const propName = aProps[i]
const propA = a[propName]
const propB = b[propName]
if (!b.hasOwnProperty(propName)) return false
if (propA instanceof Object) {
if (!isObjectValueEqual(propA, propB)) return false
@@ -101,7 +101,7 @@ export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]
* @returns {Number}
*/
export function randomNum(min: number, max: number): number {
let num = Math.floor(Math.random() * (min - max) + max)
const num = Math.floor(Math.random() * (min - max) + max)
return num
}
@@ -110,8 +110,8 @@ export function randomNum(min: number, max: number): number {
* @returns {String}
*/
export function getTimeState() {
let timeNow = new Date()
let hours = timeNow.getHours()
const timeNow = new Date()
const hours = timeNow.getHours()
if (hours >= 6 && hours <= 10) return `早上好 ⛅`
if (hours >= 10 && hours <= 14) return `中午好 🌞`
if (hours >= 14 && hours <= 18) return `下午好 🌞`
@@ -124,7 +124,7 @@ export function getTimeState() {
* @returns {String}
*/
export function getBrowserLang() {
let browserLang = navigator.language ? navigator.language : navigator.browserLanguage
const browserLang = navigator.language ? navigator.language : navigator.browserLanguage
let defaultBrowserLang = ''
if (['cn', 'zh', 'zh-cn'].includes(browserLang.toLowerCase())) {
defaultBrowserLang = 'zh'
@@ -152,7 +152,7 @@ export function getUrlWithParams() {
* @returns {Array}
*/
export function getFlatMenuList(menuList: Menu.MenuOptions[]): Menu.MenuOptions[] {
let newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList))
const newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList))
return newMenuList.flatMap(item => [item, ...(item.children ? getFlatMenuList(item.children) : [])])
}
@@ -162,7 +162,7 @@ export function getFlatMenuList(menuList: Menu.MenuOptions[]): Menu.MenuOptions[
* @returns {Array}
* */
export function getShowMenuList(menuList: Menu.MenuOptions[]) {
let newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList))
const newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList))
return newMenuList.filter(item => {
item.children?.length && (item.children = getShowMenuList(item.children))
return !item.meta?.isHide