Commit 6b4e97cd by liangjianmin

Merge branch 'master' into feature/ljm/20250825-开通芯链

# Conflicts:
#	src/views/OrderTrack/goods.vue
parents 993d8ec7 c78ef998
---
description: 签署合作协议弹窗组件开发规范
globs: **/OrderTrack/goods.vue,**/views/**/goods.vue
---
# 签署合作协议弹窗组件开发规范
## 组件功能概述
该组件实现了【签署采购合作框架协议】功能,包含文件上传、有效期选择和表单验证。
## 核心功能模块
### 1. 弹窗配置
- 弹窗宽度:680px
- 标题:签署采购合作框架协议
- 不允许点击遮罩关闭:`:close-on-click-modal="false"`
### 2. 文件上传功能
- **上传接口**:`http://file.liexindev.net/uploadFile?sys_type=2`
- **文件限制**:只允许PDF格式,无大小限制
- **返回数据格式**:
```javascript
{
"code": 0,
"data": {
"file_name": "文件名.pdf",
"oss_file_url": "文件下载地址"
}
}
```
- **显示逻辑**:上传成功后显示实际文件名,支持预览
### 3. 有效期选择
- **长期有效**:validity_type = 1
- **自定义**:validity_type = 2,必须选择日期范围
- **最低要求**:自定义有效期必须 ≥ 2年(730天)
- **快捷选择**:提供"两年"、"三年"、"五年"快捷按钮
- **日期选择器配置**:
- 支持年月快速选择:`:unlink-panels="true"`
- 日期格式:`yyyy-MM-dd`
- 支持清空:`clearable`
### 4. 表单验证逻辑
```javascript
// 提交时验证
if (!this.agreementForm.file_url) {
this.$message.error('请先上传签章协议文件');
return;
}
// 自定义有效期验证
if (this.agreementForm.validity_type === 2) {
if (!this.validityRange) {
this.$message.error('请选择有效期时间范围');
return;
}
// 验证有效期 ≥ 2年
var diffDays = (endDate - startDate) / (1000 * 60 * 60 * 24);
if (diffDays < 730) {
this.$message.error('自定义有效期必须大于等于两年');
return;
}
}
```
### 5. API集成
- **接口地址**:`POST /api/attachment/applyAttachmentAudit`
- **请求参数**:直接传递 `this.agreementForm` 对象
- **参数结构**:
```javascript
{
type: 'cooperation_agreement',
file_url: '文件地址',
file_name: '文件名称',
validity_type: 1 | 2,
validity_start: '开始日期',
validity_end: '结束日期'
}
```
## 样式规范
### 1. 布局对齐
- 表单标签宽度:`label-width="121px"`
- 有效期区域:使用 flexbox 布局,`display: flex; align-items: center`
- 日期选择器:固定宽度300px,左边距20px
### 2. 交互优化
- 按钮不使用 `disabled` 状态,通过点击时提示改善用户体验
- 文件上传成功后显示文件信息和预览链接
- 表单重置时清空所有字段
## 开发注意事项
1. **变量声明**:统一使用 `var`,避免使用 `let/const`
2. **错误处理**:友好的用户提示,避免技术性错误信息
3. **数据管理**:watch监听日期范围变化,自动同步到表单字段
4. **组件引入**:确保引入所需的Element UI组件(Radio、Upload、Link等)
## 测试要点
1. 文件上传:PDF格式验证、上传成功/失败处理
2. 日期选择:年月快速选择、快捷按钮功能
3. 表单验证:各种边界条件的错误提示
4. API调用:请求参数正确性、响应处理
\ No newline at end of file
---
description: Element UI日期选择器最佳实践
globs: **/*.vue
---
# Element UI日期选择器最佳实践
## 日期范围选择器配置
### 1. 基础配置
```vue
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
format="yyyy-MM-dd"
clearable
:unlink-panels="true"
:picker-options="pickerOptions">
</el-date-picker>
```
### 2. 关键属性说明
- **unlink-panels="true"**:左右面板独立操作,支持年月快速选择
- **value-format**:数据格式,用于v-model绑定
- **format**:显示格式,用户看到的格式
- **clearable**:支持清空选择
- **picker-options**:自定义配置选项
## 快捷选择配置
### 1. 标准快捷选项
```javascript
pickerOptions: {
shortcuts: [
{
text: '最近一周',
onClick(picker) {
var end = new Date();
var start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
},
{
text: '最近一个月',
onClick(picker) {
var end = new Date();
var start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
},
{
text: '最近三个月',
onClick(picker) {
var end = new Date();
var start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}
]
}
```
### 2. 年份跨度快捷选项
```javascript
pickerOptions: {
shortcuts: [
{
text: '两年',
onClick(picker) {
var start = new Date();
var end = new Date();
end.setFullYear(start.getFullYear() + 2);
picker.$emit('pick', [start, end]);
}
},
{
text: '三年',
onClick(picker) {
var start = new Date();
var end = new Date();
end.setFullYear(start.getFullYear() + 3);
picker.$emit('pick', [start, end]);
}
}
]
}
```
## 日期验证和处理
### 1. 日期范围验证
```javascript
// 验证日期范围不少于指定天数
validateDateRange(dateRange, minDays) {
if (!dateRange || dateRange.length !== 2) {
return false;
}
var startDate = new Date(dateRange[0]);
var endDate = new Date(dateRange[1]);
var diffTime = endDate.getTime() - startDate.getTime();
var diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays >= minDays;
}
```
### 2. 日期格式化
```javascript
// 格式化日期范围为API需要的格式
formatDateRange(dateRange) {
if (!dateRange || dateRange.length !== 2) {
return { start: '', end: '' };
}
return {
start: dateRange[0],
end: dateRange[1]
};
}
```
### 3. 监听日期变化
```javascript
watch: {
dateRange(newVal) {
if (newVal && newVal.length === 2) {
this.formData.start_date = newVal[0];
this.formData.end_date = newVal[1];
} else {
this.formData.start_date = '';
this.formData.end_date = '';
}
}
}
```
## 常用日期操作工具函数
### 1. 日期计算
```javascript
// 获取N天前的日期
getDaysAgo(days) {
var date = new Date();
date.setTime(date.getTime() - days * 24 * 60 * 60 * 1000);
return date;
}
// 获取N年后的日期
getYearsLater(years) {
var date = new Date();
date.setFullYear(date.getFullYear() + years);
return date;
}
// 格式化日期为YYYY-MM-DD
formatDate(date) {
var year = date.getFullYear();
var month = String(date.getMonth() + 1).padStart(2, '0');
var day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
```
### 2. 日期验证
```javascript
// 验证日期是否有效
isValidDate(dateString) {
var date = new Date(dateString);
return date instanceof Date && !isNaN(date);
}
// 验证日期是否在指定范围内
isDateInRange(date, startDate, endDate) {
var checkDate = new Date(date);
var start = new Date(startDate);
var end = new Date(endDate);
return checkDate >= start && checkDate <= end;
}
```
## 样式自定义
### 1. 基础样式调整
```less
.el-date-editor {
width: 100%;
.el-range-separator {
color: #606266;
}
.el-range-input {
background-color: transparent;
border: none;
outline: none;
padding: 0;
}
}
```
### 2. 快捷选项样式
```less
.el-picker-panel__shortcut {
color: #606266;
&:hover {
color: #409EFF;
background-color: #f5f7fa;
}
}
```
## 最佳实践建议
1. **启用unlink-panels**:让用户能够独立操作左右面板
2. **提供快捷选项**:常用的时间范围选择
3. **明确的占位符**:清晰的开始和结束日期提示
4. **数据验证**:确保日期范围符合业务要求
5. **响应式设计**:在不同屏幕尺寸下的适配
6. **错误处理**:友好的错误提示信息
\ No newline at end of file
---
description: Element UI文件上传组件使用规范
globs: **/*.vue
---
# Element UI文件上传组件使用规范
## 基础配置
### 1. 上传组件基本属性
```vue
<el-upload
:data="fileData"
:action="uploadUrl"
:on-success="handleUploadSuccess"
:before-upload="beforeUpload"
accept=".pdf"
:show-file-list="false">
<el-button icon="el-icon-upload2" type="primary">上传</el-button>
<span class="upload-tip">仅支持PDF格式</span>
</el-upload>
```
### 2. 上传接口规范
- **文件上传接口**:`http://file.liexindev.net/uploadFile?sys_type=2`
- **图片上传接口**:`http://image.liexindev.net/uploadImage?sys_type=4`
### 3. 返回数据格式
```javascript
// 文件上传成功返回
{
"code": 0,
"msg": "成功",
"data": {
"file_name": "实际文件名.pdf",
"oss_file_id": "文件ID",
"oss_file_url": "http://file.liexindev.net/download/xxx",
"sort": 0
}
}
```
## 文件验证规范
### 1. 上传前验证
```javascript
beforeUpload(file) {
var isValidType = file.type === 'application/pdf';
if (!isValidType) {
this.$message.error('只能上传PDF格式的文件!');
return false;
}
return true;
}
```
### 2. 常用文件类型验证
- **PDF文件**:`file.type === 'application/pdf'`
- **图片文件**:`['image/jpeg', 'image/png', 'image/gif'].includes(file.type)`
- **Excel文件**:`file.type === 'application/vnd.ms-excel'` 或 `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
### 3. 文件大小限制(可选)
```javascript
// 如果需要限制文件大小
var isValidSize = file.size / 1024 / 1024 < 10; // 10MB
if (!isValidSize) {
this.$message.error('文件大小不能超过10MB!');
return false;
}
```
## 上传成功处理
### 1. 标准处理逻辑
```javascript
handleUploadSuccess(response, file) {
if (response.code === 0) {
this.formData.file_url = response.data.oss_file_url;
this.formData.file_name = response.data.file_name;
this.$message.success('文件上传成功!');
} else {
this.$message.error(response.msg || '文件上传失败!');
}
}
```
### 2. 文件显示组件
```vue
<div v-if="formData.file_url" class="uploaded-file">
<i class="el-icon-document"></i>
<span>{{ formData.file_name }}</span>
<a href="javascript:;" @click="previewFile" class="file-link">查看</a>
</div>
```
### 3. 文件预览功能
```javascript
previewFile() {
if (this.formData.file_url) {
window.open(this.formData.file_url);
}
}
```
## 样式规范
### 1. 基础样式
```less
.upload-tip {
color: #999;
font-size: 12px;
margin-left: 10px;
}
.uploaded-file {
margin-top: 10px;
padding: 8px 12px;
background: #f0f9ff;
border: 1px solid #b3d8ff;
border-radius: 4px;
display: flex;
align-items: center;
gap: 8px;
i {
color: #409eff;
font-size: 16px;
}
span {
flex: 1;
color: #333;
}
.file-link {
color: #409eff;
text-decoration: none;
font-size: 12px;
&:hover {
text-decoration: underline;
}
}
}
```
## 开发注意事项
1. **不显示文件列表**:设置 `:show-file-list="false"` 自定义文件显示
2. **accept属性**:限制文件选择器只显示特定类型文件
3. **错误处理**:提供清晰的错误提示信息
4. **文件名显示**:使用服务器返回的真实文件名
5. **预览功能**:确保文件URL可直接访问或下载
\ No newline at end of file
---
description: OrderTrack goods.vue 签署合作协议弹窗功能规范
globs: src/views/OrderTrack/goods.vue
---
# OrderTrack goods.vue 签署合作协议弹窗功能规范
## 文件概述
[src/views/OrderTrack/goods.vue](mdc:src/views/OrderTrack/goods.vue) 包含订单跟踪商品页面和签署合作协议弹窗功能。
## 签署合作协议弹窗核心功能
### 1. 弹窗触发机制
- **触发按钮**: "签署合作协议" 按钮调用 `signPartnerAgreement()` 方法
- **状态获取**: 先调用接口 `GET /api/attachment/getAttachmentAuditDetail` 获取协议状态
- **弹窗选择**: 根据 `has_cooperation_agreement` 和 `status` 字段决定显示哪个弹窗
### 2. 四种弹窗情况和标题逻辑
```javascript
// 情况1.1:供应商没有提交过申请
has_cooperation_agreement = 0 && id = '' → 弹窗1.1 → title = '签署采购合作框架协议'
// 情况1.2:供应商已提交申请,状态为审核中
has_cooperation_agreement = 0 && status = 0 → 弹窗1.2 → title = '申请电子签'
// 情况1.3:供应商已提交申请,状态为审核不通过 (红色状态)
has_cooperation_agreement = 0 && status = -1 → 弹窗1.3 → title = '申请电子签'
// 情况2:已有【采购合作框架协议】,可走电子签流程
has_cooperation_agreement = 1 → 弹窗2 → title = '提示'
```
### 3. 申请表单弹窗功能
- **协议模板下载**: 动态生成地址 `NODE_ENVS + '/api/attachment/downloadAttachmentTemplate?type=cooperation_agreement'`
- **文件上传**: 支持PDF格式,上传接口 `http://file.liexindev.net/uploadFile?sys_type=2`
- **有效期选择**:
- 长期有效 (validity_type = 1)
- 自定义 (validity_type = 2,最低2年/730天)
- 快捷选择: 两年、三年、五年
- **状态显示**: 动态颜色显示审核状态和附加信息
- status = -1: 红色 `#F56C6C` (审核失败) + 显示审核原因
- status = 1: 绿色 `#67C23A` (审核通过)
- status = 0: 橙色 `#E6A23C` (待审核) + 固定提示文字
### 4. 待审核状态限制 (status = 0)
- **禁用文件上传**: `:disabled="agreementStatus.status === 0"`
- **禁用有效期编辑**: 单选框和日期选择器都禁用
- **禁用提交按钮**: 按钮文字变为"待审核中"
- **提示信息**: 显示"(待审核状态不可修改)"
### 5. 状态提示弹窗
- **触发条件**: `has_cooperation_agreement = 1` 且 `status = 1`
- **弹窗内容**: 显示协议已签署且通过审核的提示
- **文件链接**: 协议文件名可点击下载,绑定 `agreementForm.file_url`
### 6. 数据结构
```javascript
// 协议表单数据
agreementForm: {
id: '', // 记录ID,更新时使用
type: 'cooperation_agreement',
file_url: '', // 文件地址
file_name: '', // 文件名称
validity_type: 1, // 1=长期有效,2=自定义
validity_start: '', // 有效期开始
validity_end: '' // 有效期结束
}
// 协议状态信息
agreementStatus: {
status_name: '', // 状态名称
has_cooperation_agreement: 0, // 是否已有协议
status: 0, // 审核状态:0=待审核,1=审核通过,-1=审核失败
audit_content: '' // 审核意见/原因
}
```
### 7. 关键接口
- **获取状态**: `GET /api/attachment/getAttachmentAuditDetail?type=cooperation_agreement`
- **下载模板**: `GET /api/attachment/downloadAttachmentTemplate?type=cooperation_agreement`
- **文件上传**: `POST http://file.liexindev.net/uploadFile?sys_type=2`
- **提交审核**: `POST /api/attachment/applyAttachmentAudit`
### 8. 表单验证规则
- 文件必须上传 (PDF格式)
- 自定义有效期必须选择日期范围
- 自定义有效期必须≥2年 (730天)
- 待审核状态禁止修改任何内容
### 9. 核心方法结构
```javascript
// 主要业务方法
getAgreementStatus() // 获取协议状态并决定显示哪个弹窗
fillAgreementForm(data, id) // 填充协议表单数据
showSignDialog(title) // 显示申请签署弹窗
showStatusDialog() // 显示状态提示弹窗
handleNoDataCase(statusName) // 处理无数据情况(默认情况1.1)
```
### 10. 代码优化要点
- **消除重复代码**: 将相同的表单填充逻辑提取为 `fillAgreementForm()` 方法
- **统一弹窗显示**: 使用 `showSignDialog()` 和 `showStatusDialog()` 统一管理弹窗状态
- **合并相似逻辑**: 情况1.2和1.3使用相同处理方式,合并判断条件
- **错误处理统一**: 使用 `handleNoDataCase()` 统一处理无数据和接口失败情况
### 11. 状态显示规范
```html
<!-- 审核中状态 -->
<span v-if="agreementStatus.status === 0">
若长时间未审核,可联系猎芯对接窗口
</span>
<!-- 审核不通过状态 -->
<span v-if="agreementStatus.status === -1">
原因:{{ agreementStatus.audit_content }}
</span>
```
**显示逻辑:**
- 审核中(status=0): 显示固定提示文字,状态名称有橙色
- 审核不通过(status=-1): 显示审核原因,状态名称有红色
- 审核通过(status=1): 只显示状态名称,绿色
### 12. 开发注意事项
- 使用 `var` 声明变量,禁用 `let/const`
- 所有API调用不使用默认值,直接使用原始返回数据
- 弹窗宽度:申请表单680px,状态提示550px
- 日期选择器配置 `:unlink-panels="true"` 支持年月快速选择
- 错误处理要友好,避免技术性错误信息
- 保持代码简洁,遵循KISS原则,避免过度抽象
- 状态显示样式统一,使用内联样式保持简洁
\ No newline at end of file
---
description: 登录页面组件开发规范和约定
globs: src/views/User/login.vue,src/assets/css/login/*
---
# 登录页面开发规范
## 组件结构
- 主文件: [src/views/User/login.vue](mdc:src/views/User/login.vue)
- 样式文件: [src/assets/css/login/index.less](mdc:src/assets/css/login/index.less)
## 登录模式支持
- **账号登录**: loginType='account' - 支持账号密码+验证码登录
- **免密码登录**: loginType='sms' - 支持手机号+短信验证码登录
- **绑定手机号**: loginType='bind' - 登录成功后未绑定手机号时自动切换
## Tab切换功能
- 支持"账号登录"和"免密码登录"两种Tab切换
- 绑定手机号模式下隐藏Tab容器 (v-if="loginType !== 'bind'")
- 使用统一的Tab容器样式,背景色 #F1F4FA
- 激活状态使用蓝色主题 #1969F9 + 白色背景
- 所有Tab相关样式写在 .tab-container 类中
## 绑定手机号功能
- 登录成功检测到 !res.data.is_bind_mobile 时自动切换到绑定模式
- 复用免密码登录的表单布局和样式
- 标题改为"绑定手机号",按钮改为"绑定手机号并登录"
- 按钮添加 style="letter-spacing: 0;" 优化长文案显示
- 调用 /api/user/bindMobile 接口完成绑定
- 绑定成功后继续原有的微信绑定流程
## 错误处理模式
- 使用统一的 errors 对象管理所有错误状态
- 通过 getErrorClass() 方法动态返回错误样式类
- 使用 setError(), clearError(), clearAllErrors() 方法管理错误
- 避免使用多个独立的错误状态变量
- 支持 mobile, password, captcha, smsMobile, smsCode, bindMobile, bindSmsCode 等字段
## 表单验证
- 手机号验证统一使用 validateMobile() 方法
- 支持三种验证逻辑:账号登录、短信登录、绑定手机号
- 使用 computed 属性判断登录按钮激活状态:
- isAccountLoginActive: 账号登录激活状态
- isSmsLoginActive: 短信登录激活状态
- isBindLoginActive: 绑定登录激活状态
- 手机号格式验证支持:isMobileValid, isBindMobileValid
## 短信验证码功能
- 统一的倒计时方法 startCountdown(type),支持 'sms' 和 'bind' 两种类型
- 发送验证码按钮支持倒计时功能(60秒)
- 倒计时期间按钮不可点击,样式添加 opacity: 0.6
- 验证码输入框与发送按钮使用 .code-box 布局
- 短信登录和绑定手机号共享发送验证码接口 /auth/sendSms
## 数据结构
```javascript
data: {
loginType: 'account', // 'account' | 'sms' | 'bind'
form: { mobile, password, captcha, captcha_key }, // 账号登录
smsForm: { mobile, smsCode }, // 短信登录
bindForm: { mobile, smsCode }, // 绑定手机号
errors: {}, // 统一错误管理
canSendSms, smsText, countdown, // 短信登录倒计时
canBindSendSms, bindSmsText, bindCountdown // 绑定倒计时
}
```
## 样式约定
- 登录框宽度固定 400px,表单宽度 342px
- 所有输入框高度 43px,圆角 2px
- 主要颜色:蓝色 #1969F9,灰色 #8391AD
- 错误状态边框色 #FF1D00
- 禁止在 .less 和 .css 文件中写注释
## 代码优化原则
- 避免使用 watch 监听器,优先使用 computed 属性
- 统一使用 var 声明变量,禁用 let/const
- 错误信息统一使用 errorMessage 显示
- 相同功能合并优化,如倒计时方法共用
- 遵循 DRY 原则,避免重复代码
\ No newline at end of file
# Menu组件开发规范
## 组件职责
- 顶部导航栏和左侧菜单管理
- 标签页(tabs)功能和状态管理
- 用户信息显示和下拉操作
- 消息通知和系统公告弹窗
- 修改密码功能(支持原密码和短信验证码两种方式)
## 关键功能模块
### 1. 菜单权限管理
- 通过 `getMenu()` 方法获取用户权限菜单
- 概况页面单独处理,其他菜单根据权限过滤
- 菜单数据结构:`menuData` 数组,包含子菜单 `children`
### 2. 标签页管理
- 使用 `sessionStorage` 存储标签页数据
- 支持标签页关闭、切换和自动滚动
- 标签页状态与路由同步
### 3. 消息通知系统
- 实时消息轮询,60秒间隔
- 支持按类型分类:询价、成单、退货、上架有效期
- 消息状态管理:未读、已读、删除
### 4. 密码修改功能
- 支持两种验证方式:原密码验证和手机验证码验证
- 短信验证码倒计时功能
- 修改成功后自动退出登录
## 数据管理规范
### 关键变量声明
```javascript
// 统一使用 var 声明变量
var supplier_id = Number(localStorage.getItem('supplier_id')) || 0;
var userinfo = {};
var menuData = []; // 菜单数据
var tabs = ""; // 标签页数据
var not_read_count = ''; // 未读消息数量
```
### 状态管理
- 菜单折叠状态:`collapse`
- 弹窗显示状态:`dialogVisible`、`dialogNewsVisible`、`dialogNoticeVisible`
- 表单数据:`ruleForm`(原密码)、`smsForm`(短信验证码)
## 接口调用规范
### HTTP请求格式
- 使用项目封装的 `$http` 方法
- 消息相关接口使用 `axios` 直接调用
- 统一的错误处理和成功提示
### 接口地址
- 用户相关:`/api/user/`
- 子账户菜单:`/api/subAccount/getMenu`
- 消息系统:`NODE_ENVS_MSG` 环境变量
- 认证相关:`/auth/`
## 组件交互规范
### 事件处理
- 菜单展开收起:`handleOpen`、`handleClose`、`handleSelect`
- 标签页操作:`tabUrl`、`closetab`
- 消息操作:`updateMsg`、`deleteMsg`、`handleSelectionChange`
### 生命周期
- `created`: 初始化数据获取和事件监听
- `mounted`: 设置当前路由对应的标签状态
- `watch`: 监听路由变化,更新布局
## 样式规范
### CSS类命名
- 导航容器:`.nav-box`、`.nav-top`、`.nav-left`
- 菜单项:`.item`、`.act`(激活状态)
- 用户区域:`.user-box`、`.login-v`
- 弹窗样式:`.demo-ruleForm`、`.dialog-layer-notice`
### 响应式处理
- 根据菜单折叠状态动态调整 `#app` 的 `padding-left`
- 标签页滚动处理,超过9个标签自动滚动到最右侧
## 最佳实践
### 1. 内存管理
- 定时器使用后及时清理:`clearInterval(this.timer)`
- 组件销毁时清理事件监听
### 2. 用户体验
- 消息通知使用 `$notify` 组件,支持HTML内容
- 修改密码成功后2秒延迟跳转登录页
- 倒计时按钮禁用状态管理
### 3. 数据持久化
- 使用 `sessionStorage` 存储标签页状态
- 使用 `localStorage` 存储用户ID
- 使用 `Cookie` 存储欢迎页和公告显示状态
### 4. 错误处理
- 表单验证使用 Element UI 的验证规则
- 网络请求统一错误提示
- 边界情况处理(如空数据、权限不足等)
\ No newline at end of file
---
alwaysApply: true
description: Node.js版本兼容性和环境配置
---
# Node.js 版本兼容性规则
## 项目环境要求
- **Node.js 版本**: 使用 v16.20.2 (记录在 [.nvmrc](mdc:.nvmrc))
- **原因**: Vue CLI 4.5.x 与 Node.js v18+ 的 OpenSSL 3.0 不兼容
- **错误表现**: `error:0308010C:digital envelope routines::unsupported`
## 环境管理
使用 nvm 管理 Node.js 版本:
```bash
nvm use 16.20.2
```
## 重要提醒
- 不要随意升级 Node.js 到 v17+ 版本
- 保持 [package.json](mdc:package.json) 中的启动命令简洁,不添加环境变量
- 如需在新环境部署,确保使用 Node.js v16.x 版本
\ No newline at end of file
---
description: 用户认证和账户管理规范
globs: src/views/User/*
---
# 用户认证和账户管理规范
## 核心文件
- **登录页面**: [src/views/User/login.vue](mdc:src/views/User/login.vue) - 统一登录入口
- **绑定错误页**: [src/views/User/bindError.vue](mdc:src/views/User/bindError.vue) - 绑定异常处理
- **404页面**: [src/views/User/notfound.vue](mdc:src/views/User/notfound.vue) - 页面未找到
## 登录模式支持
### 账号登录 (loginType='account')
- 支持账号/手机号 + 密码 + 图形验证码
- 验证码自动刷新机制
- 密码显示/隐藏切换
- 表单验证:手机号格式、密码长度、验证码必填
### 免密码登录 (loginType='sms')
- 手机号 + 短信验证码登录
- 60秒倒计时防重复发送
- 手机号格式验证
- 短信验证码长度验证
### 绑定手机号 (loginType='bind')
- 登录成功后自动检测绑定状态
- 复用短信登录的界面布局
- 自动隐藏Tab切换容器
- 绑定成功后继续微信绑定流程
## 状态管理模式
### 数据结构
```javascript
data: {
loginType: 'account', // 'account' | 'sms' | 'bind'
form: { mobile, password, captcha, captcha_key },
smsForm: { mobile, smsCode },
bindForm: { mobile, smsCode },
errors: {}, // 统一错误状态管理
errorMessage: '', // 统一错误信息显示
}
```
### 计算属性
- `isMobileValid`: 登录手机号验证
- `isBindMobileValid`: 绑定手机号验证
- `isAccountLoginActive`: 账号登录按钮激活
- `isSmsLoginActive`: 短信登录按钮激活
- `isBindLoginActive`: 绑定登录按钮激活
### 错误处理方法
- `setError(field, message)`: 设置字段错误状态
- `clearError(field)`: 清除指定字段错误
- `clearAllErrors()`: 清除所有错误状态
- `getErrorClass()`: 返回错误样式类
## 接口调用规范
### 认证相关接口
- **账号登录**: `POST /auth/login`
- **短信登录**: `POST /auth/smsLogin`
- **发送验证码**: `POST /auth/sendSms`
- **绑定手机号**: `POST /api/user/bindMobile`
- **图形验证码**: `GET /auth/cp`
- **微信二维码**: `GET /api/login/getwxqrcode`
### 响应数据处理
- `res.data.api_token`: 登录令牌
- `res.data.supplier_id`: 供应商ID
- `res.data.is_bind_mobile`: 手机号绑定状态
- `res.data.is_bind_wechat`: 微信绑定状态
- 登录成功后的数据存储和跳转逻辑
## 用户流程设计
### 登录成功后的检查流程
1. 保存用户信息和token到localStorage
2. 检查手机号绑定状态 (`is_bind_mobile`)
3. 如未绑定手机号,切换到绑定模式
4. 手机号绑定成功后检查微信绑定状态
5. 如未绑定微信,跳转微信绑定页面
6. 全部绑定完成后跳转到首页
### 倒计时功能
- 统一的倒计时方法 `startCountdown(type)`
- 支持 'sms' 和 'bind' 两种类型
- 60秒倒计时,期间按钮不可点击
- 倒计时结束后恢复发送状态
## 用户体验规范
### 界面交互
- Tab切换流畅,状态保持
- 错误提示及时清除
- 按钮状态实时反馈
- Loading状态统一管理
### 错误处理
- 网络错误统一提示
- 业务错误根据code判断
- 验证错误实时显示
- 错误状态自动清除
### 安全规范
- 密码输入支持显示/隐藏
- 图形验证码防机器人
- 短信验证码防刷
- Token过期自动处理
## 样式和布局
- 登录框固定宽度400px
- 表单宽度342px
- 输入框高度43px,圆角2px
- 主色调#1969F9,错误色#FF1D00
- Tab容器背景#F1F4FA
- 绑定模式下按钮添加 `letter-spacing: 0`
\ No newline at end of file
---
alwaysApply: true
description: Vue项目结构和开发规范
---
# 芯链系统项目结构
## 技术栈
- Vue 2.6.11 + Element UI 2.15.13
- Vue Router 3.2.0
- Axios 0.21.1
- Less 样式预处理器
## 项目结构
- 入口文件: [src/main.js](mdc:src/main.js)
- 路由配置: [src/router/index.js](mdc:src/router/index.js)
- 组件目录: [src/components/](mdc:src/components/)
- 页面目录: [src/views/](mdc:src/views/)
- 样式资源: [src/assets/css/](mdc:src/assets/css/)
## 主要业务模块
- 用户管理: [src/views/User/](mdc:src/views/User/)
- 商品管理: [src/views/Goods/](mdc:src/views/Goods/)
- 门店管理: [src/views/Store/](mdc:src/views/Store/)
- 寄售管理: [src/views/consignmentManagement/](mdc:src/views/consignmentManagement/)
- 账单管理: [src/views/billManagement/](mdc:src/views/billManagement/)
## 开发配置
- 开发端口: 8080
- 代理配置在 [vue.config.js](mdc:vue.config.js)
- 关闭了 ESLint 检查 (`lintOnSave: false`)
\ No newline at end of file
......@@ -337,6 +337,7 @@
created() {
this.formInline.inquiry_sn = this.$route.query.inquiry_sn || '';
this.formInline.quote_sn = this.$route.query.quote_sn || '';
this.formInline.purchase_sn = this.$route.query.purchase_sn || '';
this.getData();
},
watch: {
......@@ -344,6 +345,7 @@
if (to.path == '/orderTrackGoods') {
this.formInline.inquiry_sn = this.$route.query.inquiry_sn || '';
this.formInline.quote_sn = this.$route.query.quote_sn || '';
this.formInline.purchase_sn = this.$route.query.purchase_sn || '';
this.getData();
}
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment