12 changed files with 397 additions and 27 deletions
@ -1,4 +1,4 @@ |
|||||||
VITE_BASE_API = '/remoteServer/admin-api/' |
VITE_BASE_API = '/remoteServer/admin-api/' |
||||||
VITE_BASE_API_SYSTEM = '/remoteServer/admin-api/system/' |
VITE_BASE_API_SYSTEM = '/remoteServer/admin-api/system/' |
||||||
VITE_SHOW_ONLINE_DEVICE = true |
VITE_SHOW_ONLINE_DEVICE = true |
||||||
VITE_BASE_URL = 'http://192.168.1.63:48089' |
VITE_BASE_URL = 'http://43.140.245.32:48089' |
||||||
@ -1,33 +1,193 @@ |
|||||||
# vue-project |
# AI 与 编程范式 (AI & Trends) |
||||||
|
|
||||||
This template should help get you started developing with Vue 3 in Vite. |
- **怎么理解现在的 AI 对程序员的影响?** |
||||||
|
- **AI 辅助开发实践:** |
||||||
|
- 如果用 AI 来写虚拟列表,提示词(Prompt)该怎么设计? |
||||||
|
- 如何对 AI 生成的代码进行 Code Review? |
||||||
|
|
||||||
## Recommended IDE Setup |
--- |
||||||
|
|
||||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). |
# 前端工程化 (Engineering) |
||||||
|
|
||||||
## Type Support for `.vue` Imports in TS |
- **构建工具:** Webpack 的打包流程、常用 Loaders 和 Plugins 的作用、性能优化技巧(如懒加载、代码分割)。Vite |
||||||
|
的构建原理(如 ESBuild 预编译、HMR 机制)及与 Webpack 的区别。 |
||||||
|
1. **Loader:** |
||||||
|
- **作用:** Loader用于处理模块中的资源文件,将它们转换为Webpack可以理解的模块。 |
||||||
|
- **资源处理:** Loader处理各种资源文件,如JavaScript、CSS、图片、字体等,执行加载、转换、编译等任务。 |
||||||
|
- **模块级别:** Loader工作在模块级别,通常用于处理单个文件或模块,它们直接与模块的内用容交互。 |
||||||
|
- **配置:** Loader通过 `module.rules` 进行配置。示例:Babel Loader用于将ES6+ 转换为ES5,CSS |
||||||
|
Loader用于加载CSS文件等。 |
||||||
|
2. **Plugin:** |
||||||
|
- **作用:** Plugin用于扩展Webpack的功能,执行各种自定义构建任务和优化。 |
||||||
|
- **构建过程控制:** Plugin可以介入Webpack的构建过程,在不同的生命周期阶段执行任务,如代码压缩、文件生成、HTML注入等。 |
||||||
|
- **应用级别:** Plugin工作在应用程序级别,可以操作整个构建过程,包括资源文件的加载和输出。 |
||||||
|
- **配置:** Plugin通过 `plugins` 进行配置。 |
||||||
|
- **示例:** HtmlWebpackPlugin用于生成HTML文件,UglifyJSPlugin用于代码压缩等。 |
||||||
|
|
||||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. |
- **工程化优化:** Tree Shaking 移除未引用代码、按需加载第三方库。 |
||||||
|
|
||||||
## Customize configuration |
- **代码规范:** ESLint、Prettier、Husky 等在项目中的应用。 |
||||||
|
|
||||||
See [Vite Configuration Reference](https://vite.dev/config/). |
- **Git:** 常用工作流、代码合并与冲突解决。 |
||||||
|
|
||||||
## Project Setup |
- **SSR/SSG:** 项目如何支持 SSG,并做 SEO?(你 Nuxt 项目相关) |
||||||
|
|
||||||
```sh |
--- |
||||||
npm install |
|
||||||
``` |
|
||||||
|
|
||||||
### Compile and Hot-Reload for Development |
# JavaScript 核心与基础 (JS Core & Basics) |
||||||
|
|
||||||
```sh |
- **数据类型:** - JS 有哪些数据类型? |
||||||
npm run dev |
- `undefined` 和 `null` 的区别是什么? |
||||||
``` |
- `typeof(undefined)` 和 `typeof(null)` 相等吗?(注意 `typeof null === 'object'` 的历史遗留问题)。 |
||||||
|
|
||||||
### Type-Check, Compile and Minify for Production |
- **运行机制:** |
||||||
|
- JS 为什么是单线程的? |
||||||
|
- **事件循环机制 (Event Loop):** 宏任务与微任务的执行顺序。 |
||||||
|
- **函数缓存 (Memoization):** 如何实现?有什么应用场景? |
||||||
|
- **异步编程:** 怎么实现异步编程? |
||||||
|
|
||||||
```sh |
- **Promise 深入:** |
||||||
npm run build |
- 讲一讲 Promise 的原理。 |
||||||
``` |
- `Promise.race` 和 `Promise.all` 的区别。 |
||||||
|
- **场景题:** 三个相同的数组三次传入 `Promise.race` 会出现什么结果? |
||||||
|
- Promise 的执行时机(微任务队列)。 |
||||||
|
- Promise 控制、并发限制。 |
||||||
|
- `async/await` 的底层原理。 |
||||||
|
|
||||||
|
- **对象与操作:** |
||||||
|
- **深比较:** 如何实现两个对象的深比较? |
||||||
|
- **深拷贝与浅拷贝**。 |
||||||
|
|
||||||
|
- **高级特性:** |
||||||
|
- EventEmitter 的实现。 |
||||||
|
- Proxy 与 Object.defineProperty 的区别(Proxy 优势:监听数组、无需递归遍历、拦截方式多)。 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
# CSS 与 响应式 (CSS & Responsive) |
||||||
|
|
||||||
|
- **单位辨析:** `px`、`rem`、`vw` 的区别与适用场景。 |
||||||
|
- **多端适配:** 手机端和 PC 端用同一套 HTML 要注意什么细节?(媒体查询、视口设置、交互差异)。 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
# 前端框架 (React / Vue) |
||||||
|
|
||||||
|
- **组件设计:** |
||||||
|
- **组件封装能力:** 怎么样去封装一个组件(例如 el-dialog 弹窗)? |
||||||
|
- *追问方向:* API 设计理念、Props/Events 定义、插槽 (Slots) 使用、状态管理(v-model)、生命周期处理、Teleport |
||||||
|
的使用等。 |
||||||
|
|
||||||
|
- **框架原理:** |
||||||
|
- React / Vue 的 diff 机制对比? |
||||||
|
- Vue 组合式 API (Composition API):与 Options API 的对比,`ref` vs `reactive`,生命周期映射。 |
||||||
|
- 虚拟 DOM (VDOM) 原理与性能优化。 |
||||||
|
- 响应式原理(Vue 2 vs Vue 3)。 |
||||||
|
|
||||||
|
- **状态管理:** Redux / Mobx / Vuex / Pinia 的设计思想和适用场景。 |
||||||
|
|
||||||
|
- **高阶能力:** |
||||||
|
- 如何构建一个可拓展的 schema-form(表单系统)? |
||||||
|
- 动态路由、微前端架构。 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
# 浏览器与网络 (Browser & Network) |
||||||
|
|
||||||
|
- **可视区域判断:** 判断一个元素是否在窗口内(`getBoundingClientRect` vs `IntersectionObserver`)。 |
||||||
|
|
||||||
|
- **列表与滚动优化:** |
||||||
|
- **虚拟列表 (Virtual List):** 怎么实现?复杂度是多少?(如果是 AI 写的代码如何 Review)。 |
||||||
|
- **加载机制:** 下拉加载(Pull-to-refresh)和滚动加载(Infinite Scroll)怎么实现? |
||||||
|
|
||||||
|
- **文件处理:** |
||||||
|
- **大文件上传:** 分片上传、断点续传、秒传的实现原理。 |
||||||
|
- 前端如何处理视频编码/解码?(ffmpeg.wasm)。 |
||||||
|
|
||||||
|
- **性能优化:** |
||||||
|
- **加载性能:** 减少 HTTP 请求、压缩资源、浏览器缓存、代码分割、懒加载、Gzip、CDN。 |
||||||
|
- **运行时性能:** 避免频繁 DOM 操作、防抖 (Debounce)与节流 (Throttle)、Web Worker、重绘与回流优化。 |
||||||
|
- **OffscreenCanvas** 的使用。 |
||||||
|
|
||||||
|
- **缓存策略:** |
||||||
|
- **强缓存 (`Cache-Control`):** - `max-age`: 有效期。 |
||||||
|
- `no-cache`: 不走强缓存,走协商缓存。 |
||||||
|
- `no-store`: 彻底不缓存。 |
||||||
|
- **协商缓存 (304):** - `Last-Modified` / `If-Modified-Since` (基于时间,有秒级误差)。 |
||||||
|
- `ETag` / `If-None-Match` (基于内容指纹,更精准)。 |
||||||
|
|
||||||
|
- **网络协议:** |
||||||
|
- **HTTP/1.1 vs HTTP/2 vs HTTP/3:** 多路复用、头部压缩、QUIC 协议。 |
||||||
|
- **跨域:** CORS、JSONP、Nginx 反代理。 |
||||||
|
- **通信方式:** REST vs RPC;WebSocket vs SSE vs EventSource。 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
# 算法 (Algorithms) |
||||||
|
|
||||||
|
- **几何算法:** 判断一个点是否在一个三角形内部(面积法、向量叉积法、重心坐标法)。 |
||||||
|
- **常用手写:** 手写 Promise、手写防抖节流、深拷贝。 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
# 库与工具 (Tools & Libs) |
||||||
|
|
||||||
|
- **Redis:** 用途(缓存、消息队列、分布式锁)。 |
||||||
|
- **消息队列:** Kafka / RabbitMQ / MQTT / ZMQ 的区别。 |
||||||
|
- **微服务通信:** 服务间如何交互。 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
# 前端安全 (Security) |
||||||
|
|
||||||
|
- **XSS (跨站脚本攻击):** 原理(注入恶意脚本)、防范(转义、CSP)。 |
||||||
|
- **CSRF (跨站请求伪造):** 原理(利用用户凭证)、防范(SameSite Cookie、Token/Referer 验证)。 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
# 框架对比 (Framework Comparison) |
||||||
|
|
||||||
|
- React 和 Vue 的区别,以及你对它们的理解。 |
||||||
|
- 微服务、实时流、渲染机制等差异理解。 |
||||||
|
- 微调 (Fine-tuning) 和 RAG (检索增强生成) 在 AI 应用中的概念。 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
# WebSocket 场景 |
||||||
|
|
||||||
|
- **WebSocket 连接在弱网环境下不稳定如何处理?心跳机制如何设计?** |
||||||
|
1. **智能重连机制 (Reconnection Strategy)** |
||||||
|
- **指数退避算法 (Exponential Backoff):** 重连间隔逐步递增 (1s -> 2s -> 4s -> ...)。 |
||||||
|
- **随机抖动 (Jitter):** 增加随机数,避免拥塞。 |
||||||
|
- **最大重试次数:** 避免无限空耗。 |
||||||
|
2. **可靠消息保证 (Message Reliability)** |
||||||
|
- **消息队列:** 前端维护发送缓冲队列 (Buffer Queue)。 |
||||||
|
- **ACK 机制与序列号:** 消息带 `id`/`seq`,收到 Server ACK 后才从队列移除。 |
||||||
|
- **重连补发:** 连接恢复后,检查队列补发未 ACK 消息。 |
||||||
|
|
||||||
|
- **高并发消息处理(聊天室 200 -> 500 人):** |
||||||
|
1. **逻辑层:** Set 防重、消息缓冲池。 |
||||||
|
2. **渲染层:** 虚拟列表 (Virtual List)、requestAnimationFrame 分批渲染/节流刷帧。 |
||||||
|
3. **视觉层:** 同用户消息合并。 |
||||||
|
|
||||||
|
- **多 Tab 共享连接(避免服务器压力):** |
||||||
|
- **SharedWorker (最佳实践):** 所有 Tab 连接同一个 Worker,由 Worker 维持唯一的 WS 连接并分发消息。 |
||||||
|
- **BroadcastChannel:** 主 Tab 维护连接,广播给其他 Tab。 |
||||||
|
|
||||||
|
- **WebSocket 消息顺序如何保证?** |
||||||
|
- **后端:** 发送严格递增的 `seqId`。 |
||||||
|
- **前端:** 维护 `nextExpectedSeqId` 和 **排序缓冲池**。 |
||||||
|
1. **命中:** `seqId === nextExpectedSeqId` -> 渲染,`next++`。 |
||||||
|
2. **超前:** `seqId > nextExpectedSeqId` -> 放入缓冲池暂存,由于可能丢包,开启定时器尝试“补洞”。 |
||||||
|
3. **过期:** `seqId < nextExpectedSeqId` -> 丢弃。 |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
# Web Worker 相关 |
||||||
|
|
||||||
|
- **Worker 场景:** 复杂 JSON 解析、路径规划、图像滤镜等 CPU 密集型任务。 |
||||||
|
- **通信与性能:** |
||||||
|
- `postMessage` (结构化克隆): 默认行为,数据拷贝,有性能开销 ($O(n)$)。 |
||||||
|
- **`Transferable` (零拷贝):** 适用于 `ArrayBuffer` 等二进制数据。 |
||||||
|
- **特点:** 瞬间完成 ($O(1)$),但原对象在发送方会失效 (Neutering)。 |
||||||
|
- **注意:** 只能转移 `buffer` (如 `myUint8Array.buffer`)。 |
||||||
@ -0,0 +1,150 @@ |
|||||||
|
<template> |
||||||
|
<el-drawer |
||||||
|
v-model="visible" |
||||||
|
title="点位详情" |
||||||
|
direction="rtl" |
||||||
|
size="70%" |
||||||
|
:before-close="handleClose" |
||||||
|
> |
||||||
|
<div class="h-full flex flex-col" v-loading="loading"> |
||||||
|
<div class="mb-4 flex gap-2"> |
||||||
|
<el-input |
||||||
|
v-model="searchKeyword" |
||||||
|
placeholder="搜索" |
||||||
|
style="width: 300px" |
||||||
|
clearable |
||||||
|
:prefix-icon="Search" |
||||||
|
@input="handleSearch" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
<div class="flex-1"> |
||||||
|
<ag-grid-vue |
||||||
|
class="ag-theme-quartz h-full w-full" |
||||||
|
:rowData="processedRowData" |
||||||
|
:columnDefs="computedColDefs" |
||||||
|
:animateRows="false" |
||||||
|
:rowHeight="40" |
||||||
|
@grid-ready="onGridReady" |
||||||
|
> |
||||||
|
</ag-grid-vue> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</el-drawer> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import { ref, shallowRef, computed } from 'vue' |
||||||
|
import { ElMessage } from 'element-plus' |
||||||
|
import { AgGridVue } from 'ag-grid-vue3' |
||||||
|
import { Search } from '@element-plus/icons-vue' |
||||||
|
import { getPointFileList } from '@/api/module/device/category' |
||||||
|
const visible = ref(false) |
||||||
|
const loading = ref(false) |
||||||
|
const pointList = ref<any[][]>([]) |
||||||
|
|
||||||
|
const searchKeyword = ref('') |
||||||
|
|
||||||
|
const col = ref([ |
||||||
|
'点位地址', |
||||||
|
'点数量', |
||||||
|
'功能码', |
||||||
|
'数据类型', |
||||||
|
'点读取周期ms', |
||||||
|
'点位段名称', |
||||||
|
]) |
||||||
|
|
||||||
|
const processedRowData = computed(() => { |
||||||
|
return pointList.value.map(row => { |
||||||
|
const obj: any = {} |
||||||
|
col.value.forEach((header, index) => { |
||||||
|
obj[header] = row[index] |
||||||
|
}) |
||||||
|
return obj |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
const computedColDefs = computed(() => { |
||||||
|
const colDefs = col.value.map((header, index) => { |
||||||
|
const isLast = index === col.value.length - 1 |
||||||
|
const def: any = { |
||||||
|
field: header, |
||||||
|
headerName: header, |
||||||
|
minWidth: 120, |
||||||
|
flex: isLast ? 1 : 0, |
||||||
|
sortable: true, |
||||||
|
} |
||||||
|
|
||||||
|
// Hex sorting for Point Address and Function Code |
||||||
|
if (header === '点位地址' || header === '功能码') { |
||||||
|
def.comparator = (valueA: string, valueB: string) => { |
||||||
|
const numA = parseInt(valueA, 16) || 0 |
||||||
|
const numB = parseInt(valueB, 16) || 0 |
||||||
|
return numA - numB |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Numeric sorting for Count and Period |
||||||
|
if (header === '点数量' || header === '点读取周期ms') { |
||||||
|
def.comparator = (valueA: string, valueB: string) => { |
||||||
|
const numA = parseFloat(valueA) || 0 |
||||||
|
const numB = parseFloat(valueB) || 0 |
||||||
|
return numA - numB |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return def |
||||||
|
}) |
||||||
|
|
||||||
|
console.log(colDefs) |
||||||
|
|
||||||
|
return colDefs |
||||||
|
}) |
||||||
|
|
||||||
|
const gridApi = shallowRef() |
||||||
|
|
||||||
|
const onGridReady = (params: any) => { |
||||||
|
gridApi.value = params.api |
||||||
|
} |
||||||
|
|
||||||
|
const open = async (projectName: string, fileName: string) => { |
||||||
|
visible.value = true |
||||||
|
loading.value = true |
||||||
|
pointList.value = [] |
||||||
|
searchKeyword.value = '' |
||||||
|
|
||||||
|
try { |
||||||
|
const res = await getPointFileList({ projectName, fileName }) |
||||||
|
if (res.code === 0 && Array.isArray(res.data?.point)) { |
||||||
|
pointList.value = res.data.point |
||||||
|
} else { |
||||||
|
ElMessage.error(res.msg || '获取点位详情失败') |
||||||
|
} |
||||||
|
} catch (error) { |
||||||
|
ElMessage.error('获取点位详情失败') |
||||||
|
} finally { |
||||||
|
loading.value = false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const handleClose = (done: () => void) => { |
||||||
|
done() |
||||||
|
} |
||||||
|
|
||||||
|
const handleSearch = () => { |
||||||
|
if (gridApi.value) { |
||||||
|
gridApi.value.setGridOption('quickFilterText', searchKeyword.value) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
defineExpose({ |
||||||
|
open, |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
:deep(.el-drawer__body) { |
||||||
|
padding: 20px; |
||||||
|
height: 100%; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
</style> |
||||||
Loading…
Reference in new issue