Cursor rule
.cursor/rules/js.mdcCodeSpirit JavaScript 开发规范 - AMIS集成、模块模式、API请求、Token管理
Cursor rules
Quality
46/100
Scores the file, not the repository.Length
1,243 words
29 headings · 20 code blocksRepository
56
— · pushed 134 days agoLast changed
3 days ago
First indexed 3 days ago.1234567# JavaScript 开发规范89## 模块模式1011### IIFE 包装1213所有 JS 文件使用立即调用函数表达式(IIFE)包装,启用严格模式:1415```javascript16/**17 * 模块说明18 * @module ModuleName19 */20(function() {21 'use strict';2223 // 模块代码2425 // 导出到全局26 window.ModuleName = {27 // 公共 API28 };29})();30```3132### 命名空间导出3334全局对象使用 `window` 命名空间导出:3536```javascript37// ✅ 正确:使用 window 命名空间38window.TokenManager = (function() {39 'use strict';4041 function getToken() { /* ... */ }4243 return {44 getToken,45 setToken,46 clearToken47 };48})();4950// ✅ 正确:使用 CodeSpirit 命名空间51window.CodeSpirit = window.CodeSpirit || {};52window.CodeSpirit.i18n = {53 t: function(key, params) { /* ... */ }54};5556// ✅ 正确:ES6 类导出57class NotificationClient {58 constructor(hubUrl = '/notification-hub') {59 this.hubUrl = hubUrl;60 }61}62window.NotificationClient = NotificationClient;63```6465## 文档注释规范6667### 文件头注释6869```javascript70/**71 * 考试系统API请求管理器72 * 负责处理API地址转换和统一的请求处理73 * @module ExamApiManager74 * @version 2.0.075 * @author CodeSpirit Team76 */77```7879### 函数注释(JSDoc)8081```javascript82/**83 * 设置认证token84 * @param {string} token - 访问token85 * @param {number} [expiryInHours=24] - 过期时间(小时)86 * @returns {void}87 * @throws {Error} 当token为空时抛出错误88 */89function setToken(token, expiryInHours = 24) {90 if (!token || typeof token !== 'string') {91 throw new Error('Token must be a non-empty string');92 }93 // ...94}9596/**97 * 统一的API请求函数98 * @param {string} url - API路径99 * @param {Object} [options={}] - fetch选项100 * @returns {Promise<Object>} API响应数据101 * @example102 * const data = await ExamApiManager.request('/exam/api/questions', { method: 'GET' });103 */104async function request(url, options = {}) {105 // ...106}107```108109## AMIS 框架集成110111### 主题配置112113项目使用 **antd** 主题 [[memory:8912919]]:114115```javascript116// 初始化 AMIS117let amisScoped = amis.embed('#root', amisJSON, {118 location: history.location,119 data: {},120 context: {121 WEB_HOST: webHost122 }123}, {124 theme: 'antd' // 必须使用 antd 主题125});126```127128### 事件系统129130使用 `onEvent` 配置事件监听:131132```javascript133{134 type: 'form',135 api: '/identity/api/identity/auth/login',136 onEvent: {137 // 表单提交成功事件138 submitSucc: {139 actions: [140 {141 actionType: 'custom',142 script: `143 const token = event.data.token;144 TokenManager.setToken(token, 24);145 window.location.href = '/';146 `147 }148 ]149 },150 // 数据初始化完成事件151 fetchInited: {152 actions: [153 {154 actionType: 'custom',155 script: 'window.fetchUnreadNotificationCount();'156 }157 ]158 }159 }160}161```162163### 行为类型164165优先使用 AMIS 内置行为类型(actionType):166167```javascript168// ✅ 核心行为169{ actionType: 'ajax', api: 'POST:/api/submit' }170{ actionType: 'link', link: '/dashboard' }171{ actionType: 'dialog', dialog: { /* ... */ } }172{ actionType: 'reload', target: 'crud' }173{ actionType: 'copy', content: '${text}' }174175// ✅ 表单行为176{ actionType: 'submit' }177{ actionType: 'reset' }178{ actionType: 'clear' }179180// ✅ 自定义脚本(仅在必要时使用)181{182 actionType: 'custom',183 script: `184 const tenantId = event.data.tenantId;185 window.location.href = '/' + tenantId + '/login';186 `187}188```189190### 请求适配器191192使用 `requestAdaptor` 和 `adaptor` 处理请求和响应:193194```javascript195api: {196 method: 'post',197 url: '/identity/api/identity/auth/login',198199 // 请求适配器 - 添加认证头200 requestAdaptor: function(api) {201 const token = TokenManager.getToken();202 api.headers = api.headers || {};203 api.headers['Authorization'] = token ? 'Bearer ' + token : '';204 api.headers['X-Forwarded-With'] = 'CodeSpirit';205 api.headers['X-Tenant-Id'] = window.tenantId || 'system';206 return api;207 },208209 // 响应适配器 - 处理响应数据210 adaptor: function(payload, response, api) {211 if (response.status === 401) {212 window.location.href = '/login';213 return { msg: '登录过期!' };214 }215216 if (payload.status === 0 && payload.data) {217 TokenManager.setToken(payload.data.token, 24);218 }219220 return payload;221 }222}223```224225## Token 管理226227### TokenManager 使用228229使用 `TokenManager` 统一管理认证状态:230231```javascript232// 初始化模式233TokenManager.initSystemMode(); // 系统平台234TokenManager.initTenantMode('tenant-id'); // 租户平台235TokenManager.initClientMode('tenant-id', 'exam'); // 客户端平台236237// Token 操作238TokenManager.setToken('access-token', 24); // 设置 token(24小时过期)239const token = TokenManager.getToken(); // 获取 token240TokenManager.clearToken(); // 清除 token241TokenManager.hasToken(); // 检查是否有 token242TokenManager.isTokenExpired(); // 检查是否过期243TokenManager.isAuthenticated(); // 检查是否已认证244245// 扩展功能246TokenManager.setTokenExtended(accessToken, refreshToken, expiresIn, tenantId);247TokenManager.getRefreshToken();248TokenManager.getAuthHeaders(); // 获取认证请求头249TokenManager.setUserInfo(userInfo);250TokenManager.getUserInfo();251```252253### 认证请求头254255所有 API 请求必须携带认证头:256257```javascript258const headers = {259 'Authorization': token ? 'Bearer ' + token : '',260 'X-Forwarded-With': 'CodeSpirit',261 'X-Tenant-Id': tenantId || 'system',262 'Content-Type': 'application/json'263};264```265266## API 请求规范267268### 服务发现路径269270API 路径必须附带服务短名:271272```javascript273// ✅ 正确:附带服务名274'/identity/api/identity/profile'275'/exam/api/exam/questions'276'/messaging/api/messaging/messages/my/list'277'/survey/api/surveys/${surveyId}'278279// ❌ 错误:缺少服务名280'/api/identity/profile'281'/api/questions'282```283284### API 管理器模式285286使用统一的 API 管理器处理请求:287288```javascript289/**290 * API管理器291 */292window.ExamApiManager = {293 /**294 * 统一的API请求函数295 * @param {string} url - API路径296 * @param {Object} options - fetch选项297 * @returns {Promise} API响应数据298 */299 request: async function(url, options = {}) {300 const token = window.TokenManager?.getToken();301302 const requestConfig = {303 ...options,304 headers: {305 'Authorization': token ? 'Bearer ' + token : '',306 'X-Tenant-Id': window.tenantId,307 'X-Forwarded-With': 'CodeSpirit',308 'Content-Type': 'application/json',309 ...options.headers310 }311 };312313 const response = await fetch(url, requestConfig);314315 // 处理认证失败316 if (response.status === 401) {317 window.location.href = '/login';318 throw new Error('认证失败,请重新登录');319 }320321 if (!response.ok) {322 throw new Error(`HTTP ${response.status}: ${response.statusText}`);323 }324325 const result = await response.json();326327 if (result.status !== undefined && result.status !== 0) {328 throw new Error(result.msg || '请求失败');329 }330331 return result.data || result;332 },333334 get: function(url, options = {}) {335 return this.request(url, { ...options, method: 'GET' });336 },337338 post: function(url, data = null, options = {}) {339 return this.request(url, {340 ...options,341 method: 'POST',342 body: data ? JSON.stringify(data) : undefined343 });344 }345};346```347348## 缓存管理349350### 缓存键命名351352使用租户隔离的缓存键:353354```javascript355// 格式:{module}_cache_{tenantId}_{key}356const cacheKey = `exam_cache_${tenantId}_login_config`;357const cacheKey = `survey_cache_${tenantId}_form_data`;358```359360### 缓存工具类361362```javascript363const CacheManager = {364 /**365 * 获取缓存数据366 * @param {string} key - 缓存key367 * @param {string} tenantId - 租户ID368 * @returns {Object|null} 缓存的数据369 */370 get: function(key, tenantId) {371 try {372 const cacheKey = `exam_cache_${tenantId}_${key}`;373 const cached = sessionStorage.getItem(cacheKey);374 if (!cached) return null;375376 const data = JSON.parse(cached);377378 // 检查是否过期379 if (data.expiry && Date.now() > data.expiry) {380 sessionStorage.removeItem(cacheKey);381 return null;382 }383384 return data.value;385 } catch (error) {386 console.error('[缓存读取失败]', error);387 return null;388 }389 },390391 /**392 * 设置缓存数据393 * @param {string} key - 缓存key394 * @param {string} tenantId - 租户ID395 * @param {Object} value - 要缓存的数据396 * @param {number} ttl - 过期时间(毫秒)397 */398 set: function(key, tenantId, value, ttl = 30 * 60 * 1000) {399 try {400 const cacheKey = `exam_cache_${tenantId}_${key}`;401 const data = {402 value: value,403 expiry: ttl ? Date.now() + ttl : null404 };405 sessionStorage.setItem(cacheKey, JSON.stringify(data));406 } catch (error) {407 console.error('[缓存写入失败]', error);408 }409 }410};411```412413## 国际化414415使用 `CodeSpirit.i18n` 进行翻译:416417```javascript418// 初始化(服务器端调用)419window.CodeSpirit.i18n.init('zh-CN', {420 'Login.Title': '用户登录',421 'Login.Success': '登录成功,欢迎 {userName}!'422});423424// 获取翻译文本425const title = CodeSpirit.i18n.t('Login.Title');426const message = CodeSpirit.i18n.t('Login.Success', { userName: 'John' });427428// 切换语言429CodeSpirit.i18n.switchLanguage('en');430```431432## 类定义规范433434使用 ES6 class 语法:435436```javascript437/**438 * 通知客户端439 * 提供与通知服务的连接和消息处理功能440 */441class NotificationClient {442 /**443 * @param {string} hubUrl - SignalR Hub URL444 */445 constructor(hubUrl = '/notification-hub') {446 this.hubUrl = hubUrl;447 this.connection = null;448 this.handlers = new Map();449 }450451 /**452 * 连接到通知服务453 * @returns {Promise} 连接Promise454 */455 async connect() {456 this.connection = new signalR.HubConnectionBuilder()457 .withUrl(this.hubUrl)458 .withAutomaticReconnect()459 .build();460461 await this.connection.start();462 console.log('通知连接已建立');463 }464465 /**466 * 注册消息处理器467 * @param {string} topic - 主题名称468 * @param {string} type - 消息类型469 * @param {Function} handler - 处理函数470 */471 on(topic, type, handler) {472 const key = `${topic}:${type}`;473 if (!this.handlers.has(key)) {474 this.handlers.set(key, []);475 }476 this.handlers.get(key).push(handler);477 }478}479480window.NotificationClient = NotificationClient;481```482483## 代码质量要求484485### 函数长度限制486487一个函数不超过 30 行代码,复杂逻辑拆分为多个小函数:488489```javascript490// ✅ 正确:拆分为多个小函数491function handleLoginSuccess(payload) {492 saveToken(payload.data.token);493 redirectToTarget();494}495496function saveToken(token) {497 TokenManager.setToken(token, 24);498}499500function redirectToTarget() {501 const redirectUrl = getRedirectUrl();502 if (isValidRedirect(redirectUrl)) {503 window.location.href = redirectUrl;504 } else {505 window.location.href = '/';506 }507}508```509510### 样式分离511512样式写入独立的 CSS 文件,不在 JS 中内联样式:513514```javascript515// ✅ 正确:使用 CSS 类名516element.className = 'survey-container loading';517518// ❌ 避免:内联样式519element.style.backgroundColor = '#fff';520element.style.padding = '20px';521```522523### 响应式支持524525界面需要支持响应式布局和移动端适配:526527```javascript528// AMIS 配置中使用响应式断点529{530 type: 'grid',531 columns: [532 {533 xs: 12, // 手机:全宽534 sm: 6, // 平板:半宽535 md: 4, // 桌面:三分之一536 lg: 3, // 大屏:四分之一537 body: { /* ... */ }538 }539 ]540}541```542543## 禁止事项544545```javascript546// ❌ 禁止:自定义 DOM 事件(使用 AMIS 事件系统)547document.getElementById('btn').addEventListener('click', handler);548549// ❌ 禁止:直接操作 DOM(除非必要)550document.getElementById('content').innerHTML = html;551552// ❌ 禁止:使用 var 声明变量553var token = getToken();554555// ❌ 禁止:缺少错误处理556const response = await fetch(url);557const data = await response.json();558559// ❌ 禁止:硬编码敏感信息560const apiKey = 'sk-xxxxxxxx';561```562563## AMIS 官方文档564565- 📖 组件文档:https://aisuda.bce.baidu.com/amis/zh-CN/docs/index566- 🎨 事件系统:https://aisuda.bce.baidu.com/amis/zh-CN/components/action#%E4%BA%8B%E4%BB%B6%E8%A1%A8567- 🔘 行为按钮:https://aisuda.bce.baidu.com/amis/zh-CN/components/action568- 📝 表单组件:https://aisuda.bce.baidu.com/amis/zh-CN/components/form/index569
Also in xin-lai/CodeSpirit
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| xin-lai/CodeSpirit.cursor/rules/ai-development.mdc · 56 | Cursor rules | api | 46/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/all.mdc · 56 | Cursor rules | testing-strategyapi | 50/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/amis-cards.mdc · 56 | Cursor rules | no sections | 54/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/api-design.mdc · 56 | Cursor rules | no sections | 54/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/controller.mdc · 56 | Cursor rules | api | 54/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/cs.mdc · 56 | Cursor rules | no sections | 25/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/csproj.mdc · 56 | Cursor rules | api | 54/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/css.mdc · 56 | Cursor rules | ui | 54/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/database.mdc · 56 | Cursor rules | no sections | 74/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/dependency-injection.mdc · 56 | Cursor rules | api | 54/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/dto.mdc · 56 | Cursor rules | no sections | 50/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/enum.mdc · 56 | Cursor rules | no sections | 50/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/i18n.mdc · 56 | Cursor rules | no sections | 50/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/naming-conventions.mdc · 56 | Cursor rules | no sections | 50/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/package-management.mdc · 56 | Cursor rules | no sections | 74/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/performance.mdc · 56 | Cursor rules | no sections | 54/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/project-structure.mdc · 56 | Cursor rules | api | 54/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/security.mdc · 56 | Cursor rules | database | 54/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/service.mdc · 56 | Cursor rules | no sections | 50/100 | 3 days ago | |
| xin-lai/CodeSpirit.cursor/rules/startup-framework.mdc · 56 | Cursor rules | api | 54/100 | 3 days ago |
Diff against .cursor/rules/ai-development.mdc Diff against .cursor/rules/all.mdc Diff against .cursor/rules/amis-cards.mdc Diff against .cursor/rules/api-design.mdc Diff against .cursor/rules/controller.mdc Diff against .cursor/rules/cs.mdc Diff against .cursor/rules/csproj.mdc Diff against .cursor/rules/css.mdc Diff against .cursor/rules/database.mdc Diff against .cursor/rules/dependency-injection.mdc Diff against .cursor/rules/dto.mdc Diff against .cursor/rules/enum.mdc Diff against .cursor/rules/i18n.mdc Diff against .cursor/rules/naming-conventions.mdc Diff against .cursor/rules/package-management.mdc Diff against .cursor/rules/performance.mdc Diff against .cursor/rules/project-structure.mdc Diff against .cursor/rules/security.mdc Diff against .cursor/rules/service.mdc Diff against .cursor/rules/startup-framework.mdc
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago |
