mirror of
https://github.com/dataease/dataease.git
synced 2026-06-16 11:21:44 +08:00
refactor(X-Pack): 对权限体系进行重构-2
This commit is contained in:
14
.mcp.json
Normal file
14
.mcp.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"dbhub": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"@bytebase/dbhub@latest",
|
||||
"--config",
|
||||
"./dbhub.toml"
|
||||
],
|
||||
"env": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
143
CLAUDE.md
Normal file
143
CLAUDE.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
DataEase is an open-source BI tool by FIT2Cloud for data analysis and visualization via drag-and-drop chart creation. Version 3.0.0, GPL v3 license.
|
||||
|
||||
**Branch:** `dev-v3` (current), PRs target `dev-v2`
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Backend:** Java 21, Spring Boot 3.3.x, JPA (Hibernate) + QueryDSL, JDK 21
|
||||
- **Frontend:** Vue 3, TypeScript, Vite, Element Plus, ECharts, AntV (G2, L7, S2)
|
||||
- **DB:** MySQL 8 (primary), also PostgreSQL, Oracle, SQL Server, DM, Kingbase, GreatSQL, H2 (desktop)
|
||||
- **Other:** Knife4j (API docs), EhCache, Quartz
|
||||
|
||||
## Module Structure
|
||||
|
||||
```
|
||||
dataease/
|
||||
├── core/
|
||||
│ ├── core-backend/ # Spring Boot app (main artifact: CoreApplication.jar)
|
||||
│ └── core-frontend/ # Vue 3 SPA
|
||||
├── sdk/ # Shared SDK (common, api, extensions, distributed)
|
||||
├── de-xpack/ # Enterprise/premium features (separate git repo)
|
||||
├── drivers/ # JDBC drivers
|
||||
└── installer/ # Docker Compose, deployment scripts
|
||||
```
|
||||
|
||||
## Backend Domain Structure
|
||||
|
||||
Each business domain under `core/core-backend/src/main/java/io/dataease/<domain>/`:
|
||||
|
||||
```
|
||||
<domain>/
|
||||
├── dao/auto/entity/ # JPA @Entity classes
|
||||
├── auto/repository/ # JpaRepository interfaces
|
||||
├── dao/ext/po/ # QueryDSL projection POJOs
|
||||
├── dao/ext/mapper/ # Custom repository interfaces
|
||||
├── dto/ # Data Transfer Objects
|
||||
├── bo/ # Business Objects
|
||||
├── manage/ # Business logic (@Component)
|
||||
├── server/ # API implementations (implements sdk interfaces)
|
||||
├── request/ # Request body classes
|
||||
└── utils/ # Domain utilities
|
||||
```
|
||||
|
||||
## Key Commands
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
# Full build (all modules)
|
||||
mvn clean install
|
||||
|
||||
# Build community standalone JAR
|
||||
cd core && mvn clean package -Pstandalone -U -Dmaven.test.skip=true
|
||||
# Output: core/core-backend/target/CoreApplication.jar
|
||||
|
||||
# Build desktop edition
|
||||
cd core && mvn clean package -Pdesktop -U -Dmaven.test.skip=true
|
||||
|
||||
# Build enterprise/distributed edition
|
||||
cd core && mvn clean package -Pdistributed -U -Dmaven.test.skip=true
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
```bash
|
||||
# Direct JAR execution
|
||||
java -jar core/core-backend/target/CoreApplication.jar
|
||||
|
||||
# Docker (recommended for testing)
|
||||
curl -sSL https://dataease.oss-cn-hangzhou.aliyuncs.com/quick_start_v2.sh | bash
|
||||
# Default credentials: admin / DataEase@123456
|
||||
|
||||
# Local installer
|
||||
cd installer && /bin/bash install.sh
|
||||
```
|
||||
|
||||
### Frontend Dev
|
||||
|
||||
```bash
|
||||
cd core/core-frontend
|
||||
npm run dev # Dev server
|
||||
npm run build:distributed # Production build
|
||||
```
|
||||
|
||||
### QueryDSL Regeneration
|
||||
|
||||
```bash
|
||||
mvn compile # Regenerates Q-classes in target/generated-sources/java/
|
||||
```
|
||||
|
||||
## Build Profiles
|
||||
|
||||
| Profile | Database | Description |
|
||||
|---------|----------|-------------|
|
||||
| `standalone` (default) | MySQL | Community/single-server |
|
||||
| `desktop` | H2 | Desktop/lightweight |
|
||||
| `distributed` | MySQL | Enterprise/multi-node |
|
||||
|
||||
Database-specific profiles: `standalone-oracle`, `standalone-pg`, `standalone-dm`, `standalone-kingbase`, `standalone-sqlserver`, `standalone-GreatSQL`
|
||||
|
||||
## Critical Dev Conventions
|
||||
|
||||
### JPA > MyBatis-Plus
|
||||
|
||||
MyBatis-Plus has been replaced by JPA. **Do not use MyBatis-Plus patterns.**
|
||||
|
||||
- Use `jakarta.persistence.*` (not `javax.persistence.*`)
|
||||
- Entities: `@Getter`/`@Setter` via Lombok (not `@Data`), `@Comment` on table and every field
|
||||
- IDs: `Long`, no `@GeneratedValue` — IDs generated manually via `SnowflakeUtil`
|
||||
- Repositories: `JpaRepository<Xxx, Long>`, `JpaSpecificationExecutor<Xxx>`
|
||||
|
||||
### JpaUpdateNonNullAspect
|
||||
|
||||
Intercepts all `save`/`saveAndFlush` calls:
|
||||
- If entity exists (non-null ID), **only non-null fields are copied** before saving — mimics MyBatis-Plus `updateById`
|
||||
- To intentionally set a field to null: use QueryDSL `@Modifying` + JPQL, `save()` cannot do this
|
||||
|
||||
### Multi-DB Compatibility
|
||||
|
||||
All QueryDSL/JPQL queries **must work across all supported databases** (MySQL, Oracle, PostgreSQL, SQL Server, DM, Kingbase, GreatSQL). Avoid database-specific dialect functions; branch per-dialect when necessary (see `DynamicCaseNamingStrategy`).
|
||||
|
||||
### Layering
|
||||
|
||||
- **server/** = API endpoint implementation, implements interfaces from `sdk` module
|
||||
- **manage/** = business logic, `@Component`, called by server
|
||||
- **dao/auto/** = JPA entities + repositories
|
||||
- **dao/ext/** = custom query POJOs and non-standard repositories
|
||||
|
||||
### ID Generation
|
||||
|
||||
Use `SnowflakeUtil` for all new entity IDs.
|
||||
|
||||
## Architecture Notes
|
||||
|
||||
- **Profile-based feature toggling:** Three Maven profiles control which modules are included (community vs enterprise)
|
||||
- **Frontend bundled into JAR:** `maven-antrun-plugin` copies frontend dist into backend `static/`
|
||||
- **Schema management:** Flyway disabled, `ddl-auto: update` manages schema in dev and production
|
||||
- **xpack independence:** de-xpack is a separate git repo; core-backend optionally depends on it for debugging
|
||||
@@ -97,6 +97,9 @@ public class MenuManage {
|
||||
if (coreMenu.getId().equals(21L)) return false;
|
||||
return coreMenu.getId().equals(7L)
|
||||
|| coreMenu.getPid().equals(7L)
|
||||
|| coreMenu.getId().equals(9L)
|
||||
|| coreMenu.getId().equals(111L)
|
||||
|| coreMenu.getId().equals(112L)
|
||||
|| coreMenu.getId().equals(14L)
|
||||
|| coreMenu.getId().equals(17L)
|
||||
|| coreMenu.getId().equals(18L)
|
||||
|
||||
@@ -18,6 +18,8 @@ i18n_menu.screen=Data Screen
|
||||
i18n_menu.dataset=Dataset
|
||||
i18n_menu.datasource=Data Source
|
||||
i18n_menu.user=User Management
|
||||
i18n_menu.sysuser=User Management
|
||||
i18n_menu.sysauth=Permission Configuration
|
||||
i18n_menu.org=Organization Management
|
||||
i18n_menu.auth=Permission Configuration
|
||||
i18n_menu.report=Scheduled Report
|
||||
@@ -149,6 +151,7 @@ i18n_template_recent=Recently Used
|
||||
i18n_default_org=Default Organization
|
||||
i18n_org_admin=Organization Admin
|
||||
i18n_ordinary_role=Ordinary User
|
||||
i18n_org_analyst=Data Analyst
|
||||
i18n_sys_admin=System Admin
|
||||
|
||||
i18n_threshold_logic_eq=Equal to
|
||||
|
||||
@@ -17,6 +17,8 @@ i18n_menu.screen=\u6570\u636E\u5927\u5C4F
|
||||
i18n_menu.dataset=\u6570\u636E\u96C6
|
||||
i18n_menu.datasource=\u6570\u636E\u6E90
|
||||
i18n_menu.user=\u7528\u6237\u7BA1\u7406
|
||||
i18n_menu.sysuser=\u7528\u6237\u7BA1\u7406
|
||||
i18n_menu.sysauth=\u6743\u9650\u914D\u7F6E
|
||||
i18n_menu.org=\u7EC4\u7EC7\u7BA1\u7406
|
||||
i18n_menu.auth=\u6743\u9650\u914D\u7F6E
|
||||
i18n_menu.report=\u5B9A\u65F6\u62A5\u544A
|
||||
@@ -148,6 +150,7 @@ i18n_template_recent=\u6700\u8FD1\u4F7F\u7528
|
||||
i18n_default_org=\u9ED8\u8BA4\u7EC4\u7EC7
|
||||
i18n_org_admin=\u7EC4\u7EC7\u7BA1\u7406\u5458
|
||||
i18n_ordinary_role=\u666E\u901A\u7528\u6237
|
||||
i18n_org_analyst=\u6570\u636E\u5206\u6790\u5E08
|
||||
i18n_sys_admin=\u7CFB\u7EDF\u7BA1\u7406\u5458
|
||||
|
||||
i18n_threshold_logic_eq=\u7B49\u4E8E
|
||||
|
||||
@@ -17,6 +17,8 @@ i18n_menu.screen=\u6578\u64DA\u5927\u5C4F
|
||||
i18n_menu.dataset=\u6578\u64DA\u96C6
|
||||
i18n_menu.datasource=\u6578\u64DA\u6E90
|
||||
i18n_menu.user=\u7528\u6236\u7BA1\u7406
|
||||
i18n_menu.sysuser=\u7528\u6236\u7BA1\u7406
|
||||
i18n_menu.sysauth=\u6B0A\u9650\u914D\u7F6E
|
||||
i18n_menu.org=\u7D44\u7E54\u7BA1\u7406
|
||||
i18n_menu.auth=\u6B0A\u9650\u914D\u7F6E
|
||||
i18n_menu.report=\u5B9A\u6642\u5831\u544A
|
||||
@@ -148,6 +150,7 @@ i18n_template_recent=\u6700\u8FD1\u4F7F\u7528
|
||||
i18n_default_org=\u9ED8\u8A8D\u7D44\u7E54
|
||||
i18n_org_admin=\u7D44\u7E54\u7BA1\u7406\u54E1
|
||||
i18n_ordinary_role=\u666E\u901A\u7528\u6236
|
||||
i18n_org_analyst=\u6578\u64DA\u5206\u6790\u5E2B
|
||||
i18n_sys_admin=\u7CFB\u7D71\u7BA1\u7406\u54E1
|
||||
|
||||
i18n_threshold_logic_eq=\u7B49\u65BC
|
||||
|
||||
3
core/core-frontend/src/assets/svg/icon_ban_filled.svg
Normal file
3
core/core-frontend/src/assets/svg/icon_ban_filled.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.99992 0.333252C3.76574 0.333252 0.333252 3.76574 0.333252 7.99992C0.333252 12.2341 3.76574 15.6666 7.99992 15.6666C12.2341 15.6666 15.6666 12.2341 15.6666 7.99992C15.6666 3.76574 12.2341 0.333252 7.99992 0.333252ZM12.6514 11.2374L4.76246 3.34847C5.68019 2.70844 6.79587 2.33325 7.99992 2.33325C11.1295 2.33325 13.6666 4.87031 13.6666 7.99992C13.6666 9.20397 13.2914 10.3196 12.6514 11.2374ZM2.33325 7.99992C2.33325 6.79599 2.70837 5.68041 3.34829 4.76272L11.2371 12.6516C10.3194 13.2915 9.20385 13.6666 7.99992 13.6666C4.87031 13.6666 2.33325 11.1295 2.33325 7.99992Z" fill="#8F959E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 701 B |
@@ -9,6 +9,8 @@ interface TreeConfig {
|
||||
showCheckbox: boolean
|
||||
checkOnClickNode: boolean
|
||||
placeholder: string
|
||||
multiple: boolean
|
||||
clearable: boolean
|
||||
}
|
||||
const props = defineProps({
|
||||
optionList: propTypes.arrayOf(
|
||||
@@ -20,7 +22,11 @@ const props = defineProps({
|
||||
})
|
||||
),
|
||||
title: propTypes.string,
|
||||
property: Object as PropType<TreeConfig>
|
||||
property: Object as PropType<TreeConfig>,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const { property } = toRefs(props)
|
||||
@@ -30,7 +36,9 @@ const treeConfig = computed(() => {
|
||||
checkStrictly: false,
|
||||
showCheckbox: true,
|
||||
checkOnClickNode: true,
|
||||
placeholder: t('user.role')
|
||||
placeholder: t('user.role'),
|
||||
multiple: true,
|
||||
clearable: false
|
||||
},
|
||||
property.value
|
||||
)
|
||||
@@ -39,12 +47,17 @@ const treeConfig = computed(() => {
|
||||
|
||||
const state = reactive({
|
||||
currentStatus: [],
|
||||
currentStatusSingle: null as any,
|
||||
activeStatus: []
|
||||
})
|
||||
|
||||
const emits = defineEmits(['filter-change'])
|
||||
const filterTree = ref()
|
||||
const treeChange = () => {
|
||||
if (!treeConfig.value.multiple) {
|
||||
emits('filter-change', state.currentStatusSingle ? [state.currentStatusSingle] : [])
|
||||
return
|
||||
}
|
||||
const nodes = state.currentStatus.map(id => {
|
||||
return filterTree.value?.getNode(id).data
|
||||
})
|
||||
@@ -59,6 +72,7 @@ const optionListNotSelect = computed(() => {
|
||||
})
|
||||
const clear = () => {
|
||||
state.currentStatus = []
|
||||
state.currentStatusSingle = null
|
||||
}
|
||||
watch(
|
||||
() => state.currentStatus,
|
||||
@@ -69,6 +83,12 @@ watch(
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => state.currentStatusSingle,
|
||||
() => {
|
||||
treeChange()
|
||||
}
|
||||
)
|
||||
defineExpose({
|
||||
clear
|
||||
})
|
||||
@@ -79,6 +99,7 @@ defineExpose({
|
||||
<span>{{ title }}</span>
|
||||
<div class="filter-item">
|
||||
<el-tree-select
|
||||
v-if="treeConfig.multiple"
|
||||
node-key="value"
|
||||
ref="filterTree"
|
||||
:teleported="false"
|
||||
@@ -86,13 +107,31 @@ defineExpose({
|
||||
v-model="state.currentStatus"
|
||||
:data="optionListNotSelect"
|
||||
:highlight-current="true"
|
||||
:disabled="disabled"
|
||||
multiple
|
||||
clearable
|
||||
:render-after-expand="false"
|
||||
:placeholder="$t('common.please_select') + treeConfig.placeholder"
|
||||
:show-checkbox="treeConfig.showCheckbox"
|
||||
:check-strictly="treeConfig.checkStrictly"
|
||||
:check-on-click-node="treeConfig.checkOnClickNode"
|
||||
/>
|
||||
<el-tree-select
|
||||
v-else
|
||||
node-key="value"
|
||||
ref="filterTree"
|
||||
:teleported="false"
|
||||
style="width: 100%"
|
||||
v-model="state.currentStatusSingle"
|
||||
:data="optionListNotSelect"
|
||||
:highlight-current="true"
|
||||
:disabled="disabled"
|
||||
:clearable="treeConfig.clearable"
|
||||
:render-after-expand="false"
|
||||
:placeholder="$t('common.please_select') + treeConfig.placeholder"
|
||||
:check-strictly="treeConfig.checkStrictly"
|
||||
:check-on-click-node="treeConfig.checkOnClickNode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -44,6 +44,13 @@ const cleanrInnerValue = (index: number) => {
|
||||
state.conditions[i].value = []
|
||||
}
|
||||
}
|
||||
// For tree-select in single-select mode, also clear single value
|
||||
if (componentList.value[index]?.type === 'tree-select') {
|
||||
const prop = componentList.value[index]?.property
|
||||
if (prop && prop.multiple === false) {
|
||||
state.conditions = state.conditions.filter(c => c.field !== field)
|
||||
}
|
||||
}
|
||||
}
|
||||
const clearInnerTag = (index?: number) => {
|
||||
if (isNaN(index)) {
|
||||
@@ -79,11 +86,16 @@ const filterChange = (value, field, operator) => {
|
||||
exits = true
|
||||
condition['value'] = value
|
||||
}
|
||||
if (!condition?.value?.length) {
|
||||
const val = condition?.value
|
||||
const isEmpty = Array.isArray(val)
|
||||
? !val.length
|
||||
: val === null || val === undefined || val === ''
|
||||
if (isEmpty) {
|
||||
state.conditions.splice(len, 1)
|
||||
}
|
||||
}
|
||||
if (!exits && value?.length) {
|
||||
const hasValue = Array.isArray(value) ? value.length : value != null && value !== ''
|
||||
if (!exits && hasValue) {
|
||||
state.conditions.push({ field, value, operator })
|
||||
}
|
||||
treeFilterChange(value, field, operator)
|
||||
@@ -129,6 +141,7 @@ defineExpose({
|
||||
:option-list="component.option"
|
||||
:title="component.title"
|
||||
:property="component.property"
|
||||
:disabled="component.disabled"
|
||||
@filter-change="v => filterChange(v, component.field, 'in')"
|
||||
/>
|
||||
<drawer-filter
|
||||
|
||||
@@ -125,7 +125,7 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-table" :class="tableData && !tableData.length && 'no-data'">
|
||||
<div class="flex-table" :class="!tableData?.length && 'no-data'">
|
||||
<el-table
|
||||
ref="table"
|
||||
:border="border"
|
||||
|
||||
@@ -478,6 +478,7 @@ export default {
|
||||
user: 'User',
|
||||
role: 'Role',
|
||||
addUser: '@:common.add @:system.user',
|
||||
selected_count: 'Selected {0} members',
|
||||
click_to_show: 'Click to show',
|
||||
click_to_hide: 'Click to hide',
|
||||
basic_settings: 'Basic settings',
|
||||
@@ -820,6 +821,17 @@ export default {
|
||||
data_import_failed: 'Some data import failed',
|
||||
data_import_failed_de: 'Data import failed'
|
||||
},
|
||||
sysuser: {
|
||||
sys_user_management: 'System User Management',
|
||||
search_placeholder: 'Search name, account, email',
|
||||
org: 'Organization',
|
||||
user_source: 'Source',
|
||||
sys_variable: 'System Variable',
|
||||
add_sys_user: 'Add System User',
|
||||
edit_sys_user: 'Edit System User',
|
||||
account_placeholder: 'Please enter account',
|
||||
name_placeholder: 'Please enter name'
|
||||
},
|
||||
userimport: {
|
||||
buttonText: 'Batch import',
|
||||
dialogTitle: 'Batch upload',
|
||||
@@ -909,7 +921,18 @@ export default {
|
||||
move_resource_first: 'Migrate resources first',
|
||||
default_parent_tips: '(Default current organization)',
|
||||
admin_parent_tips: '(Default root organization)',
|
||||
please_login_per_changed: 'Current user permissions have changed, please log in again'
|
||||
please_login_per_changed: 'Current user permissions have changed, please log in again',
|
||||
add_member: 'Add Member',
|
||||
select_member: 'Select Members',
|
||||
remove_member: 'Remove Member',
|
||||
remove_member_confirm: 'Are you sure you want to remove this member from the organization?',
|
||||
member_role: 'Member Role',
|
||||
members: 'members',
|
||||
selected_count: 'Selected {0} members',
|
||||
select_org_first: 'Please select an organization from the left panel',
|
||||
search_name_account: 'Search name or account',
|
||||
select_member_role: 'Please select member role',
|
||||
no_selected_member: 'No members selected'
|
||||
},
|
||||
auth: {
|
||||
permission_configuration: 'Permission Configuration',
|
||||
|
||||
@@ -464,6 +464,7 @@ export default {
|
||||
user: '使用者',
|
||||
role: '角色',
|
||||
addUser: '@:common.add@:system.user',
|
||||
selected_count: '已選 {0} 人',
|
||||
click_to_show: '點選顯示',
|
||||
click_to_hide: '點選隱藏',
|
||||
basic_settings: '基礎設定',
|
||||
@@ -879,7 +880,18 @@ export default {
|
||||
move_resource_first: '先遷移資源',
|
||||
default_parent_tips: '(預設目前組織)',
|
||||
admin_parent_tips: '(預設根組織)',
|
||||
please_login_per_changed: '目前使用者權限已變更,請重新登入'
|
||||
please_login_per_changed: '目前使用者權限已變更,請重新登入',
|
||||
add_member: '新增成員',
|
||||
select_member: '選擇成員',
|
||||
remove_member: '移除成員',
|
||||
remove_member_confirm: '確定將該成員移出目前組織嗎?',
|
||||
member_role: '成員角色',
|
||||
members: '人',
|
||||
selected_count: '已選 {0} 人',
|
||||
select_org_first: '請先在左側選擇組織',
|
||||
search_name_account: '搜尋姓名、帳號',
|
||||
select_member_role: '請選擇成員角色',
|
||||
no_selected_member: '暫無已選成員'
|
||||
},
|
||||
auth: {
|
||||
permission_configuration: '權限配置',
|
||||
|
||||
@@ -465,6 +465,7 @@ export default {
|
||||
user: '用户',
|
||||
role: '角色',
|
||||
addUser: '@:common.add@:system.user',
|
||||
selected_count: '已选 {0} 人',
|
||||
click_to_show: '点击显示',
|
||||
click_to_hide: '点击隐藏',
|
||||
basic_settings: '基础设置',
|
||||
@@ -795,6 +796,17 @@ export default {
|
||||
data_import_failed: '部分数据导入失败',
|
||||
data_import_failed_de: '数据导入失败'
|
||||
},
|
||||
sysuser: {
|
||||
sys_user_management: '系统用户管理',
|
||||
search_placeholder: '搜索姓名、账号、邮箱',
|
||||
org: '所在组织',
|
||||
user_source: '用户来源',
|
||||
sys_variable: '系统变量',
|
||||
add_sys_user: '添加系统用户',
|
||||
edit_sys_user: '编辑系统用户',
|
||||
account_placeholder: '请输入账号',
|
||||
name_placeholder: '请输入姓名'
|
||||
},
|
||||
userimport: {
|
||||
buttonText: '批量导入',
|
||||
dialogTitle: '批量上传',
|
||||
@@ -881,7 +893,18 @@ export default {
|
||||
move_resource_first: '先迁移资源',
|
||||
default_parent_tips: '(默认当前组织)',
|
||||
admin_parent_tips: '(默认根组织)',
|
||||
please_login_per_changed: '当前用户权限已变更,请重新登录'
|
||||
please_login_per_changed: '当前用户权限已变更,请重新登录',
|
||||
add_member: '添加成员',
|
||||
select_member: '选择成员',
|
||||
remove_member: '移除成员',
|
||||
remove_member_confirm: '确定将该成员移出当前组织吗?',
|
||||
member_role: '成员角色',
|
||||
members: '人',
|
||||
selected_count: '已选 {0} 人',
|
||||
select_org_first: '请先在左侧选择组织',
|
||||
search_name_account: '搜索姓名、账号',
|
||||
select_member_role: '请选择成员角色',
|
||||
no_selected_member: '暂无已选成员'
|
||||
},
|
||||
auth: {
|
||||
permission_configuration: '权限配置',
|
||||
|
||||
@@ -121,7 +121,6 @@ router.beforeEach(async (to, from, next) => {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
let roleRouters = (await getRoleRouters()) || []
|
||||
if (isDesktop) {
|
||||
roleRouters = roleRouters.filter(item => item.name !== 'system')
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<template>
|
||||
<p class="router-title">{{ t('commons.system_parameter_setting') }}</p>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane v-for="item in tabArray" :key="item.name" :label="item.label" :name="item.name" />
|
||||
</el-tabs>
|
||||
@@ -51,20 +50,10 @@ const addTable = tab => {
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.router-title {
|
||||
color: #1f2329;
|
||||
font-feature-settings: 'clig' off, 'liga' off;
|
||||
font-family: var(--de-custom_font, 'PingFang');
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
}
|
||||
.sys-setting-p {
|
||||
width: 100%;
|
||||
height: calc(100vh - 176px);
|
||||
height: calc(100vh - 148px);
|
||||
box-sizing: border-box;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.container-sys-param {
|
||||
|
||||
8
dbhub.toml
Normal file
8
dbhub.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[[sources]]
|
||||
id = "dashboard_mysql"
|
||||
type = "mysql"
|
||||
host = "localhost"
|
||||
port = 3306
|
||||
user = "root"
|
||||
password = "Password123@mysql"
|
||||
database = "dataease_v3"
|
||||
@@ -0,0 +1,51 @@
|
||||
package io.dataease.api.permissions.org.api;
|
||||
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
|
||||
import io.dataease.api.permissions.org.dto.SysOrgMemberAddRequest;
|
||||
import io.dataease.api.permissions.org.dto.SysOrgMemberRequest;
|
||||
import io.dataease.api.permissions.org.dto.SysOrgMemberRoleSwitchRequest;
|
||||
import io.dataease.api.permissions.org.dto.SysOrgRoleOptionRequest;
|
||||
import io.dataease.api.permissions.org.vo.SysOrgMemberVO;
|
||||
import io.dataease.api.permissions.org.vo.SysOrgRoleOptionVO;
|
||||
import io.dataease.auth.DeApiPath;
|
||||
import io.dataease.model.KeywordRequest;
|
||||
import io.dataease.result.PageResult;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.dataease.constant.AuthResourceEnum.ORG;
|
||||
|
||||
@Tag(name = "系统组织管理")
|
||||
@ApiSupport(order = 887, author = "fit2cloud-someone")
|
||||
@DeApiPath(value = "/sys/org", rt = ORG)
|
||||
public interface SysOrgApi {
|
||||
|
||||
@Operation(summary = "分页查询组织成员")
|
||||
@PostMapping("/member/page")
|
||||
PageResult<SysOrgMemberVO> memberPage(@RequestBody SysOrgMemberRequest request);
|
||||
|
||||
@Operation(summary = "查询候选用户(不属于任何组织的用户)")
|
||||
@PostMapping("/member/candidates")
|
||||
List<SysOrgMemberVO> memberCandidates(@RequestBody KeywordRequest request);
|
||||
|
||||
@Operation(summary = "查询组织可选角色列表")
|
||||
@PostMapping("/member/roleOptions")
|
||||
List<SysOrgRoleOptionVO> memberRoleOptions(@RequestBody SysOrgRoleOptionRequest request);
|
||||
|
||||
@Operation(summary = "添加组织成员")
|
||||
@PostMapping("/member/add")
|
||||
void addMembers(@RequestBody SysOrgMemberAddRequest request);
|
||||
|
||||
@Operation(summary = "移除组织成员")
|
||||
@PostMapping("/member/remove/{userId}/{orgId}")
|
||||
void removeMember(@PathVariable("userId") Long userId, @PathVariable("orgId") Long orgId);
|
||||
|
||||
@Operation(summary = "切换成员角色")
|
||||
@PostMapping("/member/switchRole")
|
||||
void switchRole(@RequestBody SysOrgMemberRoleSwitchRequest request);
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "组织构造器")
|
||||
@Data
|
||||
@@ -18,4 +19,6 @@ public class OrgCreator implements Serializable {
|
||||
private String name;
|
||||
@Schema(description = "上级ID")
|
||||
private Long pid;
|
||||
@Schema(description = "组织管理员用户ID列表")
|
||||
private List<Long> adminIds;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.dataease.api.permissions.org.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "组织成员添加请求")
|
||||
@Data
|
||||
public class SysOrgMemberAddRequest implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long orgId;
|
||||
|
||||
@Schema(description = "用户ID列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<Long> userIds;
|
||||
|
||||
@Schema(description = "角色ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long roleId;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.dataease.api.permissions.org.dto;
|
||||
|
||||
import io.dataease.model.KeywordRequest;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Schema(description = "组织成员查询请求")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class SysOrgMemberRequest extends KeywordRequest implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long orgId;
|
||||
|
||||
@Schema(description = "当前页码")
|
||||
private Long currentPage = 1L;
|
||||
|
||||
@Schema(description = "每页大小")
|
||||
private Long pageSize = 10L;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.dataease.api.permissions.org.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "组织成员角色切换请求")
|
||||
@Data
|
||||
public class SysOrgMemberRoleSwitchRequest implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long orgId;
|
||||
|
||||
@Schema(description = "角色ID列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<Long> roleIds;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.dataease.api.permissions.org.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Intermediate DTO for raw query rows in memberPage (one row = user + one role)
|
||||
*/
|
||||
@Data
|
||||
public class SysOrgMemberRowDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
private String account;
|
||||
private String name;
|
||||
private String email;
|
||||
private Long roleId;
|
||||
private String roleName;
|
||||
private Boolean enable;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.dataease.api.permissions.org.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Schema(description = "组织角色选项请求")
|
||||
@Data
|
||||
public class SysOrgRoleOptionRequest implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long orgId;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package io.dataease.api.permissions.org.vo;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "组织成员VO")
|
||||
@Data
|
||||
public class SysOrgMemberVO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "用户ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "账号")
|
||||
private String account;
|
||||
|
||||
@Schema(description = "姓名")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "角色列表")
|
||||
private List<RoleItem> roles;
|
||||
|
||||
@Schema(description = "用户状态")
|
||||
private Boolean enable;
|
||||
|
||||
@Data
|
||||
public static class RoleItem implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "角色ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long roleId;
|
||||
|
||||
@Schema(description = "角色名称")
|
||||
private String roleName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.dataease.api.permissions.org.vo;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Schema(description = "组织角色选项VO")
|
||||
@Data
|
||||
public class SysOrgRoleOptionVO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "角色ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "角色名称")
|
||||
private String name;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "角色过滤器")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@@ -17,4 +18,8 @@ public class RoleRequest extends KeywordRequest {
|
||||
private static final long serialVersionUID = 7354856549096378406L;
|
||||
@Schema(description = "用户ID")
|
||||
private Long uid;
|
||||
@Schema(description = "组织ID")
|
||||
private Long oid;
|
||||
@Schema(description = "组织ID列表(支持多选)")
|
||||
private List<Long> oidList;
|
||||
}
|
||||
|
||||
@@ -35,4 +35,6 @@ public class UserCreator implements Serializable {
|
||||
private Boolean mfaEnable = false;
|
||||
@Schema(description = "系统变量")
|
||||
private List<SysVariableValueItem> variables;
|
||||
@Schema(description = "所属组织ID")
|
||||
private Long defaultOid;
|
||||
}
|
||||
|
||||
@@ -16,5 +16,7 @@ public class UserGridRequest extends KeywordRequest implements Serializable {
|
||||
|
||||
private List<Long> roleIdList;
|
||||
|
||||
private Long oid;
|
||||
|
||||
private Boolean timeDesc;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ public class UserFormVO implements Serializable {
|
||||
@JsonSerialize(using= ToStringSerializer.class)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "所属组织ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long defaultOid;
|
||||
|
||||
@Schema(description = "账号")
|
||||
private String account;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user