Merge pull request #2655 from dataease/pr@dev_user_interactive_optimization

Pr@dev user interactive optimization
This commit is contained in:
dataeaseShu 2022-07-21 16:06:32 +08:00 committed by GitHub
commit 2e7ac18786
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 2413 additions and 849 deletions

View File

@ -1,5 +1,6 @@
module.exports = {
presets: [
'@vue/app'
]
],
plugins: ['@babel/plugin-proposal-optional-chaining']
}

View File

@ -76,6 +76,7 @@
},
"devDependencies": {
"@babel/core": "^7.4.0-0",
"@babel/plugin-proposal-optional-chaining": "^7.18.6",
"@babel/register": "7.0.0",
"@vue/cli-plugin-babel": "3.6.0",
"@vue/cli-plugin-eslint": "^3.9.1",

View File

@ -11,7 +11,6 @@
<script>
import { uuid } from 'vue-uuid'
import { get } from '@/api/system/dynamic'
export default {
name: 'AsyncComponent',
inheritAttrs: true,
@ -43,8 +42,49 @@ export default {
window.SyncComponentCache = {}
}
let res
console.log(1, this.$route);
const urlMap = {
'system-role': 'SystemRole',
'system-dept': 'SystemDept',
'system-auth': 'SystemAuth',
'email-task': 'TaskEmail',
'system-dept-form': 'SystemDeptFormn',
'system-role-form': 'SystemRoleForm',
'email-task-form': 'TaskEmailForm',
'system-ukey': 'SystemUkey'
}
// if (this.$route.name === 'system-role') {
// this.mode = () => import('de-plugins/src/views/xpack/role/index.vue');
// return
// }
// if (this.$route.name === 'system-role-form') {
// this.mode = () => import('de-plugins/src/views/xpack/role/form.vue');
// return
// }
// if (this.$route.name === 'dataset') {
// this.mode = () => import('de-plugins/src/views/xpack/dataset/rowpermissions/rowPermissions.vue');
// return
// }
// if (this.$route.name === 'system-dept') {
// this.mode = () => import('de-plugins/src/views/xpack/dept/index.vue');
// return
// }
// if (this.$route.name === 'system-auth') {
// this.mode = () => import('de-plugins/src/views/xpack/auth/index.vue');
// return
// }
if (!window.SyncComponentCache[this.url]) {
window.SyncComponentCache[this.url] = get(this.url)
if (urlMap[this.$route.name]) {
window.SyncComponentCache[this.url] = get(`/static/${urlMap[this.$route.name]}.js`)
}
// window.SyncComponentCache[this.url] = get(this.url)
// window.SyncComponentCache[this.url] = Axios.get(this.url)
res = await window.SyncComponentCache[this.url]
@ -52,8 +92,10 @@ export default {
res = await window.SyncComponentCache[this.url]
}
console.log(1, res)
const Fn = Function
this.mode = new Fn(`return ${res.data || res}`)()
this.mode = new Fn(`return ${res}`)()
/* if (res && res.data) {
const Fn = Function
this.mode = new Fn(`return ${res.data || res}`)()

View File

@ -1,5 +1,5 @@
<template>
<el-icon name="back" class="back-button" @click.native="jump" />
<i class="el-icon-arrow-left back-button" @click="jump" />
</template>
<script>
@ -34,7 +34,7 @@ export default {
.back-button {
cursor: pointer;
margin-right: 10px;
margin-right: 18px;
font-weight: 600;
&:active {

View File

@ -0,0 +1,82 @@
<template>
<div class="layout-container">
<p class="route-title">
<back-button v-if="showBack" :path="backPath" :name="backName" :to="backTo" />
<span>{{ routeTitle }}</span>
</p>
<div class="container-wrapper" :class="[isDept ? 'dept-padding' : '']">
<slot />
</div>
</div>
</template>
<script>
import BackButton from '@/components/back-button'
export default {
name: 'DeLayoutContent',
components: { BackButton },
props: {
// eslint-disable-next-line vue/require-default-prop
header: String,
// eslint-disable-next-line vue/require-default-prop
backPath: String,
// eslint-disable-next-line vue/require-default-prop
backName: String,
// eslint-disable-next-line vue/require-default-prop
backTo: Object
},
computed: {
routeTitle() {
return this.header || this.$route.meta?.title || ''
},
showBack({ backPath, backName, backTo }) {
return backPath || backName || backTo
},
isDept() {
return ['system-dept', 'system-dept-form'].includes(this.$route.name)
}
}
}
</script>
<style lang="scss" scoped>
.layout-container {
transition: 0.3s;
background-color: var(--ContentBG);
overflow: auto;
padding: 20px;
border-radius: 4px;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 14%);
box-sizing: border-box;
background: #f5f6f7;
overflow: hidden;
padding: 24px 24px 24px 24px;
height: 100%;
display: flex;
flex-direction: column;
.route-title {
font-family: PingFang SC;
font-size: 20px;
font-weight: 500;
line-height: 28px;
text-align: left;
color: #1F2329;
width: 100%;
margin: 0;
}
.container-wrapper {
width: 100%;
overflow: auto;
background: #fff;
margin-top: 24px;
padding: 24px;
flex: 1;
}
.dept-padding {
padding: 0;
margin-top: 16px;
}
}
</style>

View File

@ -5,7 +5,7 @@
:style="{'margin-left': !asideHidden ? 0 : '-' + currentWidth}"
>
<slot />
<de-horizontal-drag-bar v-if="showDragBar" :type="type" />
<de-horizontal-drag-bar v-if="isSystem" :type="type" />
</el-aside>
</template>
@ -20,6 +20,10 @@ export default {
type: String,
default: '260px'
},
isCollapseWidth: {
type: String,
default: ''
},
enableAsideHidden: {
type: Boolean,
default: true
@ -28,6 +32,10 @@ export default {
type: Boolean,
default: true
},
isTemplate: {
type: Boolean,
default: false
},
type: {
type: String,
default: null
@ -40,7 +48,11 @@ export default {
},
computed: {
currentWidth() {
return this.type && getLayout(this.type) || this.width
return this.isCollapseWidth || this.type && getLayout(this.type) || this.width
},
isSystem() {
//
return this.isTemplate || (!this.$route.fullPath.includes('system') && this.showDragBar)
}
}
}
@ -50,15 +62,19 @@ export default {
.ms-aside-container {
/* border: 1px solid #E6E6E6; */
padding: 10px;
border-radius: 2px;
box-sizing: border-box;
background-color: var(--SiderBG, #FFF);
height: calc(100vh - 56px);
border-right: 0px;
position: relative;
padding-bottom: 50px;
}
/* .collapse-style {
height: calc(100vh - 56px);
} */
.hiddenBottom {
width: 8px;
height: 50px;

View File

@ -0,0 +1,223 @@
<template>
<div class="flex-table">
<el-table
ref="table"
v-bind="$attrs"
v-on="tableEvent"
height="2000"
:data="tableData"
:style="{ width: '100%' }"
>
<table-body :columns="columns">
<slot></slot>
</table-body>
<slot name="__operation"></slot>
</el-table>
<div class="pagination-cont">
<el-pagination
background
v-bind="paginationDefalut"
v-on="paginationEvent"
>
</el-pagination>
</div>
</div>
</template>
<script>
import tableBody from "./tableBody";
export default {
components: { tableBody },
props: {
columns: {
type: Array,
default: () => [],
},
multipleSelection: {
type: Array,
default: () => [],
},
pagination: {
type: Object,
default: () => {},
},
isRememberSelected: {
type: Boolean,
default: false,
},
selectedFlags: {
type: String,
default: "id",
},
tableData: {
type: Array,
default: () => [],
},
},
data() {
return {
paginationEvent: {},
paginationDefalut: {
currentPage: 1,
pageSizes: [10, 20, 30, 40],
pageSize: 10,
layout: "total, prev, pager, next, sizes, jumper",
total: 0,
},
multipleSelectionCach: [],
tableEvent: {},
};
},
created() {
this.handleListeners();
},
computed: {
multipleSelectionAll() {
return [...this.multipleSelectionCach, ...this.multipleSelection];
},
},
watch: {
pagination: {
handler() {
this.paginationDefalut = {
...this.paginationDefalut,
...this.pagination,
};
},
deep: true,
immediate: true,
},
tableData: {
handler() {
this.$nextTick(() => {
this.$refs.table.doLayout();
});
if (!this.isRememberSelected) return;
// SelectionChange this.multipleSelection
const multipleSelection = [...this.multipleSelection];
this.$nextTick(() => {
this.handlerSelected(multipleSelection);
});
},
deep: true,
},
columns: {
handler() {
this.$nextTick(() => {
this.$refs.table.doLayout();
});
},
deep: true,
},
},
methods: {
handlerSelected(multipleSelection) {
this.multipleSelectionCach = [
...this.multipleSelectionCach,
...multipleSelection,
];
const flags = this.multipleSelectionCach.map(
(ele) => ele[this.selectedFlags]
);
//
const notCurrenArr = [];
this.tableData.forEach((ele) => {
const resultIndex = flags.indexOf(ele[this.selectedFlags]);
if (resultIndex !== -1) {
this.$refs.table.toggleRowSelection(ele, true);
notCurrenArr.push(resultIndex);
}
});
notCurrenArr.sort().reduceRight((pre, next) => {
this.multipleSelectionCach.splice(next, 1);
}, 0);
},
handleListeners() {
Object.keys(this.$listeners).forEach((key) => {
if (
[
"size-change",
"current-change",
"prev-click",
"next-click",
].includes(key)
) {
this.paginationEvent[key] = this.$listeners[key];
} else {
this.tableEvent[key] = this.$listeners[key];
}
});
},
},
};
</script>
<style lang="scss" scoped>
.flex-table {
display: flex;
height: 100%;
flex-direction: column;
justify-content: space-between;
::v-deep .el-table__header-wrapper {
background-color: #f5f6f7;
// border-top: 1px solid rgba(31, 35, 41, 0.15);
}
::v-deep .el-table__fixed-header-wrapper {
th {
background-color: var(--TableBG, #f5f6f7) !important;
}
}
::v-deep .el-table__fixed-body-wrapper {
tr {
background-color: var(--TableBG, #ffffff) !important;
}
}
.pagination-cont {
text-align: right;
margin-top: 10px;
::v-deep .el-pager li {
background-color: #fff;
border: 1px solid #bbbfc4;
border-radius: 4px;
color: #1f2329;
box-sizing: border-box;
line-height: 26px;
font-family: SF Pro Text;
font-size: 14px;
font-weight: 400;
}
::v-deep .btn-prev,
::v-deep .btn-next {
background: #fff;
background-color: #fff;
border: 1px solid #bbbfc4;
border-radius: 4px;
color: #bbbfc4;
}
::v-deep .el-pagination__total {
font-family: "PingFang SC";
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
color: #1f2329;
line-height: 28px;
}
::v-deep .number.active,
::v-deep .el-input__inner:hover {
border-color: #3370ff;
color: #3370ff !important;
background-color: #fff !important;
}
::v-deep .el-icon-more {
border: none !important;
}
}
}
</style>

View File

@ -0,0 +1,24 @@
<script>
export default {
name: "TableBody",
functional: true,
props: {
columns: {
type: Array,
default: () => [],
},
},
render(h, context) {
const nodes = [];
const { columns } = context.props;
const { children = [] } = context;
if (!columns?.length) return children;
children.forEach(ele => {
if (columns.includes(ele.componentOptions.propsData.prop)) {
nodes.push(ele)
}
})
return nodes;
},
};
</script>

View File

@ -1 +1,4 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1618217742324" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7205" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 64C264.96 64 64 264.96 64 512s200.96 448 448 448 448-200.96 448-448S759.04 64 512 64z m0 831.712c-211.584 0-383.712-172.16-383.712-383.712 0-211.584 172.128-383.712 383.712-383.712 211.552 0 383.712 172.128 383.712 383.712 0 211.552-172.16 383.712-383.712 383.712z" p-id="7206"></path><path d="M671.968 512H512V288.064c0-17.76-14.24-32.128-32-32.128s-32 14.4-32 32.128V544c0 17.76 14.272 32 32 32h191.968c17.76 0 32.128-14.24 32.128-32s-14.368-32-32.128-32z" p-id="7207"></path></svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.83301 1.33301C5.10915 1.33301 5.33301 1.55687 5.33301 1.83301V1.99967H10.6663V1.83301C10.6663 1.55687 10.8902 1.33301 11.1663 1.33301H11.4997C11.7758 1.33301 11.9997 1.55687 11.9997 1.83301V1.99967H13.9997C14.3679 1.99967 14.6663 2.29815 14.6663 2.66634V13.9997C14.6663 14.3679 14.3679 14.6663 13.9997 14.6663H1.99967C1.63148 14.6663 1.33301 14.3679 1.33301 13.9997L1.33301 2.66634C1.33301 2.29815 1.63148 1.99967 1.99967 1.99967H3.99967V1.83301C3.99967 1.55687 4.22353 1.33301 4.49967 1.33301H4.83301ZM10.6663 3.33301H5.33301V3.49967C5.33301 3.77582 5.10915 3.99967 4.83301 3.99967H4.49967C4.22353 3.99967 3.99967 3.77582 3.99967 3.49967V3.33301H2.66634V13.333H13.333V3.33301H11.9997V3.49967C11.9997 3.77582 11.7758 3.99967 11.4997 3.99967H11.1663C10.8902 3.99967 10.6663 3.77582 10.6663 3.49967V3.33301ZM5.99967 6.83301C5.99967 6.55687 5.77582 6.33301 5.49967 6.33301H4.49967C4.22353 6.33301 3.99967 6.55687 3.99967 6.83301V7.83301C3.99967 8.10915 4.22353 8.33301 4.49967 8.33301H5.49967C5.77582 8.33301 5.99967 8.10915 5.99967 7.83301V6.83301ZM6.99967 6.83301C6.99967 6.55687 7.22353 6.33301 7.49967 6.33301H8.49967C8.77582 6.33301 8.99967 6.55687 8.99967 6.83301V7.83301C8.99967 8.10915 8.77582 8.33301 8.49967 8.33301H7.49967C7.22353 8.33301 6.99967 8.10915 6.99967 7.83301V6.83301ZM5.99967 9.83301C5.99967 9.55687 5.77582 9.33301 5.49967 9.33301H4.49967C4.22353 9.33301 3.99967 9.55687 3.99967 9.83301V10.833C3.99967 11.1092 4.22353 11.333 4.49967 11.333H5.49967C5.77582 11.333 5.99967 11.1092 5.99967 10.833V9.83301ZM6.99967 9.83301C6.99967 9.55687 7.22353 9.33301 7.49967 9.33301H8.49967C8.77582 9.33301 8.99967 9.55687 8.99967 9.83301V10.833C8.99967 11.1092 8.77582 11.333 8.49967 11.333H7.49967C7.22353 11.333 6.99967 11.1092 6.99967 10.833V9.83301ZM11.9997 6.83301C11.9997 6.55687 11.7758 6.33301 11.4997 6.33301H10.4997C10.2235 6.33301 9.99967 6.55687 9.99967 6.83301V7.83301C9.99967 8.10915 10.2235 8.33301 10.4997 8.33301H11.4997C11.7758 8.33301 11.9997 8.10915 11.9997 7.83301V6.83301Z" fill="#3370FF"/>
<path d="M11.9997 9.83301C11.9997 9.55687 11.7758 9.33301 11.4997 9.33301H10.4997C10.2235 9.33301 9.99967 9.55687 9.99967 9.83301V10.833C9.99967 11.1092 10.2235 11.333 10.4997 11.333H11.4997C11.7758 11.333 11.9997 11.1092 11.9997 10.833V9.83301Z" fill="#3370FF"/>
</svg>

Before

Width:  |  Height:  |  Size: 867 B

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -131,9 +131,13 @@ export default {
default_login: 'Normal'
},
commons: {
manage_member: 'Managing members',
user_confirm_remove_cancel:'Are you sure you want to remove the user from the role?',
confirm_remove_cancel: 'Are you sure to delete the role?',
default_value: 'Default Value',
params_value: 'Param Value',
publish: 'publish',
input_role_name: 'Enter a role name',
unpublished: 'unpublished',
default_pwd: 'Default Pwd',
stop: 'Stop',
@ -159,6 +163,7 @@ export default {
button: 'Button',
man: 'Man',
woman: 'Woman',
keep_secret: 'keep secret',
nick_name: 'Name',
confirmPassword: 'Confirm Password',
upload: 'Upload',
@ -210,6 +215,8 @@ export default {
member: 'Member',
email: 'Email',
phone: 'Phone',
mobile_phone: 'Please enter your mobile phone number',
mobile_phone_number: 'Phone number',
role: 'Role',
personal_info: 'Personal Info',
api_keys: 'API Keys',
@ -503,7 +510,7 @@ export default {
please_choose_member: 'Please choose member',
search_by_name: 'Search by name',
modify_personal_info: 'Modify personal info',
edit_password: 'Edit Password',
edit_password: 'Reset Password',
edit_information: 'Edit Information',
input_name: 'Input Name',
input_email: 'Input Email',
@ -525,6 +532,12 @@ export default {
modify: 'Modify User',
input_name: 'Please enter user name',
input_id: 'Please enter user ID',
id_mandatory: 'id is mandatory',
name_mandatory: 'name is mandatory',
email_mandatory: 'email is mandatory',
role_mandatory: 'role is mandatory',
phone_format: 'mobile phone number format is incorrect',
select_gender: 'Please select gender',
input_email: 'Please input email',
input_password: 'Please input a password',
input_phone: 'Please enter the phone number',
@ -536,7 +549,8 @@ export default {
apikey_delete_confirm: 'Confirm to delete this API key?',
input_id_placeholder: 'Please enter ID (Chinese is not supported)',
source: 'User Source',
choose_org: 'Choose Organization',
choose_org: 'Please select a role',
choose_role: '请选择角色',
reset_password: 'Reset Password',
current_user: 'Current User',
origin_passwd: 'Origin Password',
@ -601,7 +615,20 @@ export default {
confirm_delete: 'Confirm delete role ',
role_name: 'Role Name ',
search_by_name: 'Search by name',
pls_input_name: 'please input name'
pls_input_name: 'please input name',
search_by_name_email: 'Search by name or email',
api_role: 'API role',
role_exist: 'Failed to add, the role already exists',
add_api_role: 'Add API role',
can_not_move: `Can't be removed, keep at least one administrator`,
manage_can_not_move: 'Administrator is a preset role of the system. By default, he has all the permissions of system management and cannot be deleted',
manage_can_not_update: 'Administrator is a preset role of the system. By default, he has all the permissions of system management and cannot be edit',
role_name: 'Role name',
role_description: 'Role description',
editer_role: 'Edit role',
add_role: 'Add role',
role_name_exist: 'The role name already exists',
search_by_role: 'Search by role name',
},
menu: {
parent_category: 'Parent Category',
@ -636,7 +663,26 @@ export default {
select_organization: 'Please select organization',
search_by_name: 'Search by name',
special_characters_are_not_supported: 'Format error (special characters are not supported and cannot start and end with \'-\')',
select: 'Select organization'
select: 'Select organization',
member: 'member',
organization: 'organization',
add_user: 'Add user',
search_by_name: 'Search by organization name',
sure_move_user: 'Are you sure to remove this user from the organization?',
move_success: 'Removed successfully',
user: 'user',
add_organization: 'Add organization',
defalut_organization_canot_move: 'The default organization cannot be deleted',
organization_name: 'Organization name',
input_organization_name: 'Please enter the organization name',
relate_top_organization: 'Associated parent organization',
organization_name_exist: 'Organization name already exists',
canot_delete: 'Cannot delete',
remove_user_first: 'Please remove all users in the organization before deleting the organization',
sure_delete_organization: 'Are you sure to delete this organization?',
delete: 'delete',
add_child_org: 'Add sub organization',
edite_organization: 'Edit organization',
},
system_parameter_setting: {
mailbox_service_settings: 'Mail Settings',
@ -1834,6 +1880,7 @@ export default {
dept: 'Dept',
role: 'Role',
user: 'User',
set_rules: 'Set rules',
sysParams_type: {
user_id: 'User ID',
user_name: 'User Name',
@ -1857,7 +1904,34 @@ export default {
view: 'View',
use: 'Use',
export: 'Export',
manage: 'Manage'
manage: 'Manage',
row_column: 'Row and column permission settings',
row_permission: 'Row permission rules',
enable_row: 'Enable row permissions',
add_condition: 'Add condition',
add_relationship: 'Add relationship',
white_list: 'White list',
white_user_not: 'The above permission rules do not take effect for white list users',
organization_or_role: 'Please select an organization or role',
column_permission: 'Column permission rule',
enable_column: 'Enable column permissions',
search_by_field: 'Search by field name',
add_condition: 'Add condition',
add_relationship: 'Add relationship',
filter_fields: 'Filter fields',
selct_filter_fields: 'Please select a filter field',
enter_keywords: 'Please enter keywords',
screen_method: 'Screening method',
select: 'Please select',
fixed_value: 'Fixed value',
defalut_method: 'Default condition',
select_all: 'Select all',
added: 'Added',
manual_input: 'Manual input',
please_fill: 'Please fill in one line and add 500 at most. Duplicate options and added options will be automatically filtered when identifying and entering',
close: 'close',
add: 'add to',
sure: 'determine',
},
about: {
auth_to: 'Authorized to',

View File

@ -131,9 +131,13 @@ export default {
default_login: '普通登錄'
},
commons: {
manage_member: '管理成員',
user_confirm_remove_cancel: '確定將該用戶從角色中移除嗎?',
confirm_remove_cancel: '確定刪除該角色嗎?',
default_value: '默認值',
params_value: '参数值',
publish: '發布',
input_role_name: '請輸入角色名稱',
unpublished: '取消發布',
default_pwd: '初始密碼',
stop: '停止',
@ -159,6 +163,7 @@ export default {
gender: '性別',
man: '男',
woman: '女',
keep_secret: '保密',
nick_name: '姓名',
confirmPassword: '確認密碼',
upload: '上傳',
@ -210,6 +215,8 @@ export default {
member: '成員',
email: '郵箱',
phone: '電話',
mobile_phone: '請輸入手機號',
mobile_phone_number: '手機號碼',
role: '角色',
personal_info: '個人信息',
api_keys: 'API Keys',
@ -503,7 +510,7 @@ export default {
please_choose_member: '請選擇成員',
search_by_name: '根據名稱搜索',
modify_personal_info: '修改個人信息',
edit_password: '修改密碼',
edit_password: '重置密码',
edit_information: '編輯信息',
input_name: '請輸入名稱',
input_email: '請輸入郵箱',
@ -525,6 +532,12 @@ export default {
modify: '修改用戶',
input_name: '請輸入用戶姓名',
input_id: '請輸入ID',
id_mandatory: 'ID為必填',
name_mandatory: '姓名為必填',
email_mandatory: '郵箱為必填',
role_mandatory: '角色為必填',
phone_format: '手機號碼格式錯誤',
select_gender: '請選擇性別',
input_email: '請輸入郵箱',
input_password: '請輸入密碼',
input_phone: '請輸入電話號碼',
@ -537,7 +550,7 @@ export default {
apikey_delete_confirm: '這個 API Key 確定要刪除嗎?',
input_id_placeholder: '請輸入ID (不支持中文)',
source: '用戶來源',
choose_org: '選擇組織',
choose_org: '選擇組織',
reset_password: '重置密碼',
current_user: '當前用戶',
origin_passwd: '原始密碼',
@ -603,7 +616,20 @@ export default {
confirm_delete: '確認刪除角色 ',
role_name: '角色名稱',
search_by_name: '按名稱搜索',
pls_input_name: '請輸入名稱'
pls_input_name: '請輸入名稱',
search_by_name_email: '通過姓名或郵箱搜索',
api_role: 'API角色',
role_exist: '添加失敗,該角色已存在',
add_api_role: '添加API角色',
can_not_move: '不可移除,至少保留一位管理員',
manage_can_not_move: '管理員是系統預置角色,默認擁有系統管理全部權限,無法刪除',
manage_can_not_update: '管理員是系統預置角色,默認擁有系統管理全部權限,無法編輯',
role_name: '角色名稱',
role_description: '角色描述',
editer_role: '編輯角色',
add_role: '添加角色',
role_name_exist: '該角色名稱已存在',
search_by_role: '通過角色名稱搜索',
},
menu: {
parent_category: '上級目錄',
@ -638,7 +664,26 @@ export default {
select_organization: '請選擇組織',
search_by_name: '根據名稱搜索',
special_characters_are_not_supported: '格式錯誤(不支持特殊字符,且不能以\'-\'開頭結尾)',
select: '選擇組織'
select: '選擇組織',
member: '成員',
organization: '組織',
add_user: '添加用戶',
search_by_name: '通過組織名稱搜索',
sure_move_user: '確定將該用戶從組織中移除嗎?',
move_success: '移除成功',
user: '用戶',
add_organization: '添加組織',
defalut_organization_canot_move: '默認組織無法刪除',
organization_name: '組織名稱',
input_organization_name: '請輸入組織名稱',
relate_top_organization: '關聯上級組織',
organization_name_exist: '組織名稱已存在',
canot_delete: '無法刪除',
remove_user_first: '請先移除組織中所有用戶,再進行刪除組織操作。',
sure_delete_organization: '確定刪除該組織嗎?',
delete: '刪除',
add_child_org: '添加子組織',
edite_organization: '編輯組織',
},
system_parameter_setting: {
mailbox_service_settings: '郵件設置',
@ -1844,6 +1889,7 @@ export default {
dept: '組織',
role: '角色',
user: '用戶',
set_rules: '設置規則',
sysParams: '系統變量',
sysParams_type: {
user_id: '用戶ID',
@ -1869,7 +1915,34 @@ export default {
view: '查看',
use: '使用',
export: '導出',
manage: '管理'
manage: '管理',
row_column: '行列權限設置',
row_permission: '行權限規則',
enable_row: '啟用行權限',
add_condition: '添加條件',
add_relationship: '添加關系',
white_list: '白名單',
white_user_not: '以上權限規則對白名單用戶不生效',
organization_or_role: '請選擇組織或角色',
column_permission: '列權限規則',
enable_column: '啟用列權限',
search_by_field: '通過字段名稱搜索',
add_condition: '添加條件',
add_relationship: '添加關系',
filter_fields: '篩選字段',
selct_filter_fields: '請選擇篩選字段',
enter_keywords: '請輸關鍵字',
screen_method: '篩選方式',
select: '請選擇',
fixed_value: '固定值',
defalut_method: '默認條件',
select_all: '全 選',
added: '已添加',
manual_input: '手工輸入',
please_fill: '請一行填一個最多添加500個,識別錄入時會自動過濾重復的選項和已經添加過的選項',
close: '關 閉',
add: '添 加',
sure: '確 定',
},
about: {
auth_to: '授權給',

View File

@ -131,8 +131,12 @@ export default {
default_login: '普通登录'
},
commons: {
manage_member: '管理成员',
confirm_remove_cancel: '确定删除该角色吗?',
user_confirm_remove_cancel:'确定将该用户从角色中移除吗?',
default_value: '默认值',
params_value: '参数值',
input_role_name: '请输入角色名称',
publish: '发布',
unpublished: '取消发布',
default_pwd: '初始密码',
@ -159,6 +163,7 @@ export default {
gender: '性别',
man: '男',
woman: '女',
keep_secret: '保密',
nick_name: '姓名',
confirmPassword: '确认密码',
upload: '上传',
@ -211,6 +216,8 @@ export default {
member: '成员',
email: '邮箱',
phone: '电话',
mobile_phone: '请输入手机号',
mobile_phone_number: '手机号码',
role: '角色',
personal_info: '个人信息',
api_keys: 'API Keys',
@ -504,7 +511,7 @@ export default {
please_choose_member: '请选择成员',
search_by_name: '根据名称搜索',
modify_personal_info: '修改个人信息',
edit_password: '修改密码',
edit_password: '重置密码',
edit_information: '编辑信息',
input_name: '请输入名称',
input_email: '请输入邮箱',
@ -522,15 +529,21 @@ export default {
no_such_user: '无此用户信息, 请输入正确的用户 ID 或者 用户邮箱!'
},
user: {
create: '新建用户',
modify: '修改用户',
input_name: '请输入用户姓名',
create: '添加用户',
modify: '编辑用户',
input_name: '请输入姓名',
input_id: '请输入ID',
id_mandatory: 'ID为必填',
name_mandatory: '姓名为必填',
email_mandatory: '邮箱为必填',
role_mandatory: '角色为必填',
phone_format: '手机号码格式错误',
input_email: '请输入邮箱',
input_password: '请输入密码',
input_phone: '请输入电话号码',
input_roles: '请选择角色',
select_users: '请选择用户',
select_gender: '请选择性别',
special_characters_are_not_supported: '不支持特殊字符',
mobile_number_format_is_incorrect: '手机号码格式不正确',
email_format_is_incorrect: '邮箱格式不正确',
@ -538,7 +551,7 @@ export default {
apikey_delete_confirm: '这个 API Key 确定要删除吗?',
input_id_placeholder: '请输入ID (不支持中文)',
source: '用户来源',
choose_org: '选择组织',
choose_org: '选择组织',
reset_password: '重置密码',
current_user: '当前用户',
origin_passwd: '原始密码',
@ -604,7 +617,20 @@ export default {
confirm_delete: '确认删除角色 ',
role_name: '角色名称',
search_by_name: '按名称搜索',
pls_input_name: '请输入名称'
pls_input_name: '请输入名称',
search_by_name_email: '通过姓名或邮箱搜索',
api_role: 'API角色',
role_exist: '添加失败,该角色已存在',
add_api_role: '添加API角色',
can_not_move: '不可移除,至少保留一位管理员',
manage_can_not_move: '管理员是系统预置角色,默认拥有系统管理全部权限,无法删除',
manage_can_not_update: '管理员是系统预置角色,默认拥有系统管理全部权限,无法编辑',
role_name: '角色名称',
role_description: '角色描述',
editer_role: '编辑角色',
add_role: '添加角色',
role_name_exist: '该角色名称已存在',
search_by_role: '通过角色名称搜索',
},
menu: {
parent_category: '上级目录',
@ -639,7 +665,26 @@ export default {
select_organization: '请选择组织',
search_by_name: '根据名称搜索',
special_characters_are_not_supported: '格式错误(不支持特殊字符,且不能以\'-\'开头结尾)',
select: '选择组织'
select: '选择组织',
member: '成员',
organization: '组织',
add_user: '添加用户',
search_by_name: '通过组织名称搜索',
sure_move_user: '确定将该用户从组织中移除吗?',
move_success: '移除成功',
user: '用户',
add_organization: '添加组织',
defalut_organization_canot_move: '默认组织无法删除',
organization_name: '组织名称',
input_organization_name: '请输入组织名称',
relate_top_organization: '关联上级组织',
organization_name_exist: '组织名称已存在',
canot_delete: '无法删除',
remove_user_first: '请先移除组织中所有用户,再进行删除组织操作。',
sure_delete_organization: '确定删除该组织吗?',
delete: '删除',
add_child_org: '添加子组织',
edite_organization: '编辑组织',
},
system_parameter_setting: {
mailbox_service_settings: '邮件设置',
@ -1853,6 +1898,7 @@ export default {
dept: '组织',
role: '角色',
user: '用户',
set_rules: '设置规则',
sysParams: '系统变量',
sysParams_type: {
user_id: '用户ID',
@ -1878,7 +1924,34 @@ export default {
view: '查看',
use: '使用',
export: '导出',
manage: '管理'
manage: '管理',
row_column: '行列权限设置',
row_permission: '行权限规则',
enable_row: '启用行权限',
add_condition: '添加条件',
add_relationship: '添加关系',
white_list: '白名单',
white_user_not: '以上权限规则对白名单用户不生效',
organization_or_role: '请选择组织或角色',
column_permission: '列权限规则',
enable_column: '启用列权限',
search_by_field: '通过字段名称搜索',
add_condition: '添加条件',
add_relationship: '添加关系',
filter_fields: '筛选字段',
selct_filter_fields: '请选择筛选字段',
enter_keywords: '请输关键字',
screen_method: '筛选方式',
select: '请选择',
fixed_value: '固定值',
defalut_method: '默认条件',
select_all: '全 选',
added: '已添加',
manual_input: '手工输入',
please_fill: '请一行填一个最多添加500个,识别录入时会自动过滤重复的选项和已经添加过的选项',
close: '关 闭',
add: '添 加',
sure: '确 定',
},
about: {
auth_to: '授权给',

View File

@ -1,60 +1,147 @@
<template>
<!-- <div :class="{'has-logo':showLogo}" :style="{'--active-bg': activeBg, '--theme':$store.state.settings.theme , '--left-menu-hovor': variables.leftMenuHovor}"> -->
<div :class="{'has-logo':showLogo}">
<div :class="{ 'has-logo': showLogo }">
<logo v-if="showLogo" :collapse="isCollapse" />
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:unique-opened="false"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
<sidebar-item
v-for="route in routes"
:key="route.path"
:item="route"
:base-path="route.path"
/>
</el-menu>
<div
:style="{ width: isCollapse ? '64px' : '260px' }"
class="sidebar-collapse-btn"
@click="changeSideWidth"
>
<i
:style="{ transform: isCollapse ? 'rotate(90deg)' : 'rotate(-90deg)' }"
class="el-icon-upload2"
></i>
{{ isCollapse ? "" : "收起导航" }}
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'
import { mapGetters } from "vuex";
import Logo from "./Logo";
import SidebarItem from "./SidebarItem";
import variables from "@/styles/variables.scss";
import path from "path";
import { isExternal } from "@/utils/validate";
export default {
components: { SidebarItem, Logo },
computed: {
...mapGetters([
'sidebar'
]),
...mapGetters(["sidebar"]),
routes() {
// return this.$router.options.routes
return this.$store.state.permission.currentRoutes.children
if (this.isCollapse) {
return this.flatterRouter(
this.$store.state.permission.currentRoutes.children
);
}
return this.$store.state.permission.currentRoutes.children;
},
activeMenu() {
const route = this.$route
const { meta, path } = route
const route = this.$route;
const { meta, path } = route;
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu
return meta.activeMenu;
}
return path
return path;
},
showLogo() {
return this.$store.state.settings.sidebarLogo
return this.$store.state.settings.sidebarLogo;
},
variables() {
return variables
return variables;
},
isCollapse() {
return false
}
}
}
},
data() {
return {
isCollapse: false,
};
},
methods: {
changeSideWidth() {
this.isCollapse = !this.isCollapse;
this.$emit("changeSideWidth", this.isCollapse ? "70px" : "");
},
resolvePath(routePath) {
if (isExternal(routePath)) {
return routePath;
}
if (isExternal(this.basePath)) {
return this.basePath;
}
const currentRoutes = this.$store.state.permission.currentRoutes;
if (currentRoutes && currentRoutes.path) {
return path.resolve(currentRoutes.path, this.basePath, routePath);
}
},
flatterRouter(arr = [], route = [], path = "") {
arr.forEach((ele) => {
this.formaterRoutePath(ele, path, route);
});
return route;
},
pathEndwith(path = "") {
return path.endsWith("/") || !path ? path : path + "/";
},
formaterRoutePath(ele, routePath = "", route) {
if (!ele.hidden) {
if (!ele.children?.length) {
ele.path = routePath + ele.path;
route.push(ele);
} else {
this.flatterRouter(ele.children, route, this.pathEndwith(ele.path));
}
}
},
},
};
</script>
<style lang="scss" scoped>
.sidebar-collapse-btn {
height: 48px;
position: fixed;
bottom: 0;
left: 0;
border-top: 1px solid #1f232926;
display: flex;
align-items: center;
justify-content: center;
//styleName: // 14 22 Regular;
font-family: PingFang SC;
font-size: 14px;
font-weight: 400;
color: #646a73;
background: #fff;
cursor: pointer;
i {
margin-right: 8.8px;
}
}
::v-deep .el-menu {
.el-menu-item,
.el-submenu__title {
height: 48px;
line-height: 48px;
}
.el-tooltip {
padding-left: 25px !important;
}
}
</style>

View File

@ -4,8 +4,8 @@
<topbar v-if="!fullHeightFlag && finishLoad" :show-tips="showTips" />
<de-container :style="mainStyle">
<de-aside-container v-if="!sidebar.hide" type="system" class="le-aside-container">
<sidebar class="sidebar-container" />
<de-aside-container v-if="!sidebar.hide" :isCollapseWidth="sideWidth" type="system" class="le-aside-container">
<sidebar @changeSideWidth="(side) => sideWidth = side" class="sidebar-container" />
</de-aside-container>
<de-main-container class="la-main-container" :class="{'full-height':fullHeightFlag}">
@ -51,7 +51,8 @@ export default {
componentName: 'PanelMain',
showTips: false,
finishLoad: false,
buttonDisable: false
buttonDisable: false,
sideWidth: "",
}
},
computed: {

View File

@ -21,6 +21,7 @@ $--border-color-lighter: #e6ebf5;
$--table-border: 1px solid #dfe6ec;
$--font-path: "~element-ui/lib/theme-chalk/fonts";
$--color-primary: #3370ff;
@import "~element-ui/packages/theme-chalk/src/index";
// topBar

View File

@ -1,6 +1,6 @@
<template>
<de-container>
<de-aside-container>
<de-aside-container isTemplate>
<el-tabs v-model="currentTemplateType" @tab-click="handleClick">
<el-tab-pane name="self">
<span slot="label"><i class="el-icon-star-off tablepanel-i" />{{ $t('panel.user_template') }}</span>

View File

@ -1,10 +1,10 @@
<template>
<layout-content v-if="!noLayout" v-loading="jsname && !innerLoadingNames.includes(jsname) && $store.getters.loadingMap[$store.getters.currentPath]" :header="header" :back-name="backName">
<de-layout-content v-if="!noLayout" v-loading="jsname && !innerLoadingNames.includes(jsname) && $store.getters.loadingMap[$store.getters.currentPath]" :header="header" :back-name="backName">
<async-component v-if="showAsync" :url="url" @execute-axios="executeAxios" @on-add-languanges="addLanguages" @on-plugin-layout="setLayoutInfo" @plugin-call-back="pluginCallBack" />
<div v-else>
<h1>未知组件无法展示</h1>
</div>
</layout-content>
</de-layout-content>
<div v-else>
<async-component v-if="showAsync" :url="url" @execute-axios="executeAxios" @on-add-languanges="addLanguages" @on-plugin-layout="setLayoutInfo" @plugin-call-back="pluginCallBack" />
<div v-else>
@ -15,7 +15,7 @@
</template>
<script>
import LayoutContent from '@/components/business/LayoutContent'
import DeLayoutContent from '@/components/business/DeLayoutContent'
import AsyncComponent from '@/components/AsyncComponent'
import i18n from '@/lang'
import bus from '@/utils/bus'
@ -23,7 +23,7 @@ import { execute } from '@/api/system/dynamic'
export default {
name: 'Dynamic',
components: {
LayoutContent,
DeLayoutContent,
AsyncComponent
},
props: {
@ -69,8 +69,8 @@ export default {
options.callBack(res)
}
}).catch(e => {
if (options.callBack) {
options.callBack(e)
if (options.error) {
options.error(e)
}
})
},

View File

@ -0,0 +1,363 @@
<template>
<el-drawer
title="筛选条件"
:visible.sync="userDrawer"
custom-class="user-drawer"
size="680px"
direction="rtl"
>
<div class="filter">
<span>状态</span>
<div class="filter-item">
<span
@click="statusChange(ele.id)"
:class="[activeStatus.includes(ele.id) ? 'active' : '']"
:key="ele.id"
v-for="ele in status"
>{{ ele.label }}</span>
</div>
</div>
<div class="filter">
<span>组织</span>
<div class="filter-item">
<span
@click="activeDeptChange(ele.id)"
:class="[activeDept.includes(ele.id) ? 'active' : '']"
:key="ele.id"
v-for="ele in selectDepts"
>{{ ele.label }}</span>
<el-popover
placement="bottom"
popper-class="user-popper"
width="200"
trigger="click"
>
<el-popover
placement="bottom"
popper-class="user-popper dept"
width="200"
trigger="click"
>
<el-tree
:load="loadNode"
:lazy="true"
:expand-on-click-node="false"
:data="depts"
:props="defaultProps"
@node-click="handleNodeClick"
></el-tree>
<el-select
ref="roleSelect"
v-model="selectDepts"
slot="reference"
popper-class="tree-select"
multiple
:placeholder="$t('commons.please_select')"
@change="changeRole"
@remove-tag="changeRole"
value-key="id"
>
<el-option
v-for="item in selectDepts"
:key="item.label"
:label="item.label"
:value="item"
/>
</el-select>
</el-popover>
<span slot="reference">+ 更多</span>
</el-popover>
</div>
</div>
<div class="filter">
<span>角色</span>
<div class="filter-item">
<span
@click="activeRoleChange(ele.id)"
:class="[activeRole.includes(ele.id) ? 'active' : '']"
:key="ele.id"
v-for="ele in rolesValue"
>{{ ele.name }}</span
>
<el-popover
placement="bottom"
popper-class="user-popper"
width="200"
trigger="click"
>
<el-select
ref="roleSelect"
v-model="rolesValue"
multiple
:placeholder="$t('commons.please_select')"
@change="changeRole"
@remove-tag="changeRole"
value-key="id"
>
<el-option
v-for="item in roles"
:key="item.name"
:label="item.name"
:value="item"
/>
</el-select>
<span slot="reference">+ 更多</span>
</el-popover>
</div>
</div>
<div class="foot">
<el-button class="btn normal" @click="reset">{{
$t("commons.reset")
}}</el-button>
<el-button type="primary" class="btn" @click="search">{{
$t("commons.adv_search.search")
}}</el-button>
</div>
</el-drawer>
</template>
<script>
// import bus from '@/utils/bus'
import { allRoles } from "@/api/system/user";
import { getDeptTree, treeByDeptId } from "@/api/system/dept";
export default {
data() {
return {
value: [],
roles: [],
status: [{
id: 1,
label: '启用'
},{
id: 0,
label: '禁用'
}],
activeStatus: [],
rolesValue: [],
activeRole: [],
depts: [],
selectDepts: [],
activeDept: [],
defaultProps: {
children: "children",
label: "label",
isLeaf: "leaf",
},
userDrawer: false
};
},
mounted() {
this.initRoles();
},
methods: {
//
treeByDeptId() {
treeByDeptId(0).then((res) => {
this.depts = res.data || [];
});
},
changeRole() {
const roles = this.rolesValue.map((item) => item.id);
this.activeRole = this.activeRole.filter((ele) => roles.includes(ele));
},
activeRoleChange(id) {
const roleIndex = this.activeRole.findIndex((ele) => ele === id);
if (roleIndex === -1) {
this.activeRole.push(id);
} else {
this.activeRole.splice(roleIndex, 1);
}
},
handleNodeClick({ id, label }) {
const deptIndex = this.selectDepts.findIndex((ele) => ele.id === id);
if (deptIndex === -1) {
this.selectDepts.push({ id, label });
} else {
this.selectDepts.splice(deptIndex, 1);
this.changeDepts();
}
},
activeDeptChange(id) {
const deptIndex = this.activeDept.findIndex((ele) => ele === id);
if (deptIndex === -1) {
this.activeDept.push(id);
} else {
this.activeDept.splice(deptIndex, 1);
}
},
statusChange(id) {
const statusIndex = this.activeStatus.findIndex((ele) => ele === id);
if (statusIndex === -1) {
this.activeStatus.push(id);
} else {
this.activeStatus.splice(statusIndex, 1);
}
},
changeDepts() {
const depts = this.selectDepts.map((item) => item.id);
this.activeDept = this.activeDept.filter((ele) => depts.includes(ele));
},
loadNode(node, resolve) {
if (!this.depts.length) {
this.treeByDeptId();
return;
}
getDeptTree(node.data.id).then((res) => {
resolve(
res.data.map((dept) => {
return this.normalizer(dept);
})
);
});
},
normalizer(node) {
return {
id: node.deptId,
label: node.name,
leaf: !node.hasChildren,
};
},
initRoles() {
allRoles().then((res) => {
this.roles = res.data;
});
},
search() {
this.userDrawer = false;
this.$emit('search', this.formatCondition())
},
formatCondition() {
const fildMap = {'r.role_id': this.activeRole, 'd.dept_id': this.activeDept, 'u.enabled': this.activeStatus}
const conditions = []
Object.keys(fildMap).forEach(ele => {
if (fildMap[ele].length) {
conditions.push({
field: ele,
operator: 'in',
value: fildMap[ele]
})
}
})
return conditions;
},
init() {
this.userDrawer = true;
},
reset() {
this.activeStatus = [];
this.activeRole = [];
this.activeDept = [];
this.search()
},
},
};
</script>
<style lang="scss">
.user-drawer {
.el-drawer__header {
padding: 16px 21px 16px 24px;
font-family: PingFang SC;
font-size: 16px;
font-weight: 500;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
color: #1f2329;
border-bottom: 1px solid rgba(187, 191, 196, 0.5);
margin: 0;
}
.el-drawer__body {
padding: 12px 24px 24px 24px;
position: relative;
}
.filter {
display: flex;
align-items: center;
height: 46px;
> :nth-child(1) {
margin-right: 88px;
color: #1f2329;
font-family: "PingFang SC";
font-style: normal;
font-weight: 400;
font-size: 14px;
}
.filter-item {
span {
font-family: PingFang SC;
font-size: 14px;
font-weight: 400;
line-height: 24px;
margin-right: 12px;
text-align: center;
padding: 1px 6px;
background: #f5f6f7;
border-radius: 2px;
cursor: pointer;
span {
margin-right: 0;
padding: 0;
span {
margin-right: 0;
padding: 0;
}
}
}
.active {
background: rgba(51, 112, 255, 0.1);
color: #0c296e;
}
}
}
.btn {
border-radius: 4px;
padding: 5px 26px 5px 26px;
font-family: PingFang SC;
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0px;
text-align: center;
border: none;
box-sizing: border-box;
}
.normal {
color: #1f2329;
border: 1px solid #bbbfc4;
margin-left: 12px;
}
.foot {
position: absolute;
right: 24px;
bottom: 24px;
text-align: right;
}
}
.user-popper {
background: transparent;
padding: 0;
.popper__arrow {
display: none;
}
}
.tree-select {
.el-select-dropdown__empty,
.popper__arrow {
display: none;
}
}
.user-popper.dept {
height: 400px;
overflow: auto;
}
</style>

View File

@ -1,352 +0,0 @@
<template>
<layout-content :header="formType=='add' ? $t('user.create') : $t('user.modify')" back-name="system-user">
<el-form ref="createUserForm" :model="form" :rules="rule" size="small" label-width="80px" label-position="right">
<el-form-item label="ID" prop="username">
<el-input v-model="form.username" :disabled="formType !== 'add'" />
</el-form-item>
<el-form-item :label="$t('commons.phone')" prop="phone">
<el-input v-model="form.phone" />
</el-form-item>
<el-form-item :label="$t('commons.nick_name')" prop="nickName">
<el-input v-model="form.nickName" />
</el-form-item>
<el-form-item :label="$t('commons.email')" prop="email">
<el-input v-model="form.email" />
</el-form-item>
<!-- <el-form-item v-if="formType !== 'modify'" :label="$t('commons.password')" prop="password">
<el-input v-model="form.password" autocomplete="off" show-password />
</el-form-item>
<el-form-item v-if="formType !== 'modify'" :label="$t('commons.confirmPassword')" prop="confirmPassword">
<el-input v-model="form.confirmPassword" autocomplete="off" show-password />
</el-form-item> -->
<el-form-item :label="$t('commons.gender')" prop="gender">
<el-radio-group v-model="form.gender" style="width: 178px">
<el-radio :label="$t('commons.man')">{{ $t('commons.man') }}</el-radio>
<el-radio :label="$t('commons.woman')">{{ $t('commons.woman') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('commons.status')" prop="enabled">
<el-radio-group v-model="form.enabled" :disabled="formType !== 'add' && form.isAdmin" style="width: 140px">
<el-radio :label="1">{{ $t('commons.enable') }}</el-radio>
<el-radio :label="0">{{ $t('commons.disable') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-show="isPluginLoaded" :label="$t('commons.organization')" prop="deptId">
<treeselect
ref="deptTreeSelect"
v-model="form.deptId"
:options="depts"
:load-options="loadDepts"
:auto-load-root-options="false"
:placeholder="$t('user.choose_org')"
:no-children-text="$t('commons.treeselect.no_children_text')"
:no-options-text="$t('commons.treeselect.no_options_text')"
:no-results-text="$t('commons.treeselect.no_results_text')"
@open="filterData"
/>
</el-form-item>
<el-form-item v-show="isPluginLoaded" :label="$t('commons.role')" prop="roleIds">
<el-select
ref="roleSelect"
v-model="form.roleIds"
style="width: 100%"
:disabled="formType !== 'add' && form.isAdmin"
multiple
:placeholder="$t('commons.please_select')"
@remove-tag="deleteTag"
@change="changeRole"
>
<el-option
v-for="item in roles"
:key="item.name"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="save">{{ $t('commons.confirm') }}</el-button>
<el-button @click="reset">{{ $t('commons.reset') }}</el-button>
</el-form-item>
<el-form-item v-if="formType === 'add'">
<!-- <el-link class="pwd-tips" type="danger" :underline="false">{{ $t('commons.default_pwd') + '' + defaultPWD }}</el-link> -->
<el-button class="pwd-tips" type="text">{{ $t('commons.default_pwd') + '' + defaultPWD }}</el-button>
<el-button
v-clipboard:copy="defaultPWD"
v-clipboard:success="onCopy"
v-clipboard:error="onError"
type="text"
>
{{ $t('commons.copy') }}
</el-button>
</el-form-item>
</el-form>
</layout-content>
</template>
<script>
import LayoutContent from '@/components/business/LayoutContent'
import { PHONE_REGEX } from '@/utils/validate'
import { getDeptTree, treeByDeptId } from '@/api/system/dept'
import { addUser, editUser, allRoles } from '@/api/system/user'
import { pluginLoaded, defaultPwd } from '@/api/user'
export default {
components: { LayoutContent },
data() {
return {
form: {
roles: [{
id: ''
}]
},
rule: {
username: [
{ required: true, message: this.$t('user.input_id'), trigger: 'blur' },
{ min: 1, max: 50, message: this.$t('commons.input_limit', [1, 50]), trigger: 'blur' },
{
required: true,
pattern: '^[^\u4e00-\u9fa5]+$',
message: this.$t('user.special_characters_are_not_supported'),
trigger: 'blur'
}
],
nickName: [
{ required: true, message: this.$t('user.input_name'), trigger: 'blur' },
{ min: 2, max: 50, message: this.$t('commons.input_limit', [2, 50]), trigger: 'blur' },
{
required: true,
message: this.$t('user.special_characters_are_not_supported'),
trigger: 'blur'
}
],
phone: [
{
pattern: PHONE_REGEX,
message: this.$t('user.mobile_number_format_is_incorrect'),
trigger: 'blur'
}
],
email: [
{ required: true, message: this.$t('user.input_email'), trigger: 'blur' },
{
required: true,
pattern: /^[a-zA-Z0-9_._-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,
message: this.$t('user.email_format_is_incorrect'),
trigger: 'blur'
}
],
password: [
{ required: true, message: this.$t('user.input_password'), trigger: 'blur' },
{
required: true,
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,30}$/,
message: this.$t('member.password_format_is_incorrect'),
trigger: 'blur'
}
],
confirmPassword: [
{ required: true, message: this.$t('user.input_password'), trigger: 'blur' },
{ required: true, validator: this.repeatValidator, trigger: 'blur' }
],
newPassword: [
{ required: true, message: this.$t('user.input_password'), trigger: 'blur' },
{
required: true,
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,30}$/,
message: this.$t('member.password_format_is_incorrect'),
trigger: 'blur'
}
],
roleIds: [{ required: true, message: this.$t('user.input_roles'), trigger: 'change' }],
deptId: [],
gender: [],
enable: []
},
defaultForm: { id: null, username: null, nickName: null, gender: '男', email: null, enabled: 1, deptId: null, phone: null, roleIds: [2] },
depts: null,
roles: [],
roleDatas: [],
userRoles: [],
formType: 'add',
isPluginLoaded: false,
defaultPWD: 'DataEase123..'
}
},
created() {
if (this.$router.currentRoute.params && this.$router.currentRoute.params.id) {
const row = this.$router.currentRoute.params
this.edit(row)
} else {
this.create()
}
this.initRoles()
},
mounted() {
this.bindKey()
},
destroyed() {
this.unBindKey()
},
beforeCreate() {
pluginLoaded().then(res => {
this.isPluginLoaded = res.success && res.data
})
defaultPwd().then(res => {
if (res && res.data) {
this.defaultPWD = res.data
}
})
},
methods: {
entryKey(event) {
const keyCode = event.keyCode
if (keyCode === 13) {
this.save()
}
},
bindKey() {
document.addEventListener('keypress', this.entryKey)
},
unBindKey() {
document.removeEventListener('keypress', this.entryKey)
},
repeatValidator(rule, value, callback) {
if (value !== this.form.password) {
callback(new Error(this.$t('member.inconsistent_passwords')))
} else {
callback()
}
},
create() {
this.depts = null
this.formType = 'add'
this.form = Object.assign({}, this.defaultForm)
},
edit(row) {
this.depts = null
this.formType = 'modify'
this.dialogVisible = true
this.form = Object.assign({}, row)
this.form.password = ''
if (this.form.deptId === 0) {
this.form.deptId = null
}
this.initDeptTree()
},
initRoles() {
allRoles().then(res => {
this.roles = res.data
})
},
initDeptTree() {
treeByDeptId(this.form.deptId || 0).then(res => {
const results = res.data.map(node => {
if (node.hasChildren && !node.children) {
node.children = null
// delete node.children
}
return node
})
this.depts = results
})
},
//
loadDepts({ action, parentNode, callback }) {
if (action === 'LOAD_ROOT_OPTIONS' && !this.form.deptId) {
const _self = this
treeByDeptId(0).then(res => {
const results = res.data.map(node => {
if (node.hasChildren && !node.children) {
node.children = null
}
return node
})
_self.depts = results
callback()
})
}
if (action === 'LOAD_CHILDREN_OPTIONS') {
const _self = this
getDeptTree(parentNode.id).then(res => {
parentNode.children = res.data.map(function(obj) {
return _self.normalizer(obj)
})
callback()
})
}
},
normalizer(node) {
if (node.hasChildren) {
node.children = null
}
return {
id: node.deptId,
label: node.name,
children: node.children
}
},
deleteTag(value) {
this.userRoles.forEach(function(data, index) {
if (data.id === value) {
this.userRoles.splice(index, value)
}
}.bind(this))
},
changeRole(value) {
this.userRoles = []
value.forEach(function(data, index) {
const role = { id: data }
this.userRoles.push(role)
}.bind(this))
},
reset() {
this.$refs.createUserForm.resetFields()
},
save() {
this.$refs.createUserForm.validate(valid => {
if (valid) {
// !this.form.deptId && (this.form.deptId = 0)
const method = this.formType === 'add' ? addUser : editUser
method(this.form).then(res => {
this.$success(this.$t('commons.save_success'))
this.backToList()
})
} else {
return false
}
})
},
backToList() {
this.$router.push({ name: 'system-user' })
},
filterData(instanceId) {
this.$refs.roleSelect && this.$refs.roleSelect.blur && this.$refs.roleSelect.blur()
if (!this.depts) {
return
}
const results = this.depts.map(node => {
if (node.hasChildren) {
node.children = null
}
return node
})
this.depts = results
},
onCopy(e) {
this.$success(this.$t('commons.copy_success'))
},
onError(e) {}
}
}
</script>
<style lang="scss" scoped>
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,578 @@
<template>
<!-- <layout-content :header="formType=='add' ? $t('user.create') : $t('user.modify')" back-name="system-user">
</layout-content> -->
<el-dialog
:title="formType == 'add' ? $t('user.create') : $t('user.modify')"
:visible.sync="dialogVisible"
class="user-editer-form"
width="840px"
:before-close="reset"
>
<div v-if="formType === 'add'" class="editer-form-title">
<i class="el-icon-info"></i>
<span class="pwd" type="text">{{
$t("commons.default_pwd") + "" + defaultPWD
}}</span>
<el-button
v-clipboard:copy="defaultPWD"
v-clipboard:success="onCopy"
v-clipboard:error="onError"
class="btn-text"
type="text"
>
{{ $t("commons.copy") }}
</el-button>
</div>
<el-form
ref="createUserForm"
:model="form"
:rules="rule"
size="small"
label-width="80px"
label-position="right"
>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item :label="$t('commons.nick_name')" prop="nickName">
<el-input
:placeholder="$t('user.input_name')"
v-model="form.nickName"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="ID" prop="username">
<el-input
:placeholder="$t('user.input_id')"
v-model="form.username"
:disabled="formType !== 'add'"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item :label="$t('commons.email')" prop="email">
<el-input
:placeholder="$t('user.input_email')"
v-model="form.email"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('commons.mobile_phone_number')" prop="phone">
<el-input
:placeholder="$t('commons.mobile_phone')"
v-model="form.phone"
class="input-with-select"
>
<el-select
v-model="form.phonePrefix"
slot="prepend"
:placeholder="$t('fu.search_bar.please_select')"
>
<el-option label="+86" value="+86"></el-option>
</el-select>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item :label="$t('commons.gender')" prop="gender">
<el-select
class="form-gender-select"
v-model="form.gender"
:placeholder="$t('user.select_gender')"
>
<el-option :label="$t('commons.man')" value="男"> </el-option>
<el-option :label="$t('commons.woman')" value="女"> </el-option>
<el-option :label="$t('commons.keep_secret')" value="保密">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
v-show="isPluginLoaded"
:label="$t('commons.organization')"
prop="deptId"
>
<treeselect
ref="deptTreeSelect"
v-model="form.deptId"
:options="depts"
:load-options="loadDepts"
:auto-load-root-options="false"
:placeholder="$t('user.choose_org')"
:no-children-text="$t('commons.treeselect.no_children_text')"
:no-options-text="$t('commons.treeselect.no_options_text')"
:no-results-text="$t('commons.treeselect.no_results_text')"
@open="filterData"
/>
</el-form-item>
</el-col>
</el-row>
<!-- <el-form-item v-if="formType !== 'modify'" :label="$t('commons.password')" prop="password">
<el-input v-model="form.password" autocomplete="off" show-password />
</el-form-item>
<el-form-item v-if="formType !== 'modify'" :label="$t('commons.confirmPassword')" prop="confirmPassword">
<el-input v-model="form.confirmPassword" autocomplete="off" show-password />
</el-form-item> -->
<el-form-item
v-show="isPluginLoaded"
:label="$t('commons.role')"
prop="roleIds"
>
<el-select
ref="roleSelect"
v-model="form.roleIds"
style="width: 100%"
:disabled="formType !== 'add' && form.isAdmin"
multiple
:placeholder="$t('user.input_roles')"
@remove-tag="deleteTag"
@change="changeRole"
>
<el-option
v-for="item in roles"
:key="item.name"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('commons.status')" prop="enabled">
<el-switch
:disabled="formType !== 'add' && form.isAdmin"
v-model="form.enabled"
:active-value="1"
:inactive-value="0"
>
</el-switch>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button class="btn normal" @click="reset">{{
$t("commons.cancel")
}}</el-button>
<el-button class="btn" type="primary" @click="save">{{
$t("commons.confirm")
}}</el-button>
</span>
</el-dialog>
</template>
<script>
import { PHONE_REGEX } from "@/utils/validate";
import { getDeptTree, treeByDeptId } from "@/api/system/dept";
import { addUser, editUser, allRoles } from "@/api/system/user";
import { pluginLoaded, defaultPwd } from "@/api/user";
import {
LOAD_CHILDREN_OPTIONS,
LOAD_ROOT_OPTIONS,
} from "@riophae/vue-treeselect";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
components: { Treeselect },
data() {
return {
form: {
roles: [
{
id: "",
},
],
},
rule: {
username: [
{
required: true,
message: this.$t("user.id_mandatory"),
trigger: "blur",
},
{
min: 1,
max: 50,
message: this.$t("commons.input_limit", [1, 50]),
trigger: "blur",
},
{
required: true,
pattern: "^[^\u4e00-\u9fa5]+$",
message: this.$t("user.special_characters_are_not_supported"),
trigger: "blur",
},
],
nickName: [
{
required: true,
message: this.$t("user.name_mandatory"),
trigger: "blur",
},
{
min: 2,
max: 50,
message: this.$t("commons.input_limit", [2, 50]),
trigger: "blur",
},
{
required: true,
message: this.$t("user.special_characters_are_not_supported"),
trigger: "blur",
},
],
phone: [
{
pattern: PHONE_REGEX,
message: this.$t("user.phone_format"),
trigger: "blur",
},
],
email: [
{
required: true,
message: this.$t("user.email_mandatory"),
trigger: "blur",
},
{
required: true,
pattern: /^[a-zA-Z0-9_._-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,
message: this.$t("user.email_format_is_incorrect"),
trigger: "blur",
},
],
password: [
{
required: true,
message: this.$t("user.input_password"),
trigger: "blur",
},
{
required: true,
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,30}$/,
message: this.$t("member.password_format_is_incorrect"),
trigger: "blur",
},
],
confirmPassword: [
{
required: true,
message: this.$t("user.input_password"),
trigger: "blur",
},
{ required: true, validator: this.repeatValidator, trigger: "blur" },
],
newPassword: [
{
required: true,
message: this.$t("user.input_password"),
trigger: "blur",
},
{
required: true,
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,30}$/,
message: this.$t("member.password_format_is_incorrect"),
trigger: "blur",
},
],
roleIds: [
{
required: true,
message: this.$t("user.role_mandatory"),
trigger: "change",
},
],
deptId: [],
gender: [],
enabled: [{ required: true, trigger: "change" }],
},
defaultForm: {
id: null,
username: null,
nickName: null,
gender: "男",
email: null,
enabled: 1,
deptId: null,
phone: null,
phonePrefix: "+86",
roleIds: [2],
},
depts: null,
roles: [],
roleDatas: [],
userRoles: [],
formType: "add",
isPluginLoaded: false,
defaultPWD: "DataEase123..",
dialogVisible: false,
};
},
beforeCreate() {
pluginLoaded().then((res) => {
this.isPluginLoaded = res.success && res.data;
});
defaultPwd().then((res) => {
if (res && res.data) {
this.defaultPWD = res.data;
}
});
},
methods: {
repeatValidator(rule, value, callback) {
if (value !== this.form.password) {
callback(new Error(this.$t("member.inconsistent_passwords")));
} else {
callback();
}
},
create() {
this.depts = null;
this.formType = "add";
this.form = Object.assign({}, this.defaultForm);
},
init(row) {
this.initRoles();
this.dialogVisible = true;
if (!row) {
this.create();
return;
}
this.depts = null;
this.formType = "modify";
this.dialogVisible = true;
this.form = Object.assign({}, row);
this.form.password = "";
if (this.form.deptId === 0) {
this.form.deptId = null;
}
if (!this.form.phonePrefix) {
this.form.phonePrefix = '+86';
}
this.initDeptTree();
},
initRoles() {
allRoles().then((res) => {
this.roles = res.data;
});
},
initDeptTree() {
treeByDeptId(this.form.deptId || 0).then((res) => {
const results = res.data.map((node) => {
if (node.hasChildren && !node.children) {
node.children = null;
// delete node.children
}
return node;
});
this.depts = results;
});
},
//
loadDepts({ action, parentNode, callback }) {
if (action === "LOAD_ROOT_OPTIONS" && !this.form.deptId) {
const _self = this;
treeByDeptId(0).then((res) => {
const results = res.data.map((node) => {
if (node.hasChildren && !node.children) {
node.children = null;
}
return node;
});
_self.depts = results;
callback();
});
}
if (action === "LOAD_CHILDREN_OPTIONS") {
const _self = this;
getDeptTree(parentNode.id).then((res) => {
parentNode.children = res.data.map(function (obj) {
return _self.normalizer(obj);
});
callback();
});
}
},
normalizer(node) {
if (node.hasChildren) {
node.children = null;
}
return {
id: node.deptId,
label: node.name,
children: node.children,
};
},
deleteTag(value) {
this.userRoles.forEach(
function (data, index) {
if (data.id === value) {
this.userRoles.splice(index, value);
}
}.bind(this)
);
},
changeRole(value) {
this.userRoles = [];
value.forEach(
function (data, index) {
const role = { id: data };
this.userRoles.push(role);
}.bind(this)
);
},
reset() {
this.$refs.createUserForm.resetFields();
this.dialogVisible = false;
},
save() {
this.$refs.createUserForm.validate((valid) => {
if (valid) {
// !this.form.deptId && (this.form.deptId = 0)
const method = this.formType === "add" ? addUser : editUser;
method(this.form).then((res) => {
this.$success(this.$t("commons.save_success"));
this.reset();
this.$emit('saved')
});
} else {
return false;
}
});
},
filterData(instanceId) {
this.$refs.roleSelect &&
this.$refs.roleSelect.blur &&
this.$refs.roleSelect.blur();
if (!this.depts) {
return;
}
const results = this.depts.map((node) => {
if (node.hasChildren) {
node.children = null;
}
return node;
});
this.depts = results;
},
onCopy(e) {
this.$success(this.$t("commons.copy_success"));
},
onError(e) {},
},
};
</script>
<style lang="scss" scoped>
.user-editer-form {
::v-deep .el-dialog__body {
padding: 0 24px 24px 24px;
}
::v-deep .el-dialog__header {
padding: 24px 24px 16px 24px;
}
::v-deep .el-dialog__footer {
padding-top: 0;
}
.editer-form-title {
width: 100%;
border-radius: 4px;
background: #e1eaff;
padding: 9px 16px;
margin-bottom: 16px;
i {
color: #3370ff;
font-size: 14.666666030883789px;
}
.pwd,
.btn-text {
font-family: PingFang SC;
font-size: 14px;
font-weight: 400;
line-height: 22px;
text-align: left;
color: #1f2329;
}
.pwd {
margin: 0 8px;
}
.btn-text {
padding: 0;
border: none;
}
}
::v-deep .el-form-item__label {
width: 100% !important;
text-align: left;
}
::v-deep
.el-form-item.is-required:not(.is-no-asterisk)
> .el-form-item__label:before {
display: none;
}
::v-deep
.el-form-item.is-required:not(.is-no-asterisk)
> .el-form-item__label::after {
content: "*";
color: #f54a45;
margin-left: 2px;
}
::v-deep .el-form-item__content {
margin-left: 0 !important;
}
.input-with-select {
::v-deep .el-input-group__prepend {
background-color: #fff;
}
.el-select {
::v-deep .el-input__inner {
width: 72px;
}
}
}
.btn {
border-radius: 4px;
padding: 5px 26px 5px 26px;
font-family: PingFang SC;
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: 0px;
text-align: center;
border: none;
box-sizing: border-box;
}
.normal {
color: #1f2329;
border: 1px solid #bbbfc4;
margin-left: 12px;
}
.form-gender-select {
width: 100%;
}
.btn {
}
}
</style>