3 Commits

Author SHA1 Message Date
yangsy 1e2ad96e56 docs(AGENTS.md): 新增AGENTS.md项目说明文档 2026-05-25 10:14:54 +08:00
yangsy 6c57af9e55 feat(vite配置): 添加杨浦21号线测试环境代理配置
新增杨浦21号线测试环境专用代理配置列表,添加注释的默认测试环境代理条目,并将新代理列表加入主代理配置中
2026-05-25 10:09:43 +08:00
yangsy ca7d6baa2e refactor: 重构NVR集群处理逻辑,优化设备树展示
抽离NVR集群相关的公共工具函数,重构isNvrCluster的实现逻辑。修复原有设备树中NVR分组的逻辑错误,原本会将所有单机NVR添加到每个集群的子节点中,现在会正确将单机NVR分配到对应集群,未归属的单机NVR作为独立节点。简化设备树组件的代码,统一使用封装后的工具方法处理集群分组。修复设备定位时的集群匹配逻辑,使用更准确的IP包含判断。初始化设备树数据的默认空值,修正类型定义错误。
2026-05-20 21:18:54 +08:00
4 changed files with 432 additions and 65 deletions
+271
View File
@@ -0,0 +1,271 @@
# 网络设备管理平台
## 项目概述
这是网络设备管理平台的前端项目,用于在地铁线路运管中心检测和查看各车站网络设备的详细数据、运行情况、异常告警,并提供分析、统计、日志和权限管理能力。
主要设备类型:
- 摄像机
- 网络录像机
- 交换机
- 解码器
- 智能安防箱
- 媒体服务器
- 视频服务器
- 网络键盘
- 报警主机
## 技术栈
- 包管理:pnpm
- 构建工具:Vite
- 前端框架:Vue 3
- 语言:TypeScript
- 路由:Vue Router
- 组件库:Naive UI
- 状态管理:Pinia + pinia-plugin-persistedstate
- 服务端状态/轮询:TanStack Vue Query
- 本地持久化:localStorage、sessionStorage、IndexedDB/localforage
- 网络请求:axios
- 实时消息:STOMP/WebSocket`@stomp/stompjs`
- 图表:ECharts
- 图标:lucide-vue-next
- 样式:Sass
## 环境与脚本
### 运行环境
- Node.js`^20.19.0 || >=22.12.0`
- pnpm:以 `package.json``packageManager` 为准
### 常用命令
```bash
pnpm install # 安装依赖
pnpm dev # 启动开发服务,默认端口 9763
pnpm build # 类型检查 + Vite 构建 + 构建产物压缩
pnpm preview # 预览构建产物
pnpm build-only # 仅执行 Vite 构建
pnpm type-check # 执行 vue-tsc 类型检查
pnpm lint # 执行 ESLint,并带 --fix
pnpm format # 使用 Prettier 格式化 src/
```
当前项目未配置测试脚本,也未发现测试文件;不要声称已运行单元测试。需要验证改动时,优先运行 `pnpm type-check``pnpm lint``pnpm build` 中与改动相关的命令。
### 构建流程
`pnpm build` 的实际流程为:
1. `tsx build/pre-build.ts`:根据 `package.json` 的版本和构建时间写入 `public/manifest.json`
2. `vue-tsc --build`:类型检查。
3. `vite build`:生成 `dist/`
4. `tsx build/post-build.ts`:基于 `dist/` 生成 `.zip``.tar``.tar.gz` 压缩包。
压缩包命名格式为:`ndm-web-platform_v<version>_<YYMMDD-HHmmss>`
## 目录结构
项目源码集中在 `src/`
```text
src/
apis/ # 接口客户端、接口模型、业务请求封装
components/ # 业务组件与全局组件
device/
global/
permission/
station/
composables/ # 组合式函数
alarm/
common/
device/
permission/
query/ # TanStack Query 轮询与请求编排
station/
stomp/ # STOMP/WebSocket 客户端
constants/ # 常量
enums/ # 枚举
helpers/ # 辅助逻辑
layouts/
app-layout.vue # 登录后主布局
pages/ # 页面
plugins/ # Pinia 持久化等插件
router/
index.ts # 路由配置与守卫
stores/ # Pinia stores
styles/ # 全局样式
types/ # 全局/工具类型
utils/ # 通用工具函数
```
路径别名:`@/*` 指向 `src/*`
## 页面与路由
路由配置在 `src/router/index.ts`。登录页独立于主布局;除 `/login` 外,其余页面都作为 `src/layouts/app-layout.vue` 的子路由。
```text
src/
router/
index.ts
layouts/
app-layout.vue
pages/
login/
login-page.vue # 登录页,对应 /login
station/
station-page.vue # 车站状态页/首页,对应 /station
device/
device-page.vue # 设备诊断页,对应 /device
alarm/
alarm-log-page.vue # 设备告警记录,对应 /alarm/alarm-log
alarm-ignore-page.vue # 告警忽略管理,对应 /alarm/alarm-ignore
log/
vimp-log-page.vue # 视频平台日志,对应 /log/vimp-log
call-log-page.vue # 上级调用日志,对应 /log/call-log
permission/
permission-page.vue # 权限管理,对应 /permission
system/
changelog/
changelog-page.vue # 更新记录,对应 /changelog
error/
not-found-page.vue # 404 页面,对应 catch-all
```
路由守卫规则:
- 未登录访问非 `/login` 页面会跳转到 `/login`
- 已登录访问 `/login` 会跳转到 `/`
- `/` 默认重定向到 `/station`
- `/permission` 需要 `useUserStore().isLamp` 为真,否则跳转到 404。
## 数据轮询与状态管理
由于后端服务按车站分布,前端需要向各车站服务依次请求数据。项目采用“单点驱动 + 变更监听 + 级联触发”的轮询模式。
核心文件位于 `src/composables/query/`
- `use-line-stations-query.ts`:查询所有车站,是业务轮询入口。
- `use-user-permission-query.ts`:查询并计算当前用户在各车站的权限,负责调度后续设备/告警查询。
- `use-line-devices-query.ts`:查询设备数据。
- `use-line-alarms-query.ts`:查询告警数据。
- `use-verify-user-query.ts`:用户登录/校验相关请求。
- `use-version-check-query.ts`:版本检查,依赖构建阶段生成的 `/manifest.json`
关键 Pinia stores 位于 `src/stores/`
- `user.ts`:用户登录态、Token、用户类型等。
- `station.ts`:车站数据。
- `device.ts`:设备数据。
- `alarm.ts`:告警数据。
- `permission.ts`:权限数据。
- `setting.ts`:系统设置、调试开关、网络开关等。
- `polling.ts`:轮询状态控制。
- `unread.ts`:未读状态。
持久化注意事项:
- 大体量业务数据会使用 IndexedDB/localforage。
- 普通设置类状态会使用 localStorage/sessionStorage。
- `src/main.ts` 会比较 `VITE_STORAGE_VERSION` 与本地 `ndm-storage-version`,不一致时清空 localStorage 并清空 localforage。
## 接口与代理
接口相关代码位于 `src/apis/`
- `client/`HTTP 客户端基础封装。
- `domain/`:领域相关类型/逻辑。
- `model/`:接口模型。
- `request/`:按业务拆分的请求函数。
开发代理配置在 `vite.config.ts`
- 开发服务端口:`9763`
- 已配置多个线路/站点前缀代理,包括 01、02、04、10、21 相关站点。
- 当前配置包含 `/api``/minio``/ws` 等代理项。
- `ProxyItem.rewrite` 用于将本地请求前缀改写为后端真实路径,例如将 `/1001/api` 改写为 `/api`
修改代理时,应同步检查 `key``target``rewrite``ws` 是否匹配实际环境。
## 环境变量
项目使用 Vite 环境变量,当前变量集中在 `.env`。文档或回答中只列变量名和用途,不要复述真实密钥、密码或授权值。
常见变量:
- `VITE_APP_TITLE`:页面标题。
- `VITE_REQUEST_INTERVAL`:轮询间隔,单位秒。
- `VITE_NDM_APP_KEY`:网管 appKey。
- `VITE_LAMP_CLIENT_ID`LAMP clientId。
- `VITE_LAMP_CLIENT_SECRET`LAMP clientSecret。
- `VITE_LAMP_USERNAME`LAMP 登录用户名。
- `VITE_LAMP_PASSWORD`LAMP 登录密码。
- `VITE_LAMP_AUTHORIZATION`:已有 Authorization 时直接使用,否则由 clientId/clientSecret 生成。
- `VITE_STORAGE_VERSION`:本地缓存版本,用于触发缓存清理。
- `VITE_DEBUG_CODE`:调试模式授权码。
## 调试模式与离线开发
调试模式默认隐藏,用于开发、联调和故障排查。
开启方式:
1. 使用快捷键 `Ctrl + Alt + D` 唤起验证弹窗。
2. 输入 `VITE_DEBUG_CODE` 对应授权码。
3. 验证通过后,“系统设置”中会显示调试分组。
调试相关能力:
- 显示设备原始数据。
- 控制是否轮询车站。
- 控制是否主动请求。
- 控制是否订阅 STOMP/WebSocket 消息。
- 启用模拟用户。
- 允许在特定场景下直接操作本地 IndexedDB。
离线开发:
- 如果浏览器已有现场缓存,可在调试模式中关闭轮询、主动请求和消息订阅,直接查看本地缓存。
- 全新环境可在登录页控制台设置 `window.$mockUser.value = true` 进入模拟登录,再通过调试面板导入 `docs/data/` 下的数据:
- `ndm-station-store.json`
- `ndm-device-store.json`
- `ndm-alarm-store.json`
## 代码风格与约定
- 遵循现有 Vue SFC、TypeScript、组合式函数和 Pinia 写法。
- 新增业务请求时优先放入 `src/apis/request/` 对应业务分类。
- 新增页面时同步更新 `src/router/index.ts`,并保持 `src/pages/` 目录与路由语义一致。
- 新增共享逻辑优先放入 `src/composables/``src/helpers/``src/utils/`,不要在页面中堆积重复逻辑。
- 新增全局/业务组件时优先放入 `src/components/` 对应分类。
- 使用 `@/` 引用 `src/` 下模块。
- 不要在代码、文档或回复中泄露真实密码、Token、Authorization 或现场地址以外的敏感信息。
格式化配置来自 `.prettierrc.json`
- LF 换行
- 2 空格缩进
- 单引号
- 使用分号
- `trailingComma: all`
- `printWidth: 200`
ESLint 重点规则:
- `@typescript-eslint/no-unused-vars` 为 warn。
- `@typescript-eslint/no-explicit-any` 当前关闭,但新增代码仍应尽量保持类型清晰。
- `vue/multi-word-component-names` 当前关闭。
## 协作规则
1. 默认不要直接修改项目代码;当用户明确授权修改时,只修改授权范围内的文件。
2. 修改前先阅读相关文件和既有实现,避免凭空猜测目录、接口或状态结构。
3. 修改后说明改了哪些文件、为什么改、如何验证。
4. 文档类修改至少重新读取目标文件确认内容正确。
5. 代码类修改应按影响范围运行 `pnpm type-check``pnpm lint``pnpm build` 或更小范围的可用验证命令。
6. 不要提交 Git commit,除非用户明确要求。
7. 不要删除失败测试或通过压制类型错误来规避问题。
8. 不要在最终说明中声称执行了未实际执行的命令。
@@ -8,7 +8,7 @@ const createDeviceNodeKey = (stationCode?: Station['code'], device?: NdmDeviceRe
import { initStationDevices, type NdmDeviceResultVO, type NdmNvrResultVO, type Station } from '@/apis'; import { initStationDevices, type NdmDeviceResultVO, type NdmNvrResultVO, type Station } from '@/apis';
import { useDeviceTree, usePermission, type UseDeviceTreeReturn } from '@/composables'; import { useDeviceTree, usePermission, type UseDeviceTreeReturn } from '@/composables';
import { DEVICE_TYPE_NAMES, DEVICE_TYPE_LITERALS, tryGetDeviceType, type DeviceType, PERMISSION_TYPE_LITERALS } from '@/enums'; import { DEVICE_TYPE_NAMES, DEVICE_TYPE_LITERALS, tryGetDeviceType, type DeviceType, PERMISSION_TYPE_LITERALS } from '@/enums';
import { isNvrCluster } from '@/helpers'; import { createNvrClusterRelationship, isNvrCluster, nvrInCluster } from '@/helpers';
import { useDeviceStore, usePermissionStore } from '@/stores'; import { useDeviceStore, usePermissionStore } from '@/stores';
import { watchDebounced, watchImmediate } from '@vueuse/core'; import { watchDebounced, watchImmediate } from '@vueuse/core';
import destr from 'destr'; import destr from 'destr';
@@ -297,8 +297,18 @@ const renderDeviceNodePrefix = (device: NdmDeviceResultVO, stationCode: Station[
return h(NFlex, { size: 'small' }, { default: () => [renderViewDeviceButton(device, stationCode), renderDeviceStatusTag(device)] }); return h(NFlex, { size: 'small' }, { default: () => [renderViewDeviceButton(device, stationCode), renderDeviceStatusTag(device)] });
}; };
// 全线设备树 // 全线设备树
const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() => { const lineDeviceTreeData = computed<Record<DeviceType, TreeOption[]>>(() => {
const treeData: Record<string, TreeOption[]> = {}; const treeData: Record<DeviceType, TreeOption[]> = {
[DEVICE_TYPE_LITERALS.ndmCamera]: [],
[DEVICE_TYPE_LITERALS.ndmNvr]: [],
[DEVICE_TYPE_LITERALS.ndmSwitch]: [],
[DEVICE_TYPE_LITERALS.ndmDecoder]: [],
[DEVICE_TYPE_LITERALS.ndmSecurityBox]: [],
[DEVICE_TYPE_LITERALS.ndmMediaServer]: [],
[DEVICE_TYPE_LITERALS.ndmVideoServer]: [],
[DEVICE_TYPE_LITERALS.ndmKeyboard]: [],
[DEVICE_TYPE_LITERALS.ndmAlarmHost]: [],
};
deviceTabPanes.forEach(({ name: paneName /* , tab: paneTab */ }) => { deviceTabPanes.forEach(({ name: paneName /* , tab: paneTab */ }) => {
treeData[paneName] = stations.value.map<TreeOption>((station) => { treeData[paneName] = stations.value.map<TreeOption>((station) => {
const { name: stationName, code: stationCode } = station; const { name: stationName, code: stationCode } = station;
@@ -307,28 +317,39 @@ const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() =>
const offlineDevices = devices?.filter((device) => device.deviceStatus === '20'); const offlineDevices = devices?.filter((device) => device.deviceStatus === '20');
// 对于录像机,需要根据clusterList字段以分号分隔设备IP,进一步形成子树结构 // 对于录像机,需要根据clusterList字段以分号分隔设备IP,进一步形成子树结构
if (paneName === DEVICE_TYPE_LITERALS.ndmNvr) { if (paneName === DEVICE_TYPE_LITERALS.ndmNvr) {
const nvrs = devices as NdmNvrResultVO[]; const nvrDevices = devices as NdmNvrResultVO[];
const nvrClusters: NdmNvrResultVO[] = [];
const nvrSingletons: NdmNvrResultVO[] = []; const { nvrClusters, nvrTreeMap, nvrStandalones } = createNvrClusterRelationship(nvrDevices);
for (const device of nvrs) {
if (isNvrCluster(device)) {
nvrClusters.push(device);
} else {
nvrSingletons.push(device);
}
}
return { return {
label: stationName, label: stationName,
key: stationCode, key: stationCode,
prefix: () => renderStationNodePrefix(station), prefix: () => renderStationNodePrefix(station),
suffix: () => renderIcmpStatistics(onlineDevices?.length ?? 0, offlineDevices?.length ?? 0, devices?.length ?? 0), suffix: () => renderIcmpStatistics(onlineDevices?.length ?? 0, offlineDevices?.length ?? 0, devices?.length ?? 0),
children: nvrClusters.map<TreeOption>((cluster) => { children: [
...nvrClusters.map((cluster) => {
return { return {
label: `${cluster.name}`, label: `${cluster.name}`,
key: createDeviceNodeKey(stationCode, cluster), key: createDeviceNodeKey(stationCode, cluster),
prefix: () => renderDeviceNodePrefix(cluster, stationCode), prefix: () => renderDeviceNodePrefix(cluster, stationCode),
suffix: () => `${cluster.ipAddress}`, suffix: () => `${cluster.ipAddress}`,
children: nvrSingletons.map<TreeOption>((device) => { children: (nvrTreeMap.get(cluster.ipAddress ?? '') ?? []).map((clusterNode) => {
return {
label: `${clusterNode.name}`,
key: createDeviceNodeKey(stationCode, clusterNode),
prefix: () => renderDeviceNodePrefix(clusterNode, stationCode),
suffix: () => `${clusterNode.ipAddress}`,
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
stationCode,
device: clusterNode,
};
}),
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站
stationCode,
device: cluster,
};
}),
...nvrStandalones.map((device) => {
return { return {
label: `${device.name}`, label: `${device.name}`,
key: createDeviceNodeKey(stationCode, device), key: createDeviceNodeKey(stationCode, device),
@@ -339,15 +360,12 @@ const lineDeviceTreeData = computed<Record<Station['code'], TreeOption[]>>(() =>
device: device, device: device,
}; };
}), }),
// 当选择设备时,能获取到设备的所有信息,以及设备所属的车站 ],
stationCode,
device: cluster,
};
}),
stationCode, stationCode,
deviceType: activeTab.value, deviceType: activeTab.value,
}; };
} }
// 非录像机设备
return { return {
label: stationName, label: stationName,
key: stationCode, key: stationCode,
@@ -382,20 +400,36 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
const onlineCount = stationDevices[deviceType].filter((device) => device.deviceStatus === '10').length; const onlineCount = stationDevices[deviceType].filter((device) => device.deviceStatus === '10').length;
const offlineCount = stationDevices[deviceType].filter((device) => device.deviceStatus === '20').length; const offlineCount = stationDevices[deviceType].filter((device) => device.deviceStatus === '20').length;
if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) { if (deviceType === DEVICE_TYPE_LITERALS.ndmNvr) {
const nvrs = stationDevices[deviceType] as NdmNvrResultVO[]; const nvrDevices = stationDevices[deviceType] as NdmNvrResultVO[];
const clusters = nvrs.filter((nvr) => isNvrCluster(nvr));
const singletons = nvrs.filter((nvr) => !isNvrCluster(nvr)); const { nvrClusters, nvrTreeMap, nvrStandalones } = createNvrClusterRelationship(nvrDevices);
return { return {
label: `${DEVICE_TYPE_NAMES[deviceType]}`, label: `${DEVICE_TYPE_NAMES[deviceType]}`,
key: deviceType, key: deviceType,
suffix: () => renderIcmpStatistics(onlineCount, offlineCount, nvrs.length), suffix: () => renderIcmpStatistics(onlineCount, offlineCount, nvrDevices.length),
children: clusters.map<TreeOption>((cluster) => { children: [
...nvrClusters.map((cluster) => {
return { return {
label: `${cluster.name}`, label: `${cluster.name}`,
key: createDeviceNodeKey(stationCode, cluster), key: createDeviceNodeKey(stationCode, cluster),
prefix: () => renderDeviceNodePrefix(cluster, stationCode), prefix: () => renderDeviceNodePrefix(cluster, stationCode),
suffix: () => `${cluster.ipAddress}`, suffix: () => `${cluster.ipAddress}`,
children: singletons.map<TreeOption>((device) => { children: (nvrTreeMap.get(cluster.ipAddress ?? '') ?? []).map((clusterNode) => {
return {
label: `${clusterNode.name}`,
key: createDeviceNodeKey(stationCode, clusterNode),
prefix: () => renderDeviceNodePrefix(clusterNode, stationCode),
suffix: () => `${clusterNode.ipAddress}`,
stationCode,
device: clusterNode,
};
}),
stationCode,
device: cluster,
};
}),
...nvrStandalones.map((device) => {
return { return {
label: `${device.name}`, label: `${device.name}`,
key: createDeviceNodeKey(stationCode, device), key: createDeviceNodeKey(stationCode, device),
@@ -405,14 +439,12 @@ const stationDeviceTreeData = computed<TreeOption[]>(() => {
device, device,
}; };
}), }),
stationCode, ],
device: cluster,
};
}),
stationCode, stationCode,
deviceType, deviceType,
}; };
} }
// 非录像机设备
return { return {
label: `${DEVICE_TYPE_NAMES[deviceType]}`, label: `${DEVICE_TYPE_NAMES[deviceType]}`,
key: deviceType, key: deviceType,
@@ -488,9 +520,13 @@ const onLocateDeviceTree = async () => {
if (stationDevices) { if (stationDevices) {
const selectedNvr = selectedDevice.value as NdmNvrResultVO; const selectedNvr = selectedDevice.value as NdmNvrResultVO;
if (!isNvrCluster(selectedNvr)) { if (!isNvrCluster(selectedNvr)) {
const nvrs = stationDevices[DEVICE_TYPE_LITERALS.ndmNvr]; const nvrDevices = stationDevices[DEVICE_TYPE_LITERALS.ndmNvr];
const clusters = nvrs.filter((nvr) => isNvrCluster(nvr) && nvr.clusterList?.includes(selectedNvr.clusterList ?? '')); const clusters = nvrDevices.filter((device) => {
expandedKeys.value.push(...clusters.map((nvr) => createDeviceNodeKey(stationCode, nvr))); if (!isNvrCluster(device)) return false;
const cluster = device;
return nvrInCluster(selectedNvr, cluster);
});
expandedKeys.value.push(...clusters.map((cluster) => createDeviceNodeKey(stationCode, cluster)));
} }
} }
} }
+51 -3
View File
@@ -1,8 +1,56 @@
import type { NdmNvrResultVO } from '@/apis'; import type { NdmNvrResultVO } from '@/apis';
// 解析 clusterList 字段
export const parseIpListFromClusterList = (nvr: NdmNvrResultVO) => {
const ipList = (nvr.clusterList ?? '').split(';');
return ipList.map((ip) => ip.trim()).filter((ip) => !!ip);
};
export const isNvrCluster = (maybeNvrCluster: NdmNvrResultVO) => { export const isNvrCluster = (maybeNvrCluster: NdmNvrResultVO) => {
const { ipAddress, clusterList } = maybeNvrCluster; const { ipAddress } = maybeNvrCluster;
if (!clusterList?.trim()) return false; const ipList = parseIpListFromClusterList(maybeNvrCluster);
if (clusterList === ipAddress) return false; if (ipList.length === 0) return false;
if (ipList.length === 1 && ipList.at(0) === ipAddress) return false;
return true; return true;
}; };
export const nvrInCluster = (nvr: NdmNvrResultVO, cluster: NdmNvrResultVO) => {
const { ipAddress } = nvr;
if (!ipAddress) return false;
const ipList = parseIpListFromClusterList(cluster);
return ipList.includes(ipAddress);
};
export const createNvrClusterRelationship = (nvrDevices: NdmNvrResultVO[]) => {
const nvrClusters = nvrDevices.filter((nvr) => isNvrCluster(nvr));
const nvrNotClusters = nvrDevices.filter((nvr) => !isNvrCluster(nvr));
const nodedNvrIpAddressSet = new Set<string | null>();
const nvrStandalones: NdmNvrResultVO[] = [];
const nvrTreeMap = new Map<string, NdmNvrResultVO[]>();
// 遍历所有非集群录像机,将它们分配到对应的录像机集群中
for (const nvr of nvrNotClusters) {
for (const cluster of nvrClusters) {
if (nvrInCluster(nvr, cluster)) {
if (!!cluster.ipAddress) {
// 写入录像机与集群的关系
nvrTreeMap.set(cluster.ipAddress, [...(nvrTreeMap.get(cluster.ipAddress) ?? []), nvr]);
// 记录已分配的录像机IP地址
nodedNvrIpAddressSet.add(nvr.ipAddress);
}
}
}
}
// 分配完成后,过滤出未分配的录像机,形成录像机单机列表
nvrNotClusters.forEach((device) => {
if (!nodedNvrIpAddressSet.has(device.ipAddress)) {
nvrStandalones.push(device);
}
});
return {
nvrClusters,
// nvrNotClusters,
nvrTreeMap,
nvrStandalones,
};
};
+12
View File
@@ -175,6 +175,12 @@ const line10ApiProxyList: ProxyItem[] = [
{ key: '/1032/api', target: 'http://10.18.244.10:18760', rewrite: ['/1032/api', '/api'] }, { key: '/1032/api', target: 'http://10.18.244.10:18760', rewrite: ['/1032/api', '/api'] },
]; ];
const line21YangpuTestProxyList: ProxyItem[] = [
{ key: '/2175/api', target: 'http://10.24.0.10:18760', rewrite: ['/2175/api', '/api'] },
{ key: '/2109/api', target: 'http://10.24.17.10:18760', rewrite: ['/2109/api', '/api'] },
{ key: '/2180/api', target: 'http://10.24.116.10:18760', rewrite: ['/2180/api', '/api'] },
];
const apiProxyList: ProxyItem[] = [ const apiProxyList: ProxyItem[] = [
// { key: '/minio', target: 'http://10.14.0.10:9000', rewrite: ['/minio', ''] }, // { key: '/minio', target: 'http://10.14.0.10:9000', rewrite: ['/minio', ''] },
// { key: '/api', target: 'http://10.14.0.10:18760' }, // { key: '/api', target: 'http://10.14.0.10:18760' },
@@ -195,6 +201,12 @@ const apiProxyList: ProxyItem[] = [
{ key: '/api', target: 'http://10.18.128.10:18760' }, { key: '/api', target: 'http://10.18.128.10:18760' },
{ key: '/ws', target: 'ws://10.18.128.10:18103', ws: true }, { key: '/ws', target: 'ws://10.18.128.10:18103', ws: true },
...line10ApiProxyList, ...line10ApiProxyList,
// 杨浦厂验环境
// { key: '/minio', target: 'http://10.24.0.10:9000', rewrite: ['/minio', ''] },
// { key: '/api', target: 'http://10.24.0.10:18760' },
// { key: '/ws', target: 'ws://10.24.0.10:18103', ws: true },
...line21YangpuTestProxyList,
]; ];
// https://vite.dev/config/ // https://vite.dev/config/