123 lines
4.7 KiB
Markdown
123 lines
4.7 KiB
Markdown
# 参考:前端框架差异对照
|
||
|
||
> 前端 UI 适配时加载此文档。
|
||
> **核心原则**:逐行阅读理解后修改,不要机械式查找替换。
|
||
|
||
---
|
||
|
||
## script 部分差异
|
||
|
||
| CRM 原版 | shanxia2.0 等价方式 | 说明 |
|
||
|----------|---------------------|------|
|
||
| `getCurrentInstance()` + `proxy` | 删除 | shanxia2.0 不使用 proxy |
|
||
| `proxy?.useDict('xxx')` | `useDictTag('xxx')` 或 `dictStore.getLabel()` | 字典获取 |
|
||
| `proxy?.$modal.msgSuccess(msg)` | `ElMessage.success(msg)` | 成功提示 |
|
||
| `proxy?.$modal.msgWarning(msg)` | `ElMessage.warning(msg)` | 警告提示 |
|
||
| `proxy?.$modal.msgError(msg)` | `ElMessage.error(msg)` | 错误提示 |
|
||
| `proxy?.$modal.confirm(msg)` | `ElMessageBox.confirm(msg, '提示', { type: 'warning' })` | 确认框 |
|
||
| `import { ElMessage } from 'element-plus'` | 删除 | 自动导入 |
|
||
| `import { ref, reactive } from 'vue'` | 删除 | 自动导入 |
|
||
| `request({ url, method, data })` | `request.post('/web/crm/xxx', data)` | API 调用 |
|
||
| `hasPermi(['crm:xxx:add'])` | `v-auth="'add'"` | 权限 |
|
||
|
||
## API 响应数据处理(重要)
|
||
|
||
`request.post` 已在拦截器中解包 `res.data.data`,返回值直接是 API 的 `data` 字段内容。
|
||
但由于加密/非加密场景差异,返回结构不完全一致,**必须使用兼容写法**:
|
||
|
||
```js
|
||
// ✅ 正确:兼容写法
|
||
const res = await fetchXxxList(params)
|
||
const data = (res as any)?.data || res || {}
|
||
list.value = data.list || data.rows || []
|
||
pagination.total = data.total ?? 0
|
||
|
||
// ❌ 错误:多套一层 .data
|
||
const res = await fetchXxxList(params)
|
||
const list = res.data?.list // undefined!
|
||
```
|
||
|
||
**下拉选择器加载用户列表**:
|
||
- `pageSize` 上限为 100(后端 `Query(le=100)`),传 `1000` 会 422
|
||
- 用 `pageSize: 100` + `filterable` 搜索过滤
|
||
- 选项字段为 **`label` / `value`**(`fetchUserOptions`),勿用原 CRM 的 `nickName` / `userId`
|
||
|
||
## Excel / Blob 导出(重要)
|
||
|
||
CRM 列表「导出」走 `responseType: 'blob'`,已在 `src/utils/http/index.ts` 集中适配:
|
||
|
||
```ts
|
||
// api/crm/xxx/index.ts
|
||
export function exportXxx(query?: XxxQuery) {
|
||
return request.post({
|
||
url: `${BASE}/export`,
|
||
data: mapListQuery(query),
|
||
responseType: 'blob',
|
||
showErrorMessage: false
|
||
})
|
||
}
|
||
```
|
||
|
||
```ts
|
||
// 页面 handleExport
|
||
const res = await exportXxx(params)
|
||
const blob = res instanceof Blob ? res : new Blob([res as BlobPart], {
|
||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||
})
|
||
// ... createObjectURL + link.click()
|
||
```
|
||
|
||
- 拦截器对 xlsx **不**做 JSON `code` 校验;对 JSON 错误体(如缺少 openpyxl)仍会解析并抛错
|
||
- 后端字典列已由 `crm_export_columns.py` + `excel_export.py` 翻译,前端无需再转 label
|
||
|
||
## 查看详情(SystemViewDialog)
|
||
|
||
| CRM 原版 | shanxia2.0 |
|
||
|----------|------------|
|
||
| `<el-drawer>` 宽抽屉 + Tab | `<SystemViewDialog>`(参考 `clue/detailDrawer.vue`) |
|
||
| 抽屉内 `<dict-tag>` | `ViewField` + `tagType` + `dictStore.getLabel()` |
|
||
|
||
打开详情前:`await dictStore.loadAllDict()`(避免字典 code 直出)。
|
||
|
||
## template 部分差异
|
||
|
||
| CRM 原版 | shanxia2.0 等价方式 | 说明 |
|
||
|----------|---------------------|------|
|
||
| `<el-drawer>` | `<el-dialog>` | 禁止抽屉,统一弹窗 |
|
||
| `<el-drawer size="80%">` | `<el-dialog width="960px">` | 固定宽度 |
|
||
| `<dict-tag :options="xxx" :value="val"/>` | `<el-tag :type="getTagType(val)" size="small">{{ getLabel(val) }}</el-tag>` | 字典展示 |
|
||
| `<right-toolbar>` | `<div class="list-toolbar"><div class="list-toolbar-row">` | 工具栏 |
|
||
| `label-width="120px"` | `label-position="top"` | 表单标签 |
|
||
| `<el-row><el-col :span="12">` 栅格 | `.dialog-form-layout` + `.form-grid-2` | 表单布局 |
|
||
| RuoYi 上传组件 | MinIO `fetchUploadAttachment` | 文件上传 |
|
||
|
||
## 弹窗标准模板
|
||
|
||
```vue
|
||
<el-dialog v-model="innerVisible" :title="isEdit ? '编辑' : '新增'" width="960px"
|
||
destroy-on-close align-center :close-on-click-modal="false"
|
||
style="backdrop-filter: none" class="dialog-form-layout">
|
||
<el-form ref="formRef" :model="formData" :rules="formRules"
|
||
label-position="top" scroll-to-error
|
||
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }">
|
||
<div class="form-grid-2">
|
||
<!-- 表单项 -->
|
||
</div>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="innerVisible = false"><el-icon><Close /></el-icon>取消</el-button>
|
||
<el-button type="primary" @click="handleSubmit"><el-icon><Check /></el-icon>确定</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
```
|
||
|
||
## style 部分差异
|
||
|
||
- UnoCSS 工具类 → Tailwind CSS 等价类
|
||
- 复杂样式用 SCSS
|
||
- 使用门户标准 CSS 类:`dialog-form-layout`、`form-grid-2`、`form-col-span-2`
|
||
|
||
## 详细对照规则
|
||
|
||
完整的 1044 行替换规则见 `docs/crm-frontend-migration-guide.md`。
|