From 2bdad3dc48b5289e57315a30b8125b221d261d23 Mon Sep 17 00:00:00 2001
From: Tevin <tingquanren@163.com>
Date: Mon, 17 Mar 2025 17:21:46 +0800
Subject: [PATCH] 知识库,设置基于 cursor rules 的文档

---
 _cursor.ai/rules/all-dev-specification.mdc |  396 +++++++++++++++++++
 _cursor.ai/rules/all-system-role.mdc       |   21 +
 /dev/null                                  |    0 
 _cursor.ai/rules/all-project-info.mdc      |  129 ++++++
 _cursor.ai/rules/fit-base-pilot.mdc        |  225 +++++++++++
 _cursor.ai/rules/fit-base-fetcher.mdc      |  312 +++++++++++++++
 _cursor.ai/rules/type-controller.mdc       |    7 
 _cursor.ai/readme.md                       |   52 ++
 8 files changed, 1,142 insertions(+), 0 deletions(-)

diff --git a/_cursor.ai/readme.md b/_cursor.ai/readme.md
new file mode 100644
index 0000000..7e9c875
--- /dev/null
+++ b/_cursor.ai/readme.md
@@ -0,0 +1,52 @@
+# 文件夹介绍
+
+## rules 文件夹
+
+rules 是指编辑器根据一定条件自动读取的对 AI 的要求
+
+> 注意:需要进行初始化  
+> 请双击执行项目根目录 `link-rules.cmd` 命令文件,将公共资源目录中的 rules 链接到编辑器中  
+> 然后在编辑器设置中查看是否链接成功(CursorSetting > Rules > ProjectRules)
+
+### 规则类型:全局使用 `Always` 
+
+所有的聊天(Agent、Ask、Edit)和 ctrl+k 编辑,都会参考此规则
+
+- [AI系统扮演角色](/src/components/_cursor.ai/rules/all-system-role.mdc)
+- [项目介绍](/src/components/_cursor.ai/rules/all-project-info.mdc)
+- [开发规范](/src/components/_cursor.ai/rules/all-dev-specification.mdc)
+
+### 规则类型:按路径匹配 `Auto-Attached`
+
+当路径规则匹配上时,会参考此规则
+
+- 请求集
+- [数据控制器](/src/components/_cursor.ai/rules/type-controller.mdc)
+- 界面
+- 子组件
+- 公共组件
+
+### 规则类型:自主决定 `Agent-Requested`
+
+根据 Description 的文字描述,由 AI 自主决定是否需要参考此规则  
+仅 Agent 模式生效,非 Agent 模式需要我们自己 @ 此规则
+
+- [请求集基类](/src/components/_cursor.ai/rules/fit-base-fetcher.mdc)
+- [数据控制器基类](/src/components/_cursor.ai/rules/fit-base-pilot.mdc)
+- 表单验证规则
+
+## prompts 文件夹
+
+prompts 是我们主动提问,和我们在聊天窗口叫 ai 干活一样  
+区别是对于经常执行的有具体要求的操作,固定下来方便反复使用
+
+## 文档文件夹
+
+公共组件库组件对应的文档
+
+| 文档目录      | 对应的组件目录          |
+| ------------- | ----------------------- |
+| `common.doc`  | src/components/common/  |
+| `forms.doc`   | src/components/forms/   |
+| `layout.doc`  | src/components/layout/  |
+| `plugins.doc` | src/components/plugins/ |
\ No newline at end of file
diff --git a/_cursor.ai/rules/all-dev-specification.mdc b/_cursor.ai/rules/all-dev-specification.mdc
new file mode 100644
index 0000000..d9bd17b
--- /dev/null
+++ b/_cursor.ai/rules/all-dev-specification.mdc
@@ -0,0 +1,396 @@
+---
+description: 
+globs: 
+alwaysApply: true
+---
+
+# 开发规范
+
+## 开发规范宗旨
+
+**代码首先是给人读的,其次才是给机器运行**,所以让代码容易阅读是你应尽的责任  
+包括但不限于以下内容:
+- 变量、函数命名需具有实义
+- 避免使用难懂的语法或设计,用最简单的方式解决问题
+- 避免单块代码过长,拆分为多个更简短的函数或方法
+- 避免代码重复,通过封装函数、类或模块进行复用,封装遵守单一职责原则
+- 写注释应解释 ‌"为什么"‌ 而不仅仅是 ‌"做什么"‌
+
+### 命名需要具有实义
+
+命名的原则是名称中有具体实体含义的词汇  
+
+比如没有实义的
+
+```js
+const onClick = () => null; // 不好的命名
+```
+
+因为 `click` 可以发生在任何元素上,从这个名称上无法分辨业务种类,所以我们认为这个函数名没有实义,需要改进一下:
+
+```js
+const onProductSelected = () => null; // 良好的命名 
+```
+
+改进后,从函数名一看便知,这是商品被选中了以后的需要进行的操作,又因为具备了在同一个页面的唯一性,能帮我们快速获取信息,知道这是哪个业务的哪个环节,大幅提高阅读效率,这就是实义
+
+## 常见命名要求
+
+### JS 变量和属性的命名
+
+使用小驼峰,注意不能以动词开头
+
+```js
+// 变量
+const name = '';
+const orderList = '';
+// 属性
+const user = {
+    name: '',
+    lastOrder: '',
+};
+```
+
+#### 变量命名的特别约定
+
+**变量名允许 is 开头**  
+变量名不能用动词开头,但是允许 is 作为特例
+
+```js
+// 允许 is 开头的变量名
+let isOpened = false;
+// 更好的命名
+let selectorOpened = false;
+```
+
+**事件变量用 evt**  
+事件 event 的简写,必须是 evt(不能用单字母 e )
+
+```html
+<view @tap="evt => onOrderClick(evt)"></view>
+<CMenu :onClick="evt => onMenuItemClick(evt)" />
+```
+
+**报错信息用 err**  
+报错 error 的简写,必须是 err(不能用单字母 e )
+
+```js
+Taro.request({
+    fail: err => {},
+});
+```
+
+**后端数据 res**  
+后端返回数据,优先采用 resource 的简写 res
+
+```js
+$fetch.getEquipments().then(res => {});
+```
+
+### JS 方法的命名
+
+#### 在数据控制层
+
+**提供给界面层使用的方法,用 on 开头**
+
+```js
+// 数据控制层定义
+onOpenOrderDetail() {}
+```
+
+```html
+// 界面层使用
+<view class="order-item" @tap="evt => onOpenOrderDetail()">
+```
+
+**数据层自用的方法,用下划线开头**
+
+```js
+// 数据控制层定义
+_checkOrderDetailOpened() {}
+
+onOpenOrderDetail() {
+    // 数据控制层内部自用
+    this._checkOrderDetailOpened();
+}
+```
+
+#### 在组件内
+
+**接收父级方法的时候,用 on 开头**
+
+```js
+export default {
+    props: {
+        // 选择会员后的回调
+        onUserSelected: Function,
+    },
+}
+```
+
+**组件自身模版绑定的时候,用 handle 开头**
+
+```js
+export default {
+    methods: {
+        // 组件层定义
+        handleUserSelected() {}
+    }
+}
+```
+
+```html
+// 组件层自身模板绑定
+<view class="user-item" @tap="evt => handleUserSelected()">
+```
+
+**函数相互调用的时候,用下划线开头**
+
+```js
+export default {
+    methods: {
+        // 组件层定义
+        _checkUser() {},
+        handleUserSelected() {
+            // 组件层内部自用
+            this._checkUser()
+        }
+    }
+}
+```
+
+**给父级ref的引用进行调用的方法,使用 $ 开头**
+
+```js
+export default {
+    methods: {
+        // 组件层定义
+        $close() {}
+    }
+}
+```
+
+父级
+
+```html
+<CUserDetail ref="userDetail">
+```
+
+```js
+onCloseAll() {
+    // 父级 ref 调用
+    this.$refs.userDetail.$close()
+}
+```
+
+### 样式命名
+
+样式名的命名,采用横杠连接
+
+```css
+.page-name {}
+```
+
+#### 界面样式
+
+任何一个界面必定包含众多样式,所以:
+
+* 最父级样式名固定,与界面名称一直
+* 中间层,需要以父级为前缀,接板块说明
+* 末端则可单名,且尽量不含实义
+
+```css
+/** 在界面 userList.vue 中,最父级样式名名称固定 **/
+.user-list {
+
+    /** 中间多层,以父级为前缀 **/
+    .user-list-title {
+
+        /** 末端可单名,尽量非实义 **/
+        .left,
+        .text,
+        .icon,
+        .sub,
+        .close {}
+    }
+}
+```
+
+#### 组件样式
+
+组件样式名必须已 `c-` 开头,最父级样式名必须与组件名挂钩
+
+例如,组件 CMenu.vue 中,最父级样式名必须是:
+
+```css
+.c-menu {}
+```
+
+
+### 文件命名
+
+常用文件命名约定
+
+#### 界面层文件
+
+界面层文件(.vue、.scss),采用小驼峰命名
+
+```
+index.vue
+index.scss
+```
+
+#### 数据控制层文件
+
+数据控制器文件(.js),以固定前缀 P 开头,再接大驼峰
+
+```
+PIndex.js
+```
+
+数据层代码定义的名称,需要和文件名一致
+
+```js
+export class PIndex extends Pilot {}
+```
+
+#### 组件文件
+
+不论是公共组件还是页面子组件(.vue、.scss),都以固定前缀 C 开头,再接大驼峰
+
+```
+CMenu.vue
+CMenu.scss
+```
+
+组件代码定义的名称,需要和文件名一致
+
+```js
+// CMenu.vue 的组件名,必须是 CMenu
+export default {
+    name: 'CMenu',
+}
+```
+
+#### 请求文件
+
+请求层文件(.js),以固定前缀 F 开头,再接大驼峰
+
+```
+FCommon.js
+```
+
+请求集合的代码定义名称,需要和文件名一致
+
+```js
+// FCommon.js 的类名称,必须是 FCommon
+class FCommon extends Fetcher {}
+
+// FCommon.js 代表的类的实例名称,则是 $fetchCommon
+export const $fetchCommon = new FCommon();
+```
+
+## 常见书写要求
+
+### 基础书写要求
+
+书写规则都由编辑器自动格式化即可,例如
+- 使用四个空格代替 tab
+- 单行代码宽度最多100个字符
+- js 代码使用单引号
+
+更多规则请参考根目录 `.prettierrc` 文件
+
+### JS 中的书写
+
+#### 变量定义符
+
+弃用 `var` ,改用 `const` 与 `let` ,且优先使用 `const` ,除非要改值
+
+```js
+const name = 'Tevin';
+```
+
+```js
+let count = 0;
+count++;
+```
+
+注意,const 定义的对象,其属性是可以改值的
+
+```js
+const search = {
+    count: 1
+};
+search.count++; // 允许运行
+search = {}; // 报错
+```
+
+所以:
+* 对于普通数据,如果明确要变更变量值,才使用 `let`
+* 对于引用数据,如果明确需要重新赋值一个引用对象,才使用 `let`
+
+#### 变量判断
+
+为了避免弱类型带来的bug追踪困难,变量判断需要使用恒等号,必须判断类型一致
+
+```js
+// 需要同时判断值和类型
+if (order.state === 1) {}
+```
+
+#### 模版中的函数调用
+
+- 模版中调用函数,为了便于阅读,必须显式的书写 evt 变量和箭头函数,即:  `evt => fncName()`,**这是规范要求,不是代码冗余**
+- 把 Taro 自身组件视为普通元素,元素绑定事件使用 `@tap` 的方式
+- 非元素皆是组件,组件使用 `:onClick` 的方式绑定
+
+例如:
+```html
+<!-- 元素绑定事件使用 -->
+<view @tap="() => onOpenOrderDetail()"></view>
+<!-- 组件绑定事件使用 -->
+<CMenu :onItemClick="evt => onMenuItemClick(evt)" />
+```
+
+#### 以 JSDoc 格式写方法注释
+
+公共代码中的方法都需要以 JSDoc 的格式写注释
+
+```js
+export class Tools {
+    /**
+     * 显示消息提示框
+     * @param {string} msg - 提示的消息内容
+     * @param {number} [duration=2000] - 提示框显示时长(毫秒)
+     * @param {boolean} [mask=false] - 是否显示透明蒙层,防止触摸穿透
+     */
+    static toast(msg, duration = 2000, mask = false) {
+    }
+}
+```
+
+### CSS 属性顺序
+
+为了避免 css 属性写重复,请按如下顺序书写:
+
+* **文档流属性**  
+    display, position, float, clear, visibility, table-layout 等
+
+* **大小属性**  
+    width, height, margin, padding 等
+
+* **文字排版属性**  
+    font, line-height, text-align, text-indent, vertical-align 等
+
+* **装饰性属性**  
+    color, background, border, opacity, shadow, cursor 等
+
+* **生成内容属性**  
+    content, list-style, quotes 等
+
+* **二次渲染属性**  
+    zoom, transform 等
+
+* **动画属性**  
+    transition, animation 等
diff --git a/_cursor.ai/rules/all-project-info.mdc b/_cursor.ai/rules/all-project-info.mdc
new file mode 100644
index 0000000..40e0023
--- /dev/null
+++ b/_cursor.ai/rules/all-project-info.mdc
@@ -0,0 +1,129 @@
+---
+description: 
+globs: 
+alwaysApply: true
+---
+
+# 项目介绍
+
+## 工程介绍
+
+这是一套Web前端开发的工程文档,用于指导移动端(H5网页、混合App、小程序等)的开发
+
+
+## 技术栈
+
+- 语法框架:Vue(v2.5.0)
+- 工程框架:Taro(v3.2.13)
+- 显示框架:Taro-UI-Vue(v1.0.0-beta.10)
+- 样式:Sass
+
+说明:使用时,优先使用公共组件库的组件,其次是 Taro-UI-Vue 的组件,最后才是 Taro 本身的基础元素组件
+
+## 工程目录结构
+
+工程主要目录及其用途
+
+- root/(根目录)
+    - public/(静态资源目录)
+    - src/(开发源码目录)
+        - components/(公共资源目录)
+            - bases/(公共基类目录)
+            - common/(公共工具目录)
+            - forms/(公共表单组件目录)
+            - layout/(公共排版组件目录)
+            - plugins/(公共复杂组件目录)
+        - fetchers/(请求层目录)
+            - FName.js(请求集)
+        - pages/(界面层目录)
+            - pageGroup/(界面层分组目录)
+                - pageName/(界面层单页目录)
+                    - cmpt/(界面子组件目录)
+                        - CName.vue(子组件)
+                        - cName.scss(子组件样式)
+                    - page.vue(界面)
+                    - page.scss(界面样式)
+        - pilots/(数据控制层目录)
+            - _overall/(全局数据控制目录)
+            - pilotGroup/(数据控制层分组目录)
+                - mixin/(混合件目录)
+                    - MName.js(混合件)
+                - PName.js(数据控制器)
+
+### 目录解释
+
+- **公共资源目录**:移动端所有项目公用,使用 git 子模块跨项目同步
+- **界面层**:包含界面和子组件,界面子组件是对整个逻辑流程的单个节点的封装(TODO:界面拆分原理)
+- **数据控制层**:包含数据控制器和混合件,混合件是数据控制器代码片段共享的一种设计(非vue混入)
+- **请求层**:请求层中的单个文件是请求集,一个请求集存放某个类型业务的所有请求的集合
+
+### 短路径映射
+
+工程资源引用时,通常使用更短的引用路径:
+
+* `@components` 代表 `root/src/components`
+* `@fetchers` 代表 `root/src/fetchers`
+* `@pages` 代表 `root/src/pages`
+* `@pilots` 代表 `root/src/pilots`
+
+例如:'@components/layout/h5Page' 实际引用的是 'root/src/components/layout/h5Page'
+
+
+## 页面构成
+
+### 页面定义
+
+当我们说页面的时候,不是普通的 vue 组件,而是由**界面层**和**数据控制层**两部分共同组成的代码实体,逻辑结构如下
+
+- 页面:
+    - 界面层(root/src/pages/pageGroup/pageName/page.vue)
+        - 界面子组件(root/src/pages/pageGroup/pageName/cmpt/CName.vue)
+    - 数据控制层(root/src/pilots/pilotsGroup/PName.js)
+        - 请求层(root/src/fetchers/FName.js)
+
+(当我们说组件、子组件、公共组件的时候,才是指普通 Vue 组件)
+
+### 界面层和数据控制层的拆分
+
+一个业务页面的界面层和数据控制层,实际上是对一个 Vue 组件的拆分,最终运行的时候,还是会合二为一,还原成原本的 Vue 实例  
+因此,数据控制层其实是对这个 Vue 实例部分功能的一种转写,类似于语法糖
+
+**数据控制层写法**
+
+例如:
+
+```js
+// 数据控制器
+export class PIndex extends Pilot {
+    $data() {
+        return {
+            a: 1,
+        };
+    }
+    $computed = {
+        a2() {},
+    };
+    $mounted() {}
+    onOpenSelector() {}
+}
+```
+
+会转换成
+
+```js
+// 标准 Vue
+const Component = Vue.extend({
+    data() {
+        return {
+            a: 1,
+        };
+    },
+    computed: {
+        a2() {},
+    },
+    methods: {
+        onOpenSelector() {},
+    },
+    mounted() {},
+});
+```
\ No newline at end of file
diff --git a/_cursor.ai/rules/all-system-role.mdc b/_cursor.ai/rules/all-system-role.mdc
new file mode 100644
index 0000000..d0410d7
--- /dev/null
+++ b/_cursor.ai/rules/all-system-role.mdc
@@ -0,0 +1,21 @@
+---
+description: 
+globs: 
+alwaysApply: true
+---
+# 角色
+
+## 你是一名拥有10年经验的资深Web前端开发工程师
+- 对代码质量有严格的要求,代码语义清晰、简洁高效
+- 拥有丰富的开发实践,擅长编写易于使用和维护的组件,能够设计干净健壮的架构
+- 懂得遵守最小改动原则,尽量不破坏现有功能
+- 擅长编用 markdown 写技术文档,层次分明、齐全易懂
+
+## 你是一个重视需求分析的开发者
+- 善于从用户角度理解业务需求
+- 会主动发现需求中存在缺陷,并与用户讨论完善
+- 倾向于选择最简单的解决方案,避免过度设计
+
+## 你是一个熟悉液化气行业的专业人士
+- 了解液化气充装、配送、销售等业务流程
+- 了解会员、订单、气瓶、电子秤等资产管理相关内容
\ No newline at end of file
diff --git a/_cursor.ai/rules/fit-base-fetcher.mdc b/_cursor.ai/rules/fit-base-fetcher.mdc
new file mode 100644
index 0000000..766f648
--- /dev/null
+++ b/_cursor.ai/rules/fit-base-fetcher.mdc
@@ -0,0 +1,312 @@
+---
+description: 数据控制器基类,所有数据控制器都必须继承此类
+globs: 
+alwaysApply: false
+---
+
+# 请求层基类 Fetcher.js
+
+## 功能说明
+
+类 Fetcher 是一个用于处理 HTTP 请求的基类,包含了URL拼接、响应数据适配、报错提示等多种方法来简化和统一请求的处理  
+请求层的每个请求集,都必须继承此类
+
+## 引用方法
+
+```js
+import {
+    Fetcher
+} from '@components/bases/Fetcher';
+```
+
+## 构造函数
+
+构造函数接受一个配置对象作为参数,包含如下属性:
+
+- **urlPrefix** 数组,包含两项,第一项为本地 Mock 的 URL 前缀,第二项为服务器接口的前缀
+
+```js
+class FCommon extends Fetcher {
+
+    constructor() {
+        // 构造函数使用示例
+        super({
+            urlPrefix: ['/api/order/', '/mini/order/'],
+        });
+    }
+}
+```
+
+## 主要方法
+
+
+### `spellURL(devSuffix, serSuffix)`
+
+**功能**  
+根据开发环境和生产环境拼接 URL 地址
+
+**参数**
+- `devSuffix` (String):开发环境下的 URL 后缀
+- `serSuffix` (String, 可选):生产环境下的 URL 后缀。如果未提供,则使用 `devSuffix`
+
+**返回值**
+- (String):处理后的完整 URL 地址
+
+**示例**
+```javascript
+class FCommon extends Fetcher {
+
+    getUserInfo(id) {
+        const url = this.spellURL('getUserInfo', 'user/user_info');
+    }
+}
+```
+
+**注意事项**
+- 如果启用了 Mock 模式(比如本地开发时),URL 会指向本地的 JSON 文件
+- 自动处理路径中的 `../` 和多余的 `/`,确保路径正确
+
+---
+
+### `get(url, data, options)`
+
+**功能**  
+发起 GET 请求
+
+**参数**  
+- `url` (String):请求的 URL 地址
+- `data` (Object):请求参数
+- `options` (Object,可选):请求配置
+
+**返回值**  
+- (Promise):返回请求结果的 Promise
+
+---
+
+### `post(url, data, options)`
+
+**功能**  
+发起 POST 请求,query 方法的简写
+
+**参数**  
+- `url` (String):请求的 URL 地址
+- `data` (Object):请求参数
+- `options` (Object,可选):请求配置
+
+**返回值**  
+- (Promise):返回请求结果的 Promise
+
+**示例**
+```js
+class FCommon extends Fetcher {
+
+    getUserInfo() {
+        const url = this.spellURL('getUserInfo', 'user/user_info');
+        const send = {};
+        return this.post(url, send);
+    }
+}
+```
+
+---
+
+### `query(type, url, data, options)`
+
+**功能**  
+发起基础 AJAX 请求
+
+**参数**  
+- `type` (String):请求类型(如 `'get'`、`'post'`)
+- `url` (String):请求的 URL 地址
+- `data` (Object,可选):请求参数
+- `options` (Object,可选):请求配置(参照下文)
+
+**返回值**  
+- (Promise):返回请求结果的 Promise
+
+**注意事项**  
+- 小程序环境下会自动处理 Cookie,无需手动设置
+
+---
+
+### `stringToCamel(str)`
+
+**功能**  
+将下划线命名的字符串转换为小驼峰命名
+
+**参数**  
+- `str` (String):需要转换的字符串  
+
+**返回值**  
+- (String):转换后的小驼峰字符串
+
+---
+
+### `stringToUnderline(str)`
+**功能**  
+将小驼峰命名的字符串转换为下划线命名,如果没有大写字母,则返回原字符串
+
+**参数**  
+- `str` (String):需要转换的字符串  
+
+**返回值**  
+- (String):转换后的下划线字符串
+
+---
+
+### `transKeyName(type, json)`
+**功能**  
+将 JSON 对象的键名在驼峰与下划线命名模式之间转换
+
+**参数**  
+- `type` (String):转换的目标类型,有两个值:
+  - `'camel'`:下划线转小驼峰
+  - `'underline'`:小驼峰转下划线
+- `json` (Object):需要转换的 JSON 对象
+
+**返回值**  
+- (Object):转换后的 JSON 对象
+
+**示例**  
+```js
+class FCommon extends Fetcher {
+
+    saveUserInfo() {
+        const json = { 'user_name': 'John' };
+        const result = this.transKeyName('camel', json); // 返回 { userName: 'John' }
+    }
+}
+```
+
+**注意事项**  
+- 支持嵌套对象的递归转换
+
+## 请求 query 的 options 配置
+
+### `hostType` 主机类型
+
+在混合 App 中,指定本次请求的主机类型(即域名),有两个值:
+
+- `base`:基础主机(预登陆用)
+- `main`:默认主机,默认值,正常业务主机
+  - 优先由 java 层在 webview 的 URL 上指定
+  - 未指定时,按文件 project.config.json 的 host.buildHost 项值指定
+
+**示例**  
+```js
+class FCommon extends Fetcher {
+
+    loginPrepare() {
+        const url = this.spellURL('loginPrepare', 'User/preLogin');
+        const send = {};
+        return this.post(url, send, { hostType: 'base' });
+    }
+}
+```
+
+### `silence` 静音请求
+设为 true 时,如果请求失败,不显示提示
+
+**示例**  
+```js
+class FCommon extends Fetcher {
+
+    getUserInfo() {
+        const url = this.spellURL('getUserInfo', 'User/info');
+        const send = {};
+        return this.post(url, send, { silence: true });
+    }
+}
+```
+
+
+## 响应处理
+
+### 处理流程
+
+1. query 方法调用 Taro.request 发送请求
+2. 收到响应后,先检测响应数据格式,发现其他类型的响应格式时,会自动转换为:**前端统一响应数据格式**
+3. 根据响应格式
+    - 如果成功,将响应内容,回传给数据控制层
+    - 如果失败,直接失败提示,回传空内容给数据控制层
+4. 将响应内容传给数据控制层之前,会进行如下处理
+    - 将数据中的键名,转换为的驼峰
+    - 将数据中的内容,如果是常规数字的字符串,转为数值
+
+### 前端统一响应数据格式
+
+格式如下:
+
+```js
+{
+    state: { // 状态
+        code, // 状态码
+        msg // 状态消息
+    }, 
+    data: { // 实际数据,必须是 Object
+    }
+}
+```
+
+例如:
+
+```json
+{
+    "state": {
+        "code": 2000,
+        "msg": "OK"
+    },
+    "data": {
+        "id": 1,
+        "name": "Tevin"
+    }
+}
+```
+
+注意,为了保证接口数据的扩展性,data 只能接 Object 类型,禁止接其他类型
+
+### 前端统一响应状态码
+
+- `2000`:通用请求成功  
+- `2001`:请求成功,但是没有数据,弹窗提示 msg(仅特殊情况使用)  
+- `5000`:通用请求失败,弹窗提示 msg  
+- `9001`:登录已过期,返回登录页  
+- `9002`:已登录但没有操作权限,弹窗提示 msg
+
+## 请求调用
+
+请求层的每一个请求集,都会实例化自身作为全局单例,使用时,只需引用已经实例化好的全局单例,直接发请求即可
+
+```js
+import { Fetcher } from '@components/bases/Fetcher';
+
+class FCommon extends Fetcher {}
+
+// 全局单例
+export const $fetchCommon = new FCommon();
+```
+
+```js
+import {
+    $fetchCommon
+} from '@fetchers/FCommon';
+
+export class PPageName extends Pilot {
+
+    // 在数据控制层中,发请求示例
+    onLoadUserInfo() {
+        Taro.showLoading();
+        $fetchCommon.getUserInfo(this.userId)
+            .then(res => {
+                Taro.hideLoading();
+                if (!res) {
+                    return;
+                }
+                this.userInfo = res || {};
+            });
+    }
+
+}
+```
+
+
diff --git a/_cursor.ai/rules/fit-base-pilot.mdc b/_cursor.ai/rules/fit-base-pilot.mdc
new file mode 100644
index 0000000..0f753aa
--- /dev/null
+++ b/_cursor.ai/rules/fit-base-pilot.mdc
@@ -0,0 +1,225 @@
+---
+description: 
+globs: 
+alwaysApply: false
+---
+
+# 数据控制层基类 Pilot.js
+
+## 功能说明
+
+Pilot 是数据控制层的基类
+- 将 Vue 对象的数据绑定、方法绑定、生命周期等功能拆分出来,进行独立申明(拆分后界面层管显示,数据控制层管数据流转) 
+- 增加跨页通讯、键名转换等相关的能力
+- 每个数据控制器都需要继承此类
+- 子类实例化后,再将申明的内容合并回去
+
+## 引用方法
+
+```js
+import {
+    Pilot
+} from '@components/bases/Pilot';
+```
+
+## 主要方法
+
+
+### `createOptions(dataAdd)`
+**功能**  
+创建页面合并对象,将实例的属性和方法映射到页面合并对象中
+
+**参数**  
+- `dataAdd` (Object):需要添加到页面中的额外数据  
+
+**返回值**  
+- (Object):页面合并对象
+
+
+**注意事项**  
+- 如果 `dataAdd` 中包含 `assets` 属性,会自动调用 transAssets 转换其中的图片地址值
+- `$` 开头的属性会被直接映射到合并对象中,同时去掉 `$` 前缀
+- 非 `$` 开头的属性会被映射到合并对象的 `methods` 属性中
+
+---
+
+### `transAssets(assets)`
+**功能**  
+转换静态图片引用的路径,根据运行环境(H5 或小程序)生成图片实际发布的路径
+
+**参数**  
+- `assets` (Object):包含图片路径的对象  
+
+**返回值**  
+- (Object):转换后的图片路径对象
+
+#### 静态图片地址转换说明
+
+在目前项目体系设计下,由于不同项目(公众号、小程序、混合app)图片加载机制的不同,设置了一个统一的图片地址转换入口  
+经过静态方法 transAssets 转换,可以获得图片实际发布的地址
+
+**示例1**  
+
+```javascript
+const assets = { logo: 'assets/images/logo.png' };
+const result = Pilot.transAssets(assets); // 输出转换后的图片实际发布地址
+```
+
+**示例2**  
+
+```HTML
+<template>
+    <image :src="assets.homeNav1" mode="aspectFit" />
+</template>
+```
+
+```js
+export default {
+    name: 'index',
+    ...new PIndex().createOptions({
+        // 首页菜单图片,会全部经过 transAssets 进行路径转换,转换为图片实际发布的地址
+        assets: {
+            homeNav1: 'assets/img/home-nav-01.png',
+            homeNav2: 'assets/img/home-nav-02.png',
+            homeNav3: 'assets/img/home-nav-03.png',
+        },
+    }),
+};
+```
+
+**注意事项**  
+移动端的图片不要使用 css 背景,而是使用 image 图片元素,再填充由 transAssets 生成的实际发布地址
+
+## 页面合并流程
+
+`createOptions` 是类 Pilot 的核心方法,此方法将独立申明的信息转换为合并对象,以方便原 Vue 对象进行合并
+
+### 第一步:生成合并对象
+
+* 扫描当前类,以 `$` 开头的属性或方法,去掉 `$` 符后,直接作为 Vue 的属性或方法准备合并
+* 扫描当前类,非以 `$` 开头的方法,作为 Vue 对象申明中 methods 的子级方法准备合并
+* 生成合并对象,用以进行合并
+
+```js
+// 数据控制器中,用 $ 开头的视为 Vue 属性或方法
+export class PIndex extends Pilot {
+    $data() {
+        return {
+            a: 1,
+        };
+    }
+    $computed = {
+        a2() {},
+    };
+    $mounted() {}
+}
+
+// 合并后的实际效果
+const Component = Vue.extend({
+    data() {
+        return {
+            a: 1,
+        };
+    },
+    computed: {
+        a2() {},
+    },
+    mounted() {},
+});
+```
+
+```js
+// 数据控制器中,非 $ 开头的,转到 methods 中
+export class PIndex extends Pilot {
+    onOpenSelector() {}
+}
+
+// 合并后的实际效果
+const Component = Vue.extend({
+    methods: {
+        onOpenSelector() {},
+    },
+});
+```
+
+注意:不能在数据控制器中定义 $name、$components、$methods 这三项,因为实际合并时会覆盖
+
+### 第二步:实际合并
+
+数据控制层和界面层实际的合并,其实是写在界面层上  
+由界面层实例化数据控制层,并使用其基类的 createOptions 方法生成选项,再合并进原 Vue 对象
+
+```js
+// 引入界面层对应数据控制器
+import {
+    PPageName
+} from '@pilots/pilotGroup/PPageName';
+
+export default {
+    name: 'PageName',
+    components: {},
+    // 示例化数据控制器,由实例生成 Vue 选项,再展开合并到界面 Vue 对象上
+    ...new PPageName().createOptions(),
+};
+```
+## 页面能力扩展
+
+### 跨页面通讯
+
+Taro 的跨页面通讯能力,仅小程序可用,非小城不能跨页通讯非常不方便  
+因此,使用 createOptions 创建合并对象时,给页面的 Vue 实例,增加了跨页面通讯的方法,兼容所有平台
+
+**跨页发送:$poster**
+
+此方法挂载到 vue 实例的 this 上
+
+```js
+this.$poster(direction, action, data);
+```
+
+参数:  
+- `direction` (String,必填):发送去向,有两值:
+  - `'nextPage'`:发生给下一页(如果正在创建,会等待创建成功后再传递)
+  - `'prevPage'`:发送给上一页
+- `action` (String,必填):动作名称  
+- `data` (any,可选):携带数据
+
+例如:
+
+```js
+export class PList extends Pilot {
+    onSaveSetting() {
+        // 设置保存后,通知上页
+        this.$poster('prevPage', 'settingChanged');
+    }
+}
+```
+
+**跨页接收:$onMessage**
+
+此方法的用法,类似生命周期的钩子
+
+```js
+$onMessage(action, data) {}
+```
+
+例如:
+
+```js
+export class PIndex extends Pilot {
+
+    $onMessage(action, data) {
+        // 接收消息,设置已变更
+        if (action === 'settingChanged') {
+            // do something
+        }
+    }
+}
+```
+
+### 跨端通讯
+
+当我们进混合 APP 开发时,我们需要与 java 层进行通讯,使用 createOptions 创建合并对象时,增加了跨通讯的能力
+
+(TODO:更多跨端通讯明细待续)
+
diff --git a/_cursor.ai/rules/rules01.technologyStack.md b/_cursor.ai/rules/rules01.technologyStack.md
deleted file mode 100644
index e69de29..0000000
--- a/_cursor.ai/rules/rules01.technologyStack.md
+++ /dev/null
diff --git a/_cursor.ai/rules/type-controller.mdc b/_cursor.ai/rules/type-controller.mdc
new file mode 100644
index 0000000..a1f356c
--- /dev/null
+++ b/_cursor.ai/rules/type-controller.mdc
@@ -0,0 +1,7 @@
+---
+description: 
+globs: src/pilots/**/P*.js
+alwaysApply: false
+---
+
+# 数据控制器

--
Gitblit v1.9.1