feat: 1. 添加axios全局路由拦截进行自动添加请求jwt。 2.重新整理路由表。 3. 文档列表新增版本差异对比。 4.菜单路由可访问列表通过对接接口返回,添加全局路由检测。
5. 修改统一认证登录和管理员登录是通过接口形式进行,存储返回的accessToken。 6. 修改交叉评查的部分样式
This commit is contained in:
@@ -0,0 +1,316 @@
|
||||
# PostgREST查询参考
|
||||
|
||||
## 过滤运算符完整列表
|
||||
|
||||
### 比较运算符
|
||||
|
||||
| 运算符 | 说明 | PostgREST语法 | JavaScript示例 |
|
||||
|--------|------|---------------|----------------|
|
||||
| eq | 等于 | `field=eq.value` | `{ field: 'eq.value' }` |
|
||||
| neq | 不等于 | `field=neq.value` | `{ field: 'neq.value' }` |
|
||||
| gt | 大于 | `field=gt.value` | `{ field: 'gt.100' }` |
|
||||
| gte | 大于等于 | `field=gte.value` | `{ field: 'gte.100' }` |
|
||||
| lt | 小于 | `field=lt.value` | `{ field: 'lt.100' }` |
|
||||
| lte | 小于等于 | `field=lte.value` | `{ field: 'lte.100' }` |
|
||||
|
||||
### 模式匹配运算符
|
||||
|
||||
| 运算符 | 说明 | PostgREST语法 | JavaScript示例 |
|
||||
|--------|------|---------------|----------------|
|
||||
| like | 模糊匹配(区分大小写) | `field=like.*pattern*` | `{ field: 'like.*测试*' }` |
|
||||
| ilike | 模糊匹配(不区分大小写) | `field=ilike.*pattern*` | `{ field: 'ilike.*TEST*' }` |
|
||||
|
||||
**模式匹配通配符**:
|
||||
- `*`: 匹配任意字符(等同于SQL的`%`)
|
||||
- `_`: 匹配单个字符
|
||||
|
||||
**示例**:
|
||||
```javascript
|
||||
// 查询标题以"合同"开头的文档
|
||||
{ title: 'like.合同*' }
|
||||
|
||||
// 查询标题以"合同"结尾的文档
|
||||
{ title: 'like.*合同' }
|
||||
|
||||
// 查询标题包含"合同"的文档
|
||||
{ title: 'like.*合同*' }
|
||||
|
||||
// 不区分大小写查找
|
||||
{ title: 'ilike.*contract*' }
|
||||
```
|
||||
|
||||
### 列表运算符
|
||||
|
||||
| 运算符 | 说明 | PostgREST语法 | JavaScript示例 |
|
||||
|--------|------|---------------|----------------|
|
||||
| in | 在列表中 | `field=in.(value1,value2,...)` | `{ field: 'in.(active,pending)' }` |
|
||||
| cs | 包含(数组字段) | `field=cs.{value1,value2}` | `{ tags: 'cs.{重要,紧急}' }` |
|
||||
| cd | 被包含(数组字段) | `field=cd.{value1,value2}` | `{ tags: 'cd.{重要,紧急}' }` |
|
||||
| ov | 重叠(数组字段) | `field=ov.{value1,value2}` | `{ tags: 'ov.{重要,紧急}' }` |
|
||||
|
||||
**示例**:
|
||||
```javascript
|
||||
// 查询状态为active或pending的文档
|
||||
{ status: 'in.(active,pending)' }
|
||||
|
||||
// 查询ID在[1, 2, 3]中的文档
|
||||
{ id: 'in.(1,2,3)' }
|
||||
|
||||
// 查询标签包含"重要"和"紧急"的文档(数组字段)
|
||||
{ tags: 'cs.{重要,紧急}' }
|
||||
```
|
||||
|
||||
### Null检查运算符
|
||||
|
||||
| 运算符 | 说明 | PostgREST语法 | JavaScript示例 |
|
||||
|--------|------|---------------|----------------|
|
||||
| is | 是null | `field=is.null` | `{ field: 'is.null' }` |
|
||||
| nt | 不是null | `field=nt.null` (或 `is.not.null`) | `{ field: 'nt.null' }` |
|
||||
|
||||
**示例**:
|
||||
```javascript
|
||||
// 查询deleted_at为null的文档(未删除)
|
||||
{ deleted_at: 'is.null' }
|
||||
|
||||
// 查询deleted_at不为null的文档(已删除)
|
||||
{ deleted_at: 'nt.null' }
|
||||
```
|
||||
|
||||
## 排序
|
||||
|
||||
### 单字段排序
|
||||
|
||||
```javascript
|
||||
// 按创建时间升序
|
||||
{ order: 'created_at.asc' }
|
||||
|
||||
// 按创建时间降序
|
||||
{ order: 'created_at.desc' }
|
||||
```
|
||||
|
||||
### 多字段排序
|
||||
|
||||
```javascript
|
||||
// 先按状态升序,再按创建时间降序
|
||||
{ order: 'status.asc,created_at.desc' }
|
||||
```
|
||||
|
||||
## 分页
|
||||
|
||||
### Limit和Offset
|
||||
|
||||
```javascript
|
||||
// 每页10条,第1页
|
||||
{ limit: 10, offset: 0 }
|
||||
|
||||
// 每页10条,第2页
|
||||
{ limit: 10, offset: 10 }
|
||||
|
||||
// 每页20条,第3页
|
||||
{ limit: 20, offset: 40 }
|
||||
```
|
||||
|
||||
### 获取总数
|
||||
|
||||
PostgREST会在响应头中返回`Content-Range`,包含总记录数:
|
||||
|
||||
```
|
||||
Content-Range: 0-9/50
|
||||
```
|
||||
表示:返回第0-9条记录,总共50条记录。
|
||||
|
||||
**解析示例**:
|
||||
```javascript
|
||||
const response = await apiClient.get('/postgrest/documents', {
|
||||
params: { limit: 10, offset: 0 }
|
||||
});
|
||||
|
||||
const contentRange = response.headers['content-range'];
|
||||
const total = parseInt(contentRange.split('/')[1]); // 50
|
||||
```
|
||||
|
||||
## 字段选择
|
||||
|
||||
### 选择特定字段
|
||||
|
||||
```javascript
|
||||
// 只返回id, title, status字段
|
||||
{ select: 'id,title,status' }
|
||||
```
|
||||
|
||||
### 嵌套查询(外键关联)
|
||||
|
||||
PostgREST支持嵌套查询,但DocAuditAI暂未启用此功能。
|
||||
|
||||
## 高级查询
|
||||
|
||||
### 逻辑运算符
|
||||
|
||||
**注意**: 前端应避免使用`or`、`and`、`not`参数,这些参数由后端自动生成(用于数据范围和交叉评查权限)。
|
||||
|
||||
### 复杂查询示例
|
||||
|
||||
```javascript
|
||||
// 查询状态为active且创建时间在2025年之后的文档,按创建时间降序,每页10条
|
||||
const params = {
|
||||
status: 'eq.active',
|
||||
created_at: 'gte.2025-01-01',
|
||||
order: 'created_at.desc',
|
||||
limit: 10,
|
||||
offset: 0
|
||||
};
|
||||
|
||||
const response = await apiClient.get('/postgrest/documents', { params });
|
||||
```
|
||||
|
||||
## 完整示例
|
||||
|
||||
### 示例1:文档列表查询
|
||||
|
||||
```javascript
|
||||
// 需求:查询标题包含"合同"、状态为active的文档,
|
||||
// 按创建时间降序排列,每页20条,第2页
|
||||
|
||||
async function getDocumentList() {
|
||||
const response = await apiClient.get('/postgrest/documents', {
|
||||
params: {
|
||||
title: 'ilike.*合同*',
|
||||
status: 'eq.active',
|
||||
order: 'created_at.desc',
|
||||
limit: 20,
|
||||
offset: 20 // 第2页
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
data: response.data,
|
||||
total: parseInt(response.headers['content-range'].split('/')[1])
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 示例2:高级搜索
|
||||
|
||||
```javascript
|
||||
// 需求:搜索2025年创建的、状态为active或pending的文档
|
||||
|
||||
async function advancedSearch() {
|
||||
const response = await apiClient.get('/postgrest/documents', {
|
||||
params: {
|
||||
status: 'in.(active,pending)',
|
||||
created_at: 'gte.2025-01-01',
|
||||
created_at: 'lt.2026-01-01', // 注意:PostgREST会组合多个同名参数
|
||||
order: 'created_at.desc'
|
||||
}
|
||||
});
|
||||
|
||||
return response.data;
|
||||
}
|
||||
```
|
||||
|
||||
### 示例3:分页封装
|
||||
|
||||
```javascript
|
||||
// 通用分页函数
|
||||
async function fetchPaginated(table, page, pageSize, filters = {}) {
|
||||
const params = {
|
||||
limit: pageSize,
|
||||
offset: (page - 1) * pageSize,
|
||||
...filters
|
||||
};
|
||||
|
||||
const response = await apiClient.get(`/postgrest/${table}`, { params });
|
||||
|
||||
const contentRange = response.headers['content-range'];
|
||||
const total = contentRange ? parseInt(contentRange.split('/')[1]) : 0;
|
||||
|
||||
return {
|
||||
data: response.data,
|
||||
total: total,
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
totalPages: Math.ceil(total / pageSize)
|
||||
};
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
const result = await fetchPaginated('documents', 1, 10, {
|
||||
status: 'eq.active',
|
||||
order: 'created_at.desc'
|
||||
});
|
||||
|
||||
console.log(`共${result.total}条记录,第${result.page}/${result.totalPages}页`);
|
||||
```
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
### 1. 始终使用分页
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法
|
||||
{ limit: 10, offset: 0 }
|
||||
|
||||
// ❌ 不好的做法(返回所有数据)
|
||||
{}
|
||||
```
|
||||
|
||||
### 2. 只查询需要的字段
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法(只查询id和title)
|
||||
{ select: 'id,title' }
|
||||
|
||||
// ❌ 不好的做法(查询所有字段)
|
||||
{}
|
||||
```
|
||||
|
||||
### 3. 使用索引字段进行过滤
|
||||
|
||||
优先使用已建立索引的字段(如id, user_id, ou_id, status)进行过滤,提升查询性能。
|
||||
|
||||
### 4. 避免过度模糊查询
|
||||
|
||||
```javascript
|
||||
// ✅ 好的做法(明确的过滤条件)
|
||||
{ title: 'like.合同*' }
|
||||
|
||||
// ❌ 不好的做法(前后通配符,性能差)
|
||||
{ title: 'like.*合同*' }
|
||||
```
|
||||
|
||||
## 常见错误
|
||||
|
||||
### 错误1:运算符拼写错误
|
||||
|
||||
```javascript
|
||||
// ❌ 错误
|
||||
{ status: 'equal.active' } // 应该是 eq
|
||||
|
||||
// ✅ 正确
|
||||
{ status: 'eq.active' }
|
||||
```
|
||||
|
||||
### 错误2:日期格式错误
|
||||
|
||||
```javascript
|
||||
// ❌ 错误
|
||||
{ created_at: 'gte.2025/01/01' } // 格式错误
|
||||
|
||||
// ✅ 正确
|
||||
{ created_at: 'gte.2025-01-01' } // ISO 8601格式
|
||||
```
|
||||
|
||||
### 错误3:in运算符语法错误
|
||||
|
||||
```javascript
|
||||
// ❌ 错误
|
||||
{ status: 'in.active,pending' } // 缺少括号
|
||||
|
||||
// ✅ 正确
|
||||
{ status: 'in.(active,pending)' }
|
||||
```
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [PostgREST官方文档](https://postgrest.org/en/stable/)
|
||||
- [PostgREST API查询语法](https://postgrest.org/en/stable/api.html#horizontal-filtering-rows)
|
||||
Reference in New Issue
Block a user