feat: 新增角色管理模块,优化API与交互体验
1. 新增角色管理后台页面、路由与国际化文案 2. 重构API请求错误处理逻辑,统一拦截业务与HTTP错误 3. 新增确认弹窗组合式函数,区分取消与请求错误场景 4. 完善表格按钮权限与显示控制逻辑 5. 更新API参数规范与文档说明 6. 修复部分页面分页数据解析问题
This commit is contained in:
213
docs/表格组件使用说明.md
213
docs/表格组件使用说明.md
@@ -17,7 +17,8 @@
|
|||||||
7. [路由配置](#7-路由配置)
|
7. [路由配置](#7-路由配置)
|
||||||
8. [API 文件写法](#8-api-文件写法)
|
8. [API 文件写法](#8-api-文件写法)
|
||||||
9. [旧代码迁移对照](#9-旧代码迁移对照)
|
9. [旧代码迁移对照](#9-旧代码迁移对照)
|
||||||
10. [常见问题排查](#10-常见问题排查)
|
10. [接口请求错误处理规范](#10-接口请求错误处理规范)
|
||||||
|
11. [常见问题排查](#11-常见问题排查)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -963,7 +964,215 @@ export function deleteFactoryArea (data) {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. 常见问题排查
|
## 10. 接口请求错误处理规范
|
||||||
|
|
||||||
|
> 错误处理采用 **三层架构**:拦截器全局兜底 → confirmMixin 区分取消/错误 → 页面标准模式。
|
||||||
|
|
||||||
|
### 10.1 错误处理总流程
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ 页面发起 API 请求 │
|
||||||
|
└──────────┬──────────┘
|
||||||
|
│
|
||||||
|
┌──────────▼──────────┐
|
||||||
|
│ 是否需 confirm ? │
|
||||||
|
└─────┬────────┬──────┘
|
||||||
|
│ 否 │ 是
|
||||||
|
│ │
|
||||||
|
│ ┌────▼───────────┐
|
||||||
|
│ │ $confirmAction() │
|
||||||
|
│ │ (confirmMixin) │
|
||||||
|
│ └────┬──────┬─────┘
|
||||||
|
│ 取消 │ │ 确认
|
||||||
|
│ ┌────▼──┐ │
|
||||||
|
│ │return │ │
|
||||||
|
│ │ true │ │
|
||||||
|
│ └───────┘ │
|
||||||
|
│ │
|
||||||
|
┌──────────▼────────────────▼──┐
|
||||||
|
│ axios 请求 │
|
||||||
|
└──────────┬───────────────────┘
|
||||||
|
│
|
||||||
|
┌───────────▼────────────┐
|
||||||
|
│ HTTP 状态码是什么? │
|
||||||
|
└──┬──────────────┬──────┘
|
||||||
|
200 │ │ 非 200
|
||||||
|
│ │
|
||||||
|
┌───────────▼──────┐ ┌───▼─────────────┐
|
||||||
|
│ response.data. │ │ HTTP 拦截器 │
|
||||||
|
│ code 是什么? │ │ error.message = │
|
||||||
|
└──┬───────────┬───┘ │ '未授权/超时/...' │
|
||||||
|
0 │ │≠0 └───┬──────────────┘
|
||||||
|
│ │ │
|
||||||
|
┌────▼──┐ ┌─────▼──────┐ │
|
||||||
|
│resolve│ │handleError()│◄─┘
|
||||||
|
│ data │ │Message.error│
|
||||||
|
└───────┘ └─────┬──────┘
|
||||||
|
│
|
||||||
|
┌────▼────┐
|
||||||
|
│ throw │
|
||||||
|
│ error │
|
||||||
|
└────┬────┘
|
||||||
|
│
|
||||||
|
┌─────────▼─────────┐
|
||||||
|
│ 页面层 catch 到了吗?│
|
||||||
|
└────┬──────────┬───┘
|
||||||
|
没写 │ │ 写了
|
||||||
|
│ │
|
||||||
|
┌─────────▼──┐ ┌────▼─────────────┐
|
||||||
|
│ 什么都不发生 │ │ finally 关锁 │
|
||||||
|
│(依赖拦截器 │ │ loading/submitting│
|
||||||
|
│ Message) │ │ = false │
|
||||||
|
└────────────┘ └──────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 三层架构详图
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ 第一层:全局拦截器 │
|
||||||
|
│ src/api/_service.js │
|
||||||
|
│ │
|
||||||
|
│ 拦截所有 axios 响应,统一处理两类错误: │
|
||||||
|
│ │
|
||||||
|
│ HTTP 错误 (4xx/5xx) │
|
||||||
|
│ ├─ 400 → error.message = '请求错误' │
|
||||||
|
│ ├─ 401 → error.message = '未授权,请登录' │
|
||||||
|
│ ├─ 403 → error.message = '拒绝访问' │
|
||||||
|
│ ├─ 404 → error.message = '请求地址出错' │
|
||||||
|
│ ├─ 408 → error.message = '请求超时' │
|
||||||
|
│ ├─ 500 → error.message = '服务器内部错误' │
|
||||||
|
│ ├─ 502 → error.message = '网关错误' │
|
||||||
|
│ ├─ ... 其它状态码 │
|
||||||
|
│ │ │
|
||||||
|
│ └─ handleError(error) │
|
||||||
|
│ ├─ store.dispatch('d2admin/log/push', ...) │
|
||||||
|
│ ├─ console.log(error) (dev only) │
|
||||||
|
│ └─ Message.error(error.message) ← 弹红色提示 │
|
||||||
|
│ │
|
||||||
|
│ 业务错误 (code ≠ 0, 如 code=500 "参数错误") │
|
||||||
|
│ ├─ 取出 response.data.msg 或 '请求失败' │
|
||||||
|
│ ├─ const err = new Error(`${msg}: ${url}`) │
|
||||||
|
│ ├─ handleError(err) │
|
||||||
|
│ └─ throw err ← 继续向上抛给页面层 │
|
||||||
|
└──────────────────────┬──────────────────────────┘
|
||||||
|
│
|
||||||
|
┌──────────────────────▼──────────────────────────┐
|
||||||
|
│ 第二层:confirmMixin │
|
||||||
|
│ src/composables/useConfirmHandle.js │
|
||||||
|
│ │
|
||||||
|
│ $confirmAction(confirmOpts, action) │
|
||||||
|
│ ├─ 第一层 try/catch │
|
||||||
|
│ │ await this.$confirm(...) │
|
||||||
|
│ │ ├─ 用户点"确定" → 继续 │
|
||||||
|
│ │ └─ 用户点"取消" → catch → return true │
|
||||||
|
│ │ │
|
||||||
|
│ └─ 第二层 try/catch │
|
||||||
|
│ await action() ← 执行 API 调用 │
|
||||||
|
│ ├─ 成功 → return false │
|
||||||
|
│ └─ 失败 → catch → return false │
|
||||||
|
│ (拦截器已弹出 Message,此处静默吞掉) │
|
||||||
|
└──────────────────────┬──────────────────────────┘
|
||||||
|
│
|
||||||
|
┌──────────────────────▼──────────────────────────┐
|
||||||
|
│ 第三层:页面标准模式 │
|
||||||
|
│ │
|
||||||
|
│ 场景A — 查询 (fetchData) │
|
||||||
|
│ ┌─────────────────────────────────────────────┐ │
|
||||||
|
│ │ async fetchData () { │ │
|
||||||
|
│ │ this.loading = true │ │
|
||||||
|
│ │ try { │ │
|
||||||
|
│ │ const res = await getList(...) │ │
|
||||||
|
│ │ this.tableData = res.data │ │
|
||||||
|
│ │ } finally { this.loading = false } │ │
|
||||||
|
│ │ } │ │
|
||||||
|
│ └─────────────────────────────────────────────┘ │
|
||||||
|
│ • try 内无 catch:拦截器已弹错误提示 │
|
||||||
|
│ • finally 始终执行:loading 无论成败都关闭 │
|
||||||
|
│ │
|
||||||
|
│ 场景B — 提交 (onDialogSubmit) │
|
||||||
|
│ ┌─────────────────────────────────────────────┐ │
|
||||||
|
│ │ async onDialogSubmit () { │ │
|
||||||
|
│ │ this.submitting = true │ │
|
||||||
|
│ │ try { │ │
|
||||||
|
│ │ await createApi(this.formData) │ │
|
||||||
|
│ │ this.$message.success(...) ← 成功才执行 │ │
|
||||||
|
│ │ this.dialogVisible = false │ │
|
||||||
|
│ │ this.fetchData() │ │
|
||||||
|
│ │ } finally { this.submitting = false } │ │
|
||||||
|
│ │ } │ │
|
||||||
|
│ └─────────────────────────────────────────────┘ │
|
||||||
|
│ • 出错时弹窗不关,用户可修改后重试 │
|
||||||
|
│ │
|
||||||
|
│ 场景C — 删除 (handleDelete) │
|
||||||
|
│ ┌─────────────────────────────────────────────┐ │
|
||||||
|
│ │ async handleDelete (row) { │ │
|
||||||
|
│ │ const cancelled = await this.$confirmAction(│ │
|
||||||
|
│ │ { message: this.key('confirm_delete'), │ │
|
||||||
|
│ │ title: this.key('tip') }, │ │
|
||||||
|
│ │ () => deleteApi({ id: row.id }) │ │
|
||||||
|
│ │ ) │ │
|
||||||
|
│ │ if (cancelled) return ← 用户取消,静默 │ │
|
||||||
|
│ │ this.$message.success(...) ← 成功才执行 │ │
|
||||||
|
│ │ this.fetchData() │ │
|
||||||
|
│ │ } │ │
|
||||||
|
│ └─────────────────────────────────────────────┘ │
|
||||||
|
│ • $confirmAction 内已处理错误 Message,不额外 catch│
|
||||||
|
│ • 没有 try/catch 包裹 → 代码更简洁直观 │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.3 关键原则
|
||||||
|
|
||||||
|
| 原则 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **拦截器兜底** | 所有 API 错误(HTTP + 业务)统一在 `_service.js` 中弹出 `Message.error` |
|
||||||
|
| **finally 关锁** | `loading` / `submitting` 用 `try { ... } finally { lock = false }`,不论成败都关闭 |
|
||||||
|
| **只用 catch 不动** | 成功后的 `$message.success` / `dialogVisible = false` / `fetchData` 不放在 `finally`,只有成功才执行 |
|
||||||
|
| **$confirmAction 包装** | 删除/批量操作类的 confirm + API 用 `$confirmAction()` 区分取消和错误 |
|
||||||
|
| **不吞业务错误** | `fetchData` / `onDialogSubmit` 的 try 只写 `finally`,不写 `catch`,错误由拦截器处理 |
|
||||||
|
|
||||||
|
### 10.4 confirmMixin 使用
|
||||||
|
|
||||||
|
页面中引入:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { confirmMixin } from '@/composables/useConfirmHandle'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [
|
||||||
|
i18nMixin('page.xxx.xxx'),
|
||||||
|
confirmMixin // ← 新增
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
API:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// $confirmAction(confirmOpts, actionFn) → Promise<boolean>
|
||||||
|
// 返回 true = 用户点了取消
|
||||||
|
// 返回 false = action 已执行完成(无论成功失败)
|
||||||
|
|
||||||
|
const cancelled = await this.$confirmAction(
|
||||||
|
{
|
||||||
|
message: this.key('confirm_delete'), // confirm 正文
|
||||||
|
title: this.key('tip'), // confirm 标题
|
||||||
|
// type: 'warning', // 可选,默认 'warning'
|
||||||
|
// confirmButtonText / cancelButtonText 可选,默认用 page.common 的
|
||||||
|
},
|
||||||
|
() => deleteApi({ id: row.id }) // 确认后执行的异步函数
|
||||||
|
)
|
||||||
|
if (cancelled) return
|
||||||
|
// 以下只在成功时执行
|
||||||
|
this.$message.success(...)
|
||||||
|
this.fetchData()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. 常见问题排查
|
||||||
|
|
||||||
### Q1:弹框打开后不显示内容?
|
### Q1:弹框打开后不显示内容?
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,18 @@
|
|||||||
```
|
```
|
||||||
- 后续统一修改路由后再批量替换
|
- 后续统一修改路由后再批量替换
|
||||||
|
|
||||||
|
#### 4.1 API 参数名必须对齐旧项目(重要)
|
||||||
|
- **API 函数入参的 key 名称必须与旧项目保持一致**,不能自行发明参数名
|
||||||
|
- 例如:旧项目角色菜单接口参数是 `role_id`,不能写成 `id`
|
||||||
|
```js
|
||||||
|
// ❌ 错误 — 自行发明参数名
|
||||||
|
getRoleMenu({ id: this.roleId })
|
||||||
|
// ✅ 正确 — 与旧项目参数名一致
|
||||||
|
getRoleMenu({ role_id: this.roleId })
|
||||||
|
```
|
||||||
|
- **迁移 API 时必须仔细阅读旧项目接口代码**,逐个核对每个接口的入参 key 名称
|
||||||
|
- 如果后端报「xxx 不能为空」,首先检查参数 key 是否与旧项目一致
|
||||||
|
|
||||||
#### 5. 旧 key → 新 key 映射
|
#### 5. 旧 key → 新 key 映射
|
||||||
如果用户提供的是旧页面代码,需要根据对照表做 key 映射。已知映射如下(持续补充):
|
如果用户提供的是旧页面代码,需要根据对照表做 key 映射。已知映射如下(持续补充):
|
||||||
|
|
||||||
|
|||||||
@@ -80,14 +80,19 @@ function createService () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 有 code 判断为项目接口请求
|
// 有 code 判断为项目接口请求
|
||||||
|
let errorMessage = ''
|
||||||
switch (response.data.code) {
|
switch (response.data.code) {
|
||||||
// 返回响应内容
|
|
||||||
case 0: return response.data.data
|
case 0: return response.data.data
|
||||||
// 例如在 code 401 情况下退回到登录页面
|
case 401:
|
||||||
case 401: throw new Error('请重新登录')
|
errorMessage = '请重新登录'
|
||||||
// 根据需要添加其它判断
|
break
|
||||||
default: throw new Error(`${response.data.msg}: ${response.config.url}`)
|
default:
|
||||||
|
errorMessage = response.data.msg || '请求失败'
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
const businessError = new Error(`${errorMessage}: ${response.config.url}`)
|
||||||
|
handleError(businessError)
|
||||||
|
throw businessError
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
const status = get(error, 'response.status')
|
const status = get(error, 'response.status')
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export function getMenuAll (data) {
|
|||||||
url: urls + 'all',
|
url: urls + 'all',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: {
|
params: {
|
||||||
|
method: 'system_settings_menu_configuration_menu_all',
|
||||||
|
platform: 'background',
|
||||||
...data
|
...data
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
75
src/api/system-administration/role.js
Normal file
75
src/api/system-administration/role.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { request } from '@/api/_service'
|
||||||
|
|
||||||
|
const BASE = 'system_settings/user_management/role/'
|
||||||
|
|
||||||
|
function apiParams (method, data = {}) {
|
||||||
|
return {
|
||||||
|
method: `system_settings_user_management_role_${method}`,
|
||||||
|
platform: 'background',
|
||||||
|
...data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRoleList (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'list',
|
||||||
|
method: 'get',
|
||||||
|
params: apiParams('list', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRole (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'create',
|
||||||
|
method: 'post',
|
||||||
|
data: apiParams('create', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function editRole (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'edit',
|
||||||
|
method: 'put',
|
||||||
|
data: apiParams('edit', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteRole (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'delete',
|
||||||
|
method: 'delete',
|
||||||
|
data: apiParams('delete', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateRoleStatus (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'update_status',
|
||||||
|
method: 'put',
|
||||||
|
data: apiParams('update_status', data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function giveRoleMenu (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'give',
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
method: 'system_settings_user_management_give_role_menu',
|
||||||
|
platform: 'background',
|
||||||
|
...data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRoleMenu (data) {
|
||||||
|
return request({
|
||||||
|
url: BASE + 'menu',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
method: 'system_settings_user_management_role_menu',
|
||||||
|
platform: 'background',
|
||||||
|
...data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -105,17 +105,19 @@
|
|||||||
v-bind="colAttrs(col)"
|
v-bind="colAttrs(col)"
|
||||||
>
|
>
|
||||||
<template #default="{ row, $index }">
|
<template #default="{ row, $index }">
|
||||||
<span
|
<template v-for="btn in visibleRowButtons">
|
||||||
v-for="btn in visibleRowButtons"
|
<span
|
||||||
:key="btn.key"
|
v-if="btn.visible(row)"
|
||||||
:style="btn.cssStyle"
|
:key="btn.key"
|
||||||
:class="{ 'action-btn--danger': btn.color === 'danger' }"
|
:style="btn.cssStyle"
|
||||||
class="action-btn"
|
:class="{ 'action-btn--danger': btn.color === 'danger' }"
|
||||||
@click="btn.onClick(row, $index)"
|
class="action-btn"
|
||||||
>
|
@click="btn.onClick(row, $index)"
|
||||||
<i v-if="btn.icon" :class="btn.icon" />
|
>
|
||||||
{{ $t(btn.label) }}
|
<i v-if="btn.icon" :class="btn.icon" />
|
||||||
</span>
|
{{ $t(btn.label) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- 4. 自定义插槽列:列内容或表头可由父组件自定义 -->
|
<!-- 4. 自定义插槽列:列内容或表头可由父组件自定义 -->
|
||||||
|
|||||||
60
src/composables/useConfirmHandle.js
Normal file
60
src/composables/useConfirmHandle.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* $confirm + API 调用的标准包装 mixin
|
||||||
|
*
|
||||||
|
* 解决问题:$confirm 取消也会 reject,和 API 失败混在同一个 catch 里无法区分
|
||||||
|
*
|
||||||
|
* 调用方无需 try/catch,只需判断返回值:
|
||||||
|
* - 返回 true → 用户取消,静默返回
|
||||||
|
* - 返回 false → action 已执行(成功或失败),拦截器已处理 Message
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* import { confirmMixin } from '@/composables/useConfirmHandle'
|
||||||
|
* export default {
|
||||||
|
* mixins: [confirmMixin, i18nMixin('page.xxx')],
|
||||||
|
* methods: {
|
||||||
|
* async handleDelete (row) {
|
||||||
|
* const cancelled = await this.$confirmAction(
|
||||||
|
* { message: this.key('confirm_delete'), title: this.key('tip') },
|
||||||
|
* () => deleteApi({ id: row.id })
|
||||||
|
* )
|
||||||
|
* if (cancelled) return
|
||||||
|
* this.$message.success(this.$t(this.key('operation_success')))
|
||||||
|
* this.fetchData()
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export const confirmMixin = {
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* @param {Object} confirmOpts - { message, title, ...$confirm 其余参数 }
|
||||||
|
* @param {Function} action - 确认后执行的 async 函数
|
||||||
|
* @returns {Promise<boolean>} true = 已取消, false = 已执行
|
||||||
|
*/
|
||||||
|
async $confirmAction (confirmOpts, action) {
|
||||||
|
try {
|
||||||
|
await this.$confirm(
|
||||||
|
confirmOpts.message ? this.$t(confirmOpts.message) : '',
|
||||||
|
confirmOpts.title ? this.$t(confirmOpts.title) : '',
|
||||||
|
{
|
||||||
|
confirmButtonText: this.$t(confirmOpts.confirmButtonText || 'page.common.confirm'),
|
||||||
|
cancelButtonText: this.$t(confirmOpts.cancelButtonText || 'page.common.cancel'),
|
||||||
|
type: confirmOpts.type || 'warning',
|
||||||
|
closeOnClickModal: confirmOpts.closeOnClickModal !== false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await action()
|
||||||
|
} catch {
|
||||||
|
// 拦截器已弹出 Message
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default confirmMixin
|
||||||
@@ -39,7 +39,8 @@ export function useTableButtons (options = {}, permissionCheck) {
|
|||||||
auth: btn.auth,
|
auth: btn.auth,
|
||||||
confirm: btn.confirm || false,
|
confirm: btn.confirm || false,
|
||||||
onClick: btn.onClick,
|
onClick: btn.onClick,
|
||||||
hasPermission: btn.auth ? check(btn.auth) : true
|
hasPermission: btn.auth ? check(btn.auth) : true,
|
||||||
|
visible: btn.visible || (() => true)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return { toolbarButtons, rowButtons }
|
return { toolbarButtons, rowButtons }
|
||||||
|
|||||||
@@ -4,7 +4,10 @@
|
|||||||
"page": {
|
"page": {
|
||||||
"common": {
|
"common": {
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
"use": "Apply"
|
"use": "Apply",
|
||||||
|
"confirm": "Confirm",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"tip": "Tip"
|
||||||
},
|
},
|
||||||
"demo": {
|
"demo": {
|
||||||
"playground": {
|
"playground": {
|
||||||
@@ -45,6 +48,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"system_administration": {
|
||||||
|
"user_management": {
|
||||||
|
"role": {
|
||||||
|
"search": "Search",
|
||||||
|
"reset": "Reset",
|
||||||
|
"index": "No.",
|
||||||
|
"name": "Role Name",
|
||||||
|
"status": "Status",
|
||||||
|
"enabled": "Enabled",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"description": "Description",
|
||||||
|
"actions": "Actions",
|
||||||
|
"add": "Add",
|
||||||
|
"edit": "Edit",
|
||||||
|
"delete": "Delete",
|
||||||
|
"add_title": "Add Role",
|
||||||
|
"edit_title": "Edit Role",
|
||||||
|
"enter_name": "Please enter role name",
|
||||||
|
"enter_description": "Please enter description",
|
||||||
|
"select_status": "Please select status",
|
||||||
|
"select_rows_first": "Please select rows first",
|
||||||
|
"operation_success": "Operation succeeded",
|
||||||
|
"confirm_delete": "Are you sure to delete this role?",
|
||||||
|
"length_limit": "Length should be 1 to 100 characters",
|
||||||
|
"assign_permissions": "Assign Permissions",
|
||||||
|
"permission_assignment": "Permission Assignment",
|
||||||
|
"confirm": "Confirm",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"tip": "Tip"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"login": {
|
"login": {
|
||||||
"time_is_most_precious": "Time is the most precious of all wealth",
|
"time_is_most_precious": "Time is the most precious of all wealth",
|
||||||
|
|||||||
@@ -4,7 +4,10 @@
|
|||||||
"page": {
|
"page": {
|
||||||
"common": {
|
"common": {
|
||||||
"help": "帮 助",
|
"help": "帮 助",
|
||||||
"use": "使用"
|
"use": "使用",
|
||||||
|
"confirm": "确定",
|
||||||
|
"cancel": "取消",
|
||||||
|
"tip": "提示"
|
||||||
},
|
},
|
||||||
"demo": {
|
"demo": {
|
||||||
"playground": {
|
"playground": {
|
||||||
@@ -45,6 +48,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"system_administration": {
|
||||||
|
"user_management": {
|
||||||
|
"role": {
|
||||||
|
"search": "查询",
|
||||||
|
"reset": "重置",
|
||||||
|
"index": "序号",
|
||||||
|
"name": "角色名称",
|
||||||
|
"status": "状态",
|
||||||
|
"enabled": "启用",
|
||||||
|
"disabled": "禁用",
|
||||||
|
"description": "描述",
|
||||||
|
"actions": "操作",
|
||||||
|
"add": "新 增",
|
||||||
|
"edit": "编 辑",
|
||||||
|
"delete": "删 除",
|
||||||
|
"add_title": "新增角色",
|
||||||
|
"edit_title": "编辑角色",
|
||||||
|
"enter_name": "请输入角色名称",
|
||||||
|
"enter_description": "请输入描述",
|
||||||
|
"select_status": "请选择状态",
|
||||||
|
"select_rows_first": "请先勾选要操作的数据",
|
||||||
|
"operation_success": "操作成功",
|
||||||
|
"confirm_delete": "确定要删除该角色吗?",
|
||||||
|
"length_limit": "长度在 1 到 100 个字符",
|
||||||
|
"assign_permissions": "分配权限",
|
||||||
|
"permission_assignment": "权限分配",
|
||||||
|
"confirm": "确定",
|
||||||
|
"cancel": "取消",
|
||||||
|
"tip": "提示"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"login": {
|
"login": {
|
||||||
"time_is_most_precious": "时间是一切财富中最宝贵的财富",
|
"time_is_most_precious": "时间是一切财富中最宝贵的财富",
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ export default {
|
|||||||
name: `${pre}index`,
|
name: `${pre}index`,
|
||||||
meta: { ...meta, title: '系统设置', root: '/system_settings' },
|
meta: { ...meta, title: '系统设置', root: '/system_settings' },
|
||||||
component: _import('system/function/module-index')
|
component: _import('system/function/module-index')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'user_management/role',
|
||||||
|
name: `${pre}user_management-role`,
|
||||||
|
meta: { ...meta, cache: true, title: '角色' },
|
||||||
|
component: _import('system-administration/user-management/role')
|
||||||
}
|
}
|
||||||
])('system_settings-')
|
])('system_settings-')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
import { useTableColumns } from '@/composables/useTableColumns'
|
import { useTableColumns } from '@/composables/useTableColumns'
|
||||||
import { useTableButtons } from '@/composables/useTableButtons'
|
import { useTableButtons } from '@/composables/useTableButtons'
|
||||||
import { i18nMixin } from '@/composables/useI18n'
|
import { i18nMixin } from '@/composables/useI18n'
|
||||||
|
import { confirmMixin } from '@/composables/useConfirmHandle'
|
||||||
import {
|
import {
|
||||||
getFactoryAreaList,
|
getFactoryAreaList,
|
||||||
createFactoryArea,
|
createFactoryArea,
|
||||||
@@ -82,7 +83,7 @@ import PageDialogForm from '@/components/page-dialog-form'
|
|||||||
export default {
|
export default {
|
||||||
name: 'production-master-data-factory-area',
|
name: 'production-master-data-factory-area',
|
||||||
components: { PageTable, PageDialogForm },
|
components: { PageTable, PageDialogForm },
|
||||||
mixins: [i18nMixin('page.production_master_data.factory_model.factory_area')],
|
mixins: [i18nMixin('page.production_master_data.factory_model.factory_area'), confirmMixin],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -195,8 +196,10 @@ export default {
|
|||||||
page_no: this.pagination.current,
|
page_no: this.pagination.current,
|
||||||
page_size: this.pagination.size
|
page_size: this.pagination.size
|
||||||
})
|
})
|
||||||
this.tableData = res.data || []
|
const list = Array.isArray(res) ? res : (res.data || [])
|
||||||
this.pagination.total = res.count || 0
|
const total = Array.isArray(res) ? res.length : (res.count || 0)
|
||||||
|
this.tableData = list
|
||||||
|
this.pagination.total = total
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
@@ -261,27 +264,20 @@ export default {
|
|||||||
this.resetForm()
|
this.resetForm()
|
||||||
},
|
},
|
||||||
async handleDelete (row) {
|
async handleDelete (row) {
|
||||||
try {
|
const cancelled = await this.$confirmAction(
|
||||||
await this.$confirm(
|
{
|
||||||
this.$t(this.key('confirm_delete')),
|
message: this.key('confirm_delete'),
|
||||||
this.$t(this.key('tip')),
|
title: this.key('tip')
|
||||||
{
|
},
|
||||||
confirmButtonText: this.$t(this.key('confirm')),
|
() => deleteFactoryArea({ id: [row.id] })
|
||||||
cancelButtonText: this.$t(this.key('cancel')),
|
)
|
||||||
type: 'warning',
|
if (cancelled) return
|
||||||
closeOnClickModal: false
|
this.$message.success(this.$t(this.key('operation_success')))
|
||||||
}
|
this.pagination.current = Math.min(
|
||||||
)
|
this.pagination.current,
|
||||||
await deleteFactoryArea({ id: [row.id] })
|
Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1
|
||||||
this.$message.success(this.$t(this.key('operation_success')))
|
)
|
||||||
this.pagination.current = Math.min(
|
this.fetchData()
|
||||||
this.pagination.current,
|
|
||||||
Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1
|
|
||||||
)
|
|
||||||
this.fetchData()
|
|
||||||
} catch (e) {
|
|
||||||
// 取消删除 / 请求失败时不处理
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer
|
||||||
|
:visible.sync="visibleProxy"
|
||||||
|
:title="$t(title)"
|
||||||
|
:size="width"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
direction="rtl"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<div class="perm-drawer__body" v-loading="treeLoading">
|
||||||
|
<el-tree
|
||||||
|
v-if="treeReady"
|
||||||
|
ref="permTree"
|
||||||
|
node-key="menu_id"
|
||||||
|
:data="treeData"
|
||||||
|
:props="{ label: 'name', children: 'children' }"
|
||||||
|
:default-expand-all="false"
|
||||||
|
:expand-on-click-node="false"
|
||||||
|
:check-strictly="true"
|
||||||
|
show-checkbox
|
||||||
|
@check="onTreeCheck"
|
||||||
|
>
|
||||||
|
<span slot-scope="{ node, data }" class="perm-drawer__tree-node">
|
||||||
|
<span :class="{ 'perm-drawer__tree-node--disabled': !data.status }">
|
||||||
|
<i v-if="data.icon" :class="`fa fa-${data.icon}`" />
|
||||||
|
<i v-else-if="data.children" :class="`el-icon-${node.expanded ? 'folder-opened' : 'folder'}`" />
|
||||||
|
<i v-else class="el-icon-document" />
|
||||||
|
{{ $t(data.name) }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</el-tree>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="perm-drawer__footer">
|
||||||
|
<el-button size="mini" @click="onCancel">
|
||||||
|
{{ $t(cancelText) }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" size="mini" :loading="submitting" @click="onSubmit">
|
||||||
|
{{ $t(confirmText) }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { i18nMixin } from '@/composables/useI18n'
|
||||||
|
import util from '@/libs/util'
|
||||||
|
import { getMenuAll } from '@/api/menu'
|
||||||
|
import { giveRoleMenu, getRoleMenu } from '@/api/system-administration/role'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RolePermDrawer',
|
||||||
|
mixins: [i18nMixin('page.system_administration.user_management.role')],
|
||||||
|
props: {
|
||||||
|
visible: { type: Boolean, default: false },
|
||||||
|
roleId: { type: Number, default: 0 },
|
||||||
|
title: { type: String, default: '' },
|
||||||
|
confirmText: { type: String, default: '' },
|
||||||
|
cancelText: { type: String, default: '' },
|
||||||
|
width: { type: String, default: '360px' }
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
treeLoading: false,
|
||||||
|
submitting: false,
|
||||||
|
treeReady: false,
|
||||||
|
treeData: [],
|
||||||
|
checkedMenuIds: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
visibleProxy: {
|
||||||
|
get () { return this.visible },
|
||||||
|
set (val) { this.$emit('update:visible', val) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visible (val) {
|
||||||
|
if (val) { this.loadData() }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadData () {
|
||||||
|
this.treeReady = false
|
||||||
|
this.treeLoading = true
|
||||||
|
try {
|
||||||
|
const [menuRes, roleMenuRes] = await Promise.all([
|
||||||
|
getMenuAll()
|
||||||
|
])
|
||||||
|
const menuData = Array.isArray(menuRes) ? menuRes : (menuRes.data || [])
|
||||||
|
const roleData = Array.isArray(roleMenuRes) ? roleMenuRes : (roleMenuRes.data || [])
|
||||||
|
this.checkedMenuIds = roleData.map(item => item.menu_id)
|
||||||
|
this.treeData = util.formatDataToTree(menuData)
|
||||||
|
this.treeReady = true
|
||||||
|
await this.$nextTick()
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50))
|
||||||
|
this.$refs.permTree.setCheckedKeys(this.checkedMenuIds)
|
||||||
|
} finally {
|
||||||
|
this.treeLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onTreeCheck () {},
|
||||||
|
onClose () {
|
||||||
|
this.treeReady = false
|
||||||
|
this.treeData = []
|
||||||
|
this.checkedMenuIds = []
|
||||||
|
},
|
||||||
|
onCancel () {
|
||||||
|
this.visibleProxy = false
|
||||||
|
},
|
||||||
|
async onSubmit () {
|
||||||
|
this.submitting = true
|
||||||
|
try {
|
||||||
|
const menuIds = this.$refs.permTree.getCheckedKeys()
|
||||||
|
await giveRoleMenu({ role_id: this.roleId, role_menu: menuIds })
|
||||||
|
this.$message.success(this.$t(this.key('operation_success')))
|
||||||
|
this.visibleProxy = false
|
||||||
|
this.$emit('saved')
|
||||||
|
} finally {
|
||||||
|
this.submitting = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.perm-drawer__body {
|
||||||
|
height: calc(100vh - 140px);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px 20px;
|
||||||
|
}
|
||||||
|
.perm-drawer__footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-top: 1px solid #e8e8e8;
|
||||||
|
background: #fff;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.perm-drawer__tree-node {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
.perm-drawer__tree-node--disabled {
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
365
src/views/system-administration/user-management/role/index.vue
Normal file
365
src/views/system-administration/user-management/role/index.vue
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
<template>
|
||||||
|
<d2-container>
|
||||||
|
<template #header>
|
||||||
|
<div class="search-bar">
|
||||||
|
<el-form :inline="true" size="mini">
|
||||||
|
<el-form-item :label="$t(key('name'))">
|
||||||
|
<el-input
|
||||||
|
v-model="search.name"
|
||||||
|
:placeholder="$t(key('enter_name'))"
|
||||||
|
clearable
|
||||||
|
style="width:200px"
|
||||||
|
@keyup.enter.native="onSearch"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t(key('status'))">
|
||||||
|
<el-select
|
||||||
|
v-model="search.status"
|
||||||
|
:placeholder="$t(key('select_status'))"
|
||||||
|
clearable
|
||||||
|
style="width:150px"
|
||||||
|
>
|
||||||
|
<el-option :value="'1'" :label="$t(key('enabled'))" />
|
||||||
|
<el-option :value="'0'" :label="$t(key('disabled'))" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" @click="onSearch">
|
||||||
|
{{ $t(key('search')) }}
|
||||||
|
</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" @click="onReset">
|
||||||
|
{{ $t(key('reset')) }}
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<page-table
|
||||||
|
ref="pageTable"
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:toolbar-buttons="toolbarButtons"
|
||||||
|
:row-buttons="rowButtons"
|
||||||
|
:pagination="pagination"
|
||||||
|
:table-attrs="{ selectable: row => row.system === 0 }"
|
||||||
|
auto-height
|
||||||
|
@page-change="onPageChange"
|
||||||
|
@selection-change="onSelect"
|
||||||
|
>
|
||||||
|
<template #col-status="{ row }">
|
||||||
|
<span v-if="row.status === 1" style="color: #67c23a;">
|
||||||
|
<i class="el-icon-circle-check" />
|
||||||
|
{{ $t(key('enabled')) }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #909399;">
|
||||||
|
<i class="el-icon-circle-close" />
|
||||||
|
{{ $t(key('disabled')) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</page-table>
|
||||||
|
|
||||||
|
<page-dialog-form
|
||||||
|
ref="dialogForm"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
:title="dialogTitle"
|
||||||
|
width="35%"
|
||||||
|
:form-cols="formCols"
|
||||||
|
:form-data="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="100px"
|
||||||
|
:submitting="submitting"
|
||||||
|
:confirm-text="key('confirm')"
|
||||||
|
:cancel-text="key('cancel')"
|
||||||
|
@submit="onDialogSubmit"
|
||||||
|
@close="onDialogClose"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<role-perm-drawer
|
||||||
|
:visible.sync="permVisible"
|
||||||
|
:role-id="permRole.id"
|
||||||
|
:title="key('assign_permissions')"
|
||||||
|
:confirm-text="key('confirm')"
|
||||||
|
:cancel-text="key('cancel')"
|
||||||
|
width="360px"
|
||||||
|
@saved="fetchData"
|
||||||
|
/>
|
||||||
|
</d2-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useTableColumns } from '@/composables/useTableColumns'
|
||||||
|
import { useTableButtons } from '@/composables/useTableButtons'
|
||||||
|
import { i18nMixin } from '@/composables/useI18n'
|
||||||
|
import { confirmMixin } from '@/composables/useConfirmHandle'
|
||||||
|
import {
|
||||||
|
getRoleList,
|
||||||
|
createRole,
|
||||||
|
editRole,
|
||||||
|
deleteRole,
|
||||||
|
updateRoleStatus
|
||||||
|
} from '@/api/system-administration/role'
|
||||||
|
import PageTable from '@/components/page-table'
|
||||||
|
import PageDialogForm from '@/components/page-dialog-form'
|
||||||
|
import RolePermDrawer from './components/PermDrawer/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'system-administration-role',
|
||||||
|
components: { PageTable, PageDialogForm, RolePermDrawer },
|
||||||
|
mixins: [i18nMixin('page.system_administration.user_management.role'), confirmMixin],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
submitting: false,
|
||||||
|
tableData: [],
|
||||||
|
selectedRows: [],
|
||||||
|
dialogVisible: false,
|
||||||
|
dialogTitle: '',
|
||||||
|
editId: '',
|
||||||
|
handleType: 'create',
|
||||||
|
search: { name: '', status: '' },
|
||||||
|
pagination: { current: 1, size: 10, total: 0 },
|
||||||
|
formData: { name: '', description: '', status: '1' },
|
||||||
|
rules: {
|
||||||
|
name: [
|
||||||
|
{ required: true, message: this.key('enter_name'), trigger: 'blur' },
|
||||||
|
{ min: 1, max: 100, message: this.key('length_limit'), trigger: 'blur' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
columns: [],
|
||||||
|
toolbarButtons: [],
|
||||||
|
rowButtons: [],
|
||||||
|
formCols: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
prop: 'name',
|
||||||
|
label: this.key('name'),
|
||||||
|
placeholder: this.key('enter_name'),
|
||||||
|
clearable: true,
|
||||||
|
style: { width: '90%' }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
inputType: 'textarea',
|
||||||
|
prop: 'description',
|
||||||
|
autosize: { minRows: 2, maxRows: 6 },
|
||||||
|
label: this.key('description'),
|
||||||
|
placeholder: this.key('enter_description'),
|
||||||
|
clearable: true,
|
||||||
|
style: { width: '90%' }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
prop: 'status',
|
||||||
|
label: this.key('status'),
|
||||||
|
clearable: false,
|
||||||
|
style: { width: '90%' },
|
||||||
|
options: [
|
||||||
|
{ value: '1', label: this.$t(this.key('enabled')) },
|
||||||
|
{ value: '0', label: this.$t(this.key('disabled')) }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
permVisible: false,
|
||||||
|
permRole: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.columns = useTableColumns([
|
||||||
|
{ prop: 'sort', label: this.key('index'), width: 80 },
|
||||||
|
{ prop: 'name', label: this.key('name'), minWidth: 120 },
|
||||||
|
{ prop: 'status', label: this.key('status'), slot: 'status', width: 100 },
|
||||||
|
{ prop: 'description', label: this.key('description') },
|
||||||
|
{ prop: '_actions', label: this.key('actions'), width: 220, fixed: 'right' }
|
||||||
|
])
|
||||||
|
const btns = useTableButtons({
|
||||||
|
toolbar: [
|
||||||
|
{
|
||||||
|
key: 'add',
|
||||||
|
label: this.key('add'),
|
||||||
|
icon: 'el-icon-plus',
|
||||||
|
type: 'primary',
|
||||||
|
auth: '/system_settings/user_management/role/create',
|
||||||
|
onClick: this.openAdd
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'enable',
|
||||||
|
label: this.key('enabled'),
|
||||||
|
icon: 'el-icon-check',
|
||||||
|
type: 'success',
|
||||||
|
auth: '/system_settings/user_management/role/enable',
|
||||||
|
onClick: () => this.batchUpdateStatus(1)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'disable',
|
||||||
|
label: this.key('disabled'),
|
||||||
|
icon: 'el-icon-close',
|
||||||
|
type: 'warning',
|
||||||
|
auth: '/system_settings/user_management/role/disable',
|
||||||
|
onClick: () => this.batchUpdateStatus(0)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
row: [
|
||||||
|
{
|
||||||
|
key: 'edit',
|
||||||
|
label: this.key('edit'),
|
||||||
|
icon: 'el-icon-edit',
|
||||||
|
auth: '/system_settings/user_management/role/edit',
|
||||||
|
visible: row => row.system !== 1,
|
||||||
|
onClick: this.openEdit
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'assign',
|
||||||
|
label: this.key('assign_permissions'),
|
||||||
|
icon: 'el-icon-edit-outline',
|
||||||
|
auth: '/system_settings/user_management/role/role_give',
|
||||||
|
visible: row => row.system !== 1,
|
||||||
|
onClick: this.openPermDialog
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
label: this.key('delete'),
|
||||||
|
icon: 'el-icon-delete',
|
||||||
|
color: 'danger',
|
||||||
|
auth: '/system_settings/user_management/role/delete',
|
||||||
|
visible: row => row.system !== 1,
|
||||||
|
onClick: this.handleDelete
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, this.$permission)
|
||||||
|
this.toolbarButtons = btns.toolbarButtons
|
||||||
|
this.rowButtons = btns.rowButtons
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchData () {
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const res = await getRoleList({
|
||||||
|
...this.search,
|
||||||
|
page_no: this.pagination.current,
|
||||||
|
page_size: this.pagination.size
|
||||||
|
})
|
||||||
|
const list = Array.isArray(res) ? res : (res.data || [])
|
||||||
|
const total = Array.isArray(res) ? res.length : (res.count || 0)
|
||||||
|
this.tableData = list
|
||||||
|
this.pagination.total = total
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSearch () {
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
onReset () {
|
||||||
|
this.search = { name: '', status: '' }
|
||||||
|
this.pagination.current = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
onPageChange (page) {
|
||||||
|
this.pagination.current = page.current
|
||||||
|
this.pagination.size = page.size
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
onSelect (rows) {
|
||||||
|
this.selectedRows = rows
|
||||||
|
},
|
||||||
|
resetForm () {
|
||||||
|
this.formData = { name: '', description: '', status: '1' }
|
||||||
|
this.editId = ''
|
||||||
|
},
|
||||||
|
openAdd () {
|
||||||
|
this.handleType = 'create'
|
||||||
|
this.dialogTitle = this.key('add_title')
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.dialogForm && this.$refs.dialogForm.reset()
|
||||||
|
this.resetForm()
|
||||||
|
this.dialogVisible = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openEdit (row) {
|
||||||
|
this.handleType = 'edit'
|
||||||
|
this.dialogTitle = this.key('edit_title')
|
||||||
|
this.editId = row.id
|
||||||
|
this.formData = {
|
||||||
|
name: row.name,
|
||||||
|
description: row.description || '',
|
||||||
|
status: String(row.status)
|
||||||
|
}
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
async onDialogSubmit () {
|
||||||
|
this.submitting = true
|
||||||
|
try {
|
||||||
|
if (this.handleType === 'create') {
|
||||||
|
await createRole(this.formData)
|
||||||
|
} else {
|
||||||
|
await editRole({ ...this.formData, id: this.editId })
|
||||||
|
}
|
||||||
|
this.$message.success(this.$t(this.key('operation_success')))
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.fetchData()
|
||||||
|
} finally {
|
||||||
|
this.submitting = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDialogClose () {
|
||||||
|
this.resetForm()
|
||||||
|
},
|
||||||
|
async batchUpdateStatus (status) {
|
||||||
|
const rows = this.selectedRows.filter(row => row.system === 0)
|
||||||
|
if (rows.length === 0) {
|
||||||
|
this.$message.warning(this.$t(this.key('select_rows_first')))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await updateRoleStatus({
|
||||||
|
ids: rows.map(row => row.id),
|
||||||
|
status
|
||||||
|
})
|
||||||
|
this.$message.success(this.$t(this.key('operation_success')))
|
||||||
|
this.fetchData()
|
||||||
|
} catch {
|
||||||
|
// 拦截器已处理
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async handleDelete (row) {
|
||||||
|
const cancelled = await this.$confirmAction(
|
||||||
|
{
|
||||||
|
message: this.key('confirm_delete'),
|
||||||
|
title: this.key('tip')
|
||||||
|
},
|
||||||
|
() => deleteRole({ id: [row.id] })
|
||||||
|
)
|
||||||
|
if (cancelled) return
|
||||||
|
this.$message.success(this.$t(this.key('operation_success')))
|
||||||
|
this.pagination.current = Math.min(
|
||||||
|
this.pagination.current,
|
||||||
|
Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1
|
||||||
|
)
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
async openPermDialog (row) {
|
||||||
|
this.permRole = row
|
||||||
|
this.permVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.search-bar {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
/deep/ .el-form-item--mini.el-form-item {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user