21 changed files with 600 additions and 1629 deletions
Binary file not shown.
@ -1,204 +0,0 @@ |
|||||
<template> |
|
||||
<el-form |
|
||||
ref="formCreateUser" |
|
||||
label-width="110px" |
|
||||
:model="createUser" |
|
||||
:rules="createUserRules" |
|
||||
> |
|
||||
<el-tabs v-model="activedTabPane"> |
|
||||
<el-tab-pane |
|
||||
:label="$t('userProfile.basic')" |
|
||||
name="basic" |
|
||||
> |
|
||||
<el-form-item |
|
||||
prop="userName" |
|
||||
:label="$t('users.userName')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="createUser.userName" |
|
||||
:placeholder="$t('userProfile.pleaseInputUserName')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="name" |
|
||||
:label="$t('users.name')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="createUser.name" |
|
||||
:placeholder="$t('userProfile.pleaseInputName')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="surname" |
|
||||
:label="$t('users.surname')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="createUser.surname" |
|
||||
:placeholder="$t('userProfile.pleaseInputSurname')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="phoneNumber" |
|
||||
:label="$t('users.phoneNumber')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="createUser.phoneNumber" |
|
||||
:placeholder="$t('userProfile.pleaseInputPhoneNumber')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="email" |
|
||||
:label="$t('users.email')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="createUser.email" |
|
||||
:placeholder="$t('userProfile.pleaseInputEmail')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
</el-tab-pane> |
|
||||
<el-tab-pane |
|
||||
:label="$t('userProfile.security')" |
|
||||
name="security" |
|
||||
> |
|
||||
<el-form-item |
|
||||
prop="password" |
|
||||
label-width="100px" |
|
||||
:label="$t('userProfile.password')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="createUser.password" |
|
||||
type="password" |
|
||||
:placeholder="$t('userProfile.pleaseInputPassword')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="twoFactorEnabled" |
|
||||
label-width="100px" |
|
||||
:label="$t('users.twoFactorEnabled')" |
|
||||
> |
|
||||
<el-switch v-model="createUser.twoFactorEnabled" /> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="lockoutEnabled" |
|
||||
label-width="100px" |
|
||||
:label="$t('users.lockoutEnabled')" |
|
||||
> |
|
||||
<el-switch v-model="createUser.lockoutEnabled" /> |
|
||||
</el-form-item> |
|
||||
</el-tab-pane> |
|
||||
</el-tabs> |
|
||||
<el-form-item> |
|
||||
<el-button |
|
||||
class="cancel" |
|
||||
style="width:100px" |
|
||||
@click="onCancel" |
|
||||
> |
|
||||
{{ $t('table.cancel') }} |
|
||||
</el-button> |
|
||||
<el-button |
|
||||
class="confirm" |
|
||||
type="primary" |
|
||||
style="width:100px" |
|
||||
@click="onSubmit" |
|
||||
> |
|
||||
{{ $t('table.confirm') }} |
|
||||
</el-button> |
|
||||
</el-form-item> |
|
||||
</el-form> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { Component, Vue } from 'vue-property-decorator' |
|
||||
import UserApiService, { UserCreateDto } from '@/api/users' |
|
||||
import { checkPermission } from '@/utils/permission' |
|
||||
|
|
||||
@Component({ |
|
||||
name: 'UserProfile', |
|
||||
methods: { |
|
||||
checkPermission |
|
||||
} |
|
||||
}) |
|
||||
export default class extends Vue { |
|
||||
private createUser: UserCreateDto |
|
||||
|
|
||||
private activedTabPane: string |
|
||||
|
|
||||
constructor() { |
|
||||
super() |
|
||||
this.activedTabPane = 'basic' |
|
||||
this.createUser = new UserCreateDto() |
|
||||
} |
|
||||
|
|
||||
private validatePhoneNumberValue = (rule: any, value: string, callback: any) => { |
|
||||
const phoneReg = /^1[34578]\d{9}$/ |
|
||||
if (!value || !phoneReg.test(value)) { |
|
||||
callback(new Error(this.l('global.pleaseInputBy', { key: this.l('global.correctPhoneNumber') }))) |
|
||||
} else { |
|
||||
callback() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private createUserRules = { |
|
||||
userName: [ |
|
||||
{ required: true, message: this.l('userProfile.pleaseInputUserName'), trigger: 'blur' }, |
|
||||
{ min: 3, max: 128, message: this.l('global.charLengthRange', { min: 3, max: 128 }), trigger: 'blur' } |
|
||||
], |
|
||||
name: [ |
|
||||
{ required: true, message: this.l('userProfile.pleaseInputName'), trigger: 'blur' }, |
|
||||
{ min: 2, max: 256, message: this.l('global.charLengthRange', { min: 2, max: 256 }), trigger: 'blur' } |
|
||||
], |
|
||||
email: [ |
|
||||
{ required: true, message: this.l('userProfile.pleaseInputEmail'), trigger: 'blur' }, |
|
||||
{ type: 'email', message: this.l('global.pleaseInputBy', { key: this.l('global.correctEmailAddress') }), trigger: ['blur', 'change'] } |
|
||||
], |
|
||||
password: [ |
|
||||
{ required: true, message: this.l('userProfile.pleaseInputPassword'), trigger: 'blur' }, |
|
||||
{ min: 6, max: 15, message: this.l('global.charLengthRange', { min: 6, max: 15 }), trigger: 'blur' } |
|
||||
], |
|
||||
phoneNumber: [ |
|
||||
{ required: true, validator: this.validatePhoneNumberValue, trigger: 'blur' } |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
private onSubmit() { |
|
||||
const frmCreateUser = this.$refs.formCreateUser as any |
|
||||
frmCreateUser.validate(async(valid: boolean) => { |
|
||||
if (valid) { |
|
||||
UserApiService.createUser(this.createUser).then(user => { |
|
||||
this.$message.success(this.l('users.createUserSuccess', { name: user.name })) |
|
||||
this.resetForm() |
|
||||
this.onCancel() |
|
||||
this.$emit('onUserProfileChanged', user.id) |
|
||||
}) |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
private onCancel() { |
|
||||
this.resetForm() |
|
||||
this.$emit('onClose') |
|
||||
} |
|
||||
|
|
||||
private resetForm() { |
|
||||
this.activedTabPane = 'basic' |
|
||||
this.createUser = new UserCreateDto() |
|
||||
const frmCreateUser = this.$refs.formCreateUser as any |
|
||||
frmCreateUser.resetFields() |
|
||||
} |
|
||||
|
|
||||
private l(name: string, values?: any[] | { [key: string]: any }) { |
|
||||
return this.$t(name, values).toString() |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.confirm { |
|
||||
position: absolute; |
|
||||
right: 10px; |
|
||||
} |
|
||||
.cancel { |
|
||||
position: absolute; |
|
||||
right: 120px; |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,321 @@ |
|||||
|
<template> |
||||
|
<el-dialog |
||||
|
:visible="showDialog" |
||||
|
:title="title" |
||||
|
width="800px" |
||||
|
custom-class="modal-form" |
||||
|
:close-on-click-modal="false" |
||||
|
:close-on-press-escape="false" |
||||
|
:show-close="false" |
||||
|
@close="onFormClosed" |
||||
|
> |
||||
|
<el-form |
||||
|
ref="editUserForm" |
||||
|
label-width="110px" |
||||
|
:model="currentUser" |
||||
|
:rules="currentUserRules" |
||||
|
> |
||||
|
<el-tabs v-model="activedTabPane"> |
||||
|
<el-tab-pane |
||||
|
:label="$t('AbpIdentity.UserInformations')" |
||||
|
name="information" |
||||
|
> |
||||
|
<el-form-item |
||||
|
prop="userName" |
||||
|
:label="$t('AbpIdentity.DisplayName:UserName')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="currentUser.userName" |
||||
|
:placeholder="$t('global.pleaseInputBy', {key: $t('AbpIdentity.DisplayName:UserName')})" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
prop="name" |
||||
|
:label="$t('AbpIdentity.DisplayName:Name')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="currentUser.name" |
||||
|
:placeholder="$t('global.pleaseInputBy', {key: $t('AbpIdentity.DisplayName:Name')})" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
prop="surname" |
||||
|
:label="$t('AbpIdentity.DisplayName:Surname')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="currentUser.surname" |
||||
|
:placeholder="$t('global.pleaseInputBy', {key: $t('AbpIdentity.DisplayName:Surname')})" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
prop="phoneNumber" |
||||
|
:label="$t('AbpIdentity.DisplayName:PhoneNumber')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="currentUser.phoneNumber" |
||||
|
:placeholder="$t('global.pleaseInputBy', {key: $t('AbpIdentity.DisplayName:PhoneNumber')})" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
prop="email" |
||||
|
:label="$t('AbpIdentity.DisplayName:Email')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="currentUser.email" |
||||
|
:placeholder="$t('global.pleaseInputBy', {key: $t('AbpIdentity.DisplayName:Email')})" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
v-if="!isEditUser" |
||||
|
prop="password" |
||||
|
:label="$t('AbpIdentity.DisplayName:Password')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="currentUser.password" |
||||
|
type="password" |
||||
|
:placeholder="$t('global.pleaseInputBy', {key: $t('AbpIdentity.DisplayName:Password')})" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-row> |
||||
|
<el-col :span="8"> |
||||
|
<el-form-item |
||||
|
prop="twoFactorEnabled" |
||||
|
label-width="120px" |
||||
|
:label="$t('AbpIdentity.DisplayName:TwoFactorEnabled')" |
||||
|
> |
||||
|
<el-switch v-model="currentUser.twoFactorEnabled" /> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
<el-col :span="16"> |
||||
|
<el-form-item |
||||
|
prop="lockoutEnabled" |
||||
|
label-width="180px" |
||||
|
:label="$t('AbpIdentity.LockoutEnabled')" |
||||
|
> |
||||
|
<el-switch v-model="currentUser.lockoutEnabled" /> |
||||
|
</el-form-item> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</el-tab-pane> |
||||
|
<el-tab-pane |
||||
|
:label="$t('AbpIdentity.Roles')" |
||||
|
name="roles" |
||||
|
> |
||||
|
<el-transfer |
||||
|
v-model="currentUserRoles" |
||||
|
:titles="[$t('AbpIdentity.RoleList'),$t('AbpIdentity.HasRoles')]" |
||||
|
:data="roleList" |
||||
|
/> |
||||
|
</el-tab-pane> |
||||
|
</el-tabs> |
||||
|
<el-form-item> |
||||
|
<el-button |
||||
|
class="cancel" |
||||
|
type="info" |
||||
|
@click="onFormClosed" |
||||
|
> |
||||
|
{{ $t('AbpIdentity.Cancel') }} |
||||
|
</el-button> |
||||
|
<el-button |
||||
|
class="confirm" |
||||
|
type="primary" |
||||
|
icon="el-icon-check" |
||||
|
@click="onSave" |
||||
|
> |
||||
|
{{ $t('AbpIdentity.Save') }} |
||||
|
</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
</el-dialog> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import Component, { mixins } from 'vue-class-component' |
||||
|
import { Prop, Watch } from 'vue-property-decorator' |
||||
|
import { checkPermission } from '@/utils/permission' |
||||
|
import { AbpModule } from '@/store/modules/abp' |
||||
|
import EventBusMiXin from '@/mixins/EventBusMiXin' |
||||
|
|
||||
|
import RoleService from '@/api/roles' |
||||
|
import { IRoleData } from '@/api/types' |
||||
|
import UserApiService, { UserCreate, UserUpdate, User, UserCreateOrUpdate } from '@/api/users' |
||||
|
|
||||
|
@Component({ |
||||
|
name: 'UserCreateOrUpdateForm', |
||||
|
methods: { |
||||
|
checkPermission |
||||
|
} |
||||
|
}) |
||||
|
export default class extends mixins(EventBusMiXin) { |
||||
|
@Prop({ default: false }) |
||||
|
private showDialog!: boolean |
||||
|
|
||||
|
@Prop({ default: '' }) |
||||
|
private editUserId!: string |
||||
|
|
||||
|
private title = '' |
||||
|
private activedTabPane = 'information' |
||||
|
private editUser = new User() |
||||
|
private currentUser = new UserCreateOrUpdate() |
||||
|
private currentUserRoles = new Array<string>() |
||||
|
private roleList = new Array<{key: string, label: string, disabled: boolean}>() |
||||
|
|
||||
|
private currentUserRoleChanged = false |
||||
|
|
||||
|
private currentUserRules = { |
||||
|
userName: [ |
||||
|
{ required: true, message: this.l('global.pleaseInputBy', { key: this.l('AbpIdentity.DisplayName:UserName') }), trigger: 'blur' } |
||||
|
], |
||||
|
email: [ |
||||
|
{ required: true, message: this.l('global.pleaseInputBy', { key: this.l('AbpIdentity.DisplayName:Email') }), trigger: 'blur' }, |
||||
|
{ type: 'email', message: this.l('AbpValidation.ThisFieldIsNotAValidEmailAddress'), trigger: ['blur', 'change'] } |
||||
|
], |
||||
|
password: [ |
||||
|
{ required: true, message: this.l('global.pleaseInputBy', { key: this.l('AbpIdentity.DisplayName:Password') }), trigger: 'blur' }, |
||||
|
{ min: this.requiredPasswordLength, message: this.l('AbpValidation.ThisFieldMustBeAStringWithAMinimumLengthOf', { 0: this.requiredPasswordLength }), trigger: 'blur' } |
||||
|
] |
||||
|
} |
||||
|
|
||||
|
get requiredPasswordLength() { |
||||
|
if (AbpModule.configuration) { |
||||
|
const setting = AbpModule.configuration.setting.values['Abp.Identity.Password.RequiredLength'] |
||||
|
if (setting) { |
||||
|
return Number(setting) |
||||
|
} |
||||
|
} |
||||
|
return 3 |
||||
|
} |
||||
|
|
||||
|
get isEditUser() { |
||||
|
if (this.editUserId) { |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
@Watch('showDialog', { immediate: true }) |
||||
|
private onShowDialogChanged() { |
||||
|
this.handleGetUser() |
||||
|
if (this.editUserId) { |
||||
|
this.title = this.l('AbpIdentity.Edit') |
||||
|
} else { |
||||
|
this.title = this.l('AbpIdentity.NewUser') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Watch('currentUserRoles') |
||||
|
onUserRolesChanged(val: string[], oldVal: string[]) { |
||||
|
if (val.length !== oldVal.length) { |
||||
|
this.currentUserRoleChanged = true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
mounted() { |
||||
|
this.handleGetRoles() |
||||
|
} |
||||
|
|
||||
|
private handleGetRoles() { |
||||
|
RoleService.getAllRoles().then(data => { |
||||
|
const roles = data.items.map((item: IRoleData) => { |
||||
|
return { |
||||
|
key: item.name, |
||||
|
label: item.name, |
||||
|
disabled: !item.isPublic |
||||
|
} |
||||
|
}) |
||||
|
this.roleList = roles |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
private handleGetUser() { |
||||
|
this.activedTabPane = 'information' |
||||
|
this.editUser = new User() |
||||
|
this.currentUser = new UserCreateOrUpdate() |
||||
|
this.currentUserRoles = new Array<string>() |
||||
|
if (this.showDialog && this.editUserId) { |
||||
|
UserApiService.getUserById(this.editUserId).then(user => { |
||||
|
this.editUser = user |
||||
|
this.currentUser.name = user.name |
||||
|
this.currentUser.userName = user.userName |
||||
|
this.currentUser.surname = user.surname |
||||
|
this.currentUser.email = user.email |
||||
|
this.currentUser.phoneNumber = user.phoneNumber |
||||
|
this.currentUser.lockoutEnabled = user.lockoutEnabled |
||||
|
}) |
||||
|
UserApiService.getUserRoles(this.editUserId).then(roles => { |
||||
|
this.currentUserRoles = roles.items.map(role => role.name) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private onSave() { |
||||
|
const editUserForm = this.$refs.editUserForm as any |
||||
|
editUserForm.validate(async(valid: boolean) => { |
||||
|
if (valid) { |
||||
|
if (this.isEditUser) { |
||||
|
const updateUser = new UserUpdate() |
||||
|
this.updateUserByInput(updateUser) |
||||
|
updateUser.concurrencyStamp = this.editUser.concurrencyStamp |
||||
|
updateUser.password = null |
||||
|
UserApiService.updateUser(this.editUserId, updateUser) |
||||
|
.then(user => { |
||||
|
this.trigger('userChanged', user) |
||||
|
this.onFormClosed() |
||||
|
}) |
||||
|
} else { |
||||
|
const createUser = new UserCreate() |
||||
|
this.updateUserByInput(createUser) |
||||
|
createUser.password = this.currentUser.password |
||||
|
UserApiService.createUser(createUser) |
||||
|
.then(user => { |
||||
|
this.trigger('userChanged', user) |
||||
|
this.onFormClosed() |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
private updateUserByInput(user: UserCreateOrUpdate) { |
||||
|
user.name = this.currentUser.name |
||||
|
user.userName = this.currentUser.userName |
||||
|
user.surname = this.currentUser.surname |
||||
|
user.email = this.currentUser.email |
||||
|
user.phoneNumber = this.currentUser.phoneNumber |
||||
|
user.lockoutEnabled = this.currentUser.lockoutEnabled |
||||
|
if (this.currentUserRoleChanged) { |
||||
|
user.roleNames = this.currentUserRoles |
||||
|
} else { |
||||
|
user.roleNames = null |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private onFormClosed() { |
||||
|
this.resetForm() |
||||
|
this.$emit('closed') |
||||
|
} |
||||
|
|
||||
|
private resetForm() { |
||||
|
this.activedTabPane = 'infomation' |
||||
|
const editUserForm = this.$refs.editUserForm as any |
||||
|
editUserForm.resetFields() |
||||
|
} |
||||
|
|
||||
|
private l(name: string, values?: any[] | { [key: string]: any }) { |
||||
|
return this.$t(name, values).toString() |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.confirm { |
||||
|
position: absolute; |
||||
|
right: 10px; |
||||
|
width: 100px |
||||
|
} |
||||
|
.cancel { |
||||
|
position: absolute; |
||||
|
right: 120px; |
||||
|
width: 100px |
||||
|
} |
||||
|
</style> |
||||
@ -1,373 +0,0 @@ |
|||||
<template> |
|
||||
<el-form |
|
||||
ref="formEditUser" |
|
||||
label-width="110px" |
|
||||
:model="userProfile" |
|
||||
:rules="userProfileRules" |
|
||||
> |
|
||||
<el-tabs v-model="activedTabPane"> |
|
||||
<el-tab-pane |
|
||||
:label="$t('userProfile.basic')" |
|
||||
name="basic" |
|
||||
> |
|
||||
<el-form-item |
|
||||
prop="userName" |
|
||||
:label="$t('users.userName')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="userProfile.userName" |
|
||||
:placeholder="$t('userProfile.pleaseInputUserName')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="name" |
|
||||
:label="$t('users.name')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="userProfile.name" |
|
||||
:placeholder="$t('userProfile.pleaseInputName')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="surname" |
|
||||
:label="$t('users.surname')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="userProfile.surname" |
|
||||
:placeholder="$t('userProfile.pleaseInputSurname')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="phoneNumber" |
|
||||
:label="$t('users.phoneNumber')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="userProfile.phoneNumber" |
|
||||
:placeholder="$t('userProfile.pleaseInputPhoneNumber')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="email" |
|
||||
:label="$t('users.email')" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="userProfile.email" |
|
||||
:placeholder="$t('userProfile.pleaseInputEmail')" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
</el-tab-pane> |
|
||||
<el-tab-pane |
|
||||
:label="$t('userProfile.security')" |
|
||||
name="security" |
|
||||
> |
|
||||
<el-form-item |
|
||||
prop="twoFactorEnabled" |
|
||||
label-width="100px" |
|
||||
:label="$t('users.twoFactorEnabled')" |
|
||||
> |
|
||||
<el-switch v-model="userProfile.twoFactorEnabled" /> |
|
||||
</el-form-item> |
|
||||
<el-form-item |
|
||||
prop="lockoutEnabled" |
|
||||
label-width="100px" |
|
||||
:label="$t('users.lockoutEnabled')" |
|
||||
> |
|
||||
<el-switch v-model="userProfile.lockoutEnabled" /> |
|
||||
</el-form-item> |
|
||||
</el-tab-pane> |
|
||||
<el-tab-pane |
|
||||
:label="$t('userProfile.roles')" |
|
||||
name="roles" |
|
||||
> |
|
||||
<el-transfer |
|
||||
v-model="userRoles" |
|
||||
:titles="[$t('userProfile.roleList'),$t('userProfile.hasRoles')]" |
|
||||
:data="roleList" |
|
||||
/> |
|
||||
</el-tab-pane> |
|
||||
<el-tab-pane |
|
||||
:label="$t('userProfile.organizationUnits')" |
|
||||
> |
|
||||
<organization-unit-tree |
|
||||
:checked-organization-units="userOrganizationUnits" |
|
||||
@onOrganizationUnitsChanged="onOrganizationUnitsChanged" |
|
||||
/> |
|
||||
</el-tab-pane> |
|
||||
<el-tab-pane |
|
||||
v-if="allowedChangePermissions() && hasLoadPermission" |
|
||||
:label="$t('userProfile.permission')" |
|
||||
> |
|
||||
<PermissionTree |
|
||||
ref="permissionTree" |
|
||||
:expanded="false" |
|
||||
:readonly="!checkPermission(['AbpIdentity.Users.ManagePermissions'])" |
|
||||
:permission="userPermission" |
|
||||
@onPermissionChanged="onPermissionChanged" |
|
||||
/> |
|
||||
</el-tab-pane> |
|
||||
</el-tabs> |
|
||||
<el-form-item> |
|
||||
<el-button |
|
||||
class="cancel" |
|
||||
style="width:100px" |
|
||||
@click="onCancel" |
|
||||
> |
|
||||
{{ $t('table.cancel') }} |
|
||||
</el-button> |
|
||||
<el-button |
|
||||
class="confirm" |
|
||||
type="primary" |
|
||||
style="width:100px" |
|
||||
@click="onSubmit" |
|
||||
> |
|
||||
{{ $t('table.confirm') }} |
|
||||
</el-button> |
|
||||
</el-form-item> |
|
||||
</el-form> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { Component, Vue, Prop, Watch } from 'vue-property-decorator' |
|
||||
import UserApiService, { |
|
||||
UserDataDto, |
|
||||
UserUpdateDto, |
|
||||
ChangeUserOrganizationUnitDto |
|
||||
} from '@/api/users' |
|
||||
import RoleService from '@/api/roles' |
|
||||
import PermissionService, { PermissionDto, UpdatePermissionsDto } from '@/api/permission' |
|
||||
import PermissionTree from '@/components/PermissionTree/index.vue' |
|
||||
import OrganizationUnitTree from '@/components/OrganizationUnitTree/index.vue' |
|
||||
import { IRoleData, IPermission } from '@/api/types' |
|
||||
import { checkPermission } from '@/utils/permission' |
|
||||
|
|
||||
@Component({ |
|
||||
name: 'UserProfile', |
|
||||
components: { |
|
||||
PermissionTree, |
|
||||
OrganizationUnitTree |
|
||||
}, |
|
||||
methods: { |
|
||||
checkPermission |
|
||||
} |
|
||||
}) |
|
||||
export default class extends Vue { |
|
||||
@Prop({ default: '' }) |
|
||||
private userId!: string |
|
||||
|
|
||||
private roleList = new Array<{key: string, label: string, disabled: boolean}>() |
|
||||
/** 用户组 */ |
|
||||
private userRoles = new Array<string>() |
|
||||
private hasEditUser = false |
|
||||
private userRolesChanged = false |
|
||||
private userProfile = new UserDataDto() |
|
||||
/** 是否加载用户权限 */ |
|
||||
private hasLoadPermission = false |
|
||||
/** 用户权限数据 */ |
|
||||
private userPermission = new PermissionDto() |
|
||||
/** 用户权限已变更 */ |
|
||||
private userPermissionChanged = false |
|
||||
/** 变更用户权限数据 */ |
|
||||
private editUserPermissions = new Array<IPermission>() |
|
||||
/** 用户组织机构 */ |
|
||||
private userOrganizationUnits = new Array<string>() |
|
||||
private userOrganizationUnitsChanged = false |
|
||||
|
|
||||
private activedTabPane = 'basic' |
|
||||
|
|
||||
@Watch('userId', { immediate: true }) |
|
||||
onUserIdChanged(userId: string) { |
|
||||
if (userId) { |
|
||||
this.handleGetUserProfile() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
onUserRolesChanged(val: string[], oldVal: string[]) { |
|
||||
if (val.length !== oldVal.length) { |
|
||||
this.userRolesChanged = true |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
onUserOrganizationUnitsChanged(val: string[], oldVal: string[]) { |
|
||||
if (val.length !== oldVal.length) { |
|
||||
this.userRolesChanged = true |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
onOrganizationUnitsChanged(checkedKeys: string[]) { |
|
||||
this.userOrganizationUnitsChanged = true |
|
||||
this.userOrganizationUnits = checkedKeys |
|
||||
} |
|
||||
|
|
||||
private validatePhoneNumberValue = (rule: any, value: string, callback: any) => { |
|
||||
const phoneReg = /^1[34578]\d{9}$/ |
|
||||
if (!value || !phoneReg.test(value)) { |
|
||||
callback(new Error(this.l('global.pleaseInputBy', { key: this.l('global.correctPhoneNumber') }))) |
|
||||
} else { |
|
||||
callback() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private userProfileRules = { |
|
||||
userName: [ |
|
||||
{ required: true, message: this.l('userProfile.pleaseInputUserName'), trigger: 'blur' }, |
|
||||
{ min: 3, max: 20, message: this.l('global.charLengthRange', { min: 3, max: 20 }), trigger: 'blur' } |
|
||||
], |
|
||||
name: [ |
|
||||
{ required: true, message: this.l('userProfile.pleaseInputName'), trigger: 'blur' }, |
|
||||
{ min: 3, max: 50, message: this.l('global.charLengthRange', { min: 3, max: 50 }), trigger: 'blur' } |
|
||||
], |
|
||||
email: [ |
|
||||
{ required: true, message: this.l('userProfile.pleaseInputEmail'), trigger: 'blur' }, |
|
||||
{ type: 'email', message: this.l('global.pleaseInputBy', { key: this.l('global.correctEmailAddress') }), trigger: ['blur', 'change'] } |
|
||||
], |
|
||||
password: [ |
|
||||
{ required: true, message: this.l('userProfile.pleaseInputPassword'), trigger: 'blur' }, |
|
||||
{ min: 6, max: 15, message: this.l('global.charLengthRange', { min: 6, max: 15 }), trigger: 'blur' } |
|
||||
], |
|
||||
phoneNumber: [ |
|
||||
{ required: true, validator: this.validatePhoneNumberValue, trigger: 'blur' } |
|
||||
] |
|
||||
} |
|
||||
|
|
||||
mounted() { |
|
||||
this.handleGetRoles() |
|
||||
if (this.userId) { |
|
||||
this.handleGetUserProfile() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** 允许变更权限 */ |
|
||||
private allowedChangePermissions() { |
|
||||
return checkPermission(['AbpIdentity.Users.ManagePermissions']) |
|
||||
} |
|
||||
|
|
||||
private handleGetUserProfile() { |
|
||||
this.userRoles = new Array<string>() |
|
||||
this.userPermission = new PermissionDto() |
|
||||
UserApiService.getUserById(this.userId).then(user => { |
|
||||
this.userProfile = user |
|
||||
this.handleGetUserRoles(this.userId) |
|
||||
this.handleGetUserOrganizationUnits(this.userId) |
|
||||
if (this.allowedChangePermissions()) { |
|
||||
this.handleGetUserPermissions(this.userId) |
|
||||
} |
|
||||
this.hasEditUser = true |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
private handleGetRoles() { |
|
||||
RoleService.getAllRoles().then(data => { |
|
||||
const roles = data.items.map((item: IRoleData) => { |
|
||||
return { |
|
||||
key: item.name, |
|
||||
label: item.name, |
|
||||
disabled: !item.isPublic |
|
||||
} |
|
||||
}) |
|
||||
this.roleList = roles |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
private async handleGetUserRoles(userId: string) { |
|
||||
const userRoleDto = await UserApiService.getUserRoles(userId) |
|
||||
this.userRoles = userRoleDto.items.map(r => r.name) |
|
||||
// 监听用户组变化 |
|
||||
this.$watch('userRoles', this.onUserRolesChanged) |
|
||||
} |
|
||||
|
|
||||
private handleGetUserOrganizationUnits(userId: string) { |
|
||||
UserApiService.getUserOrganizationUnits(userId).then(res => { |
|
||||
this.userOrganizationUnits = res.items.map(ou => ou.id) |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
private async handleGetUserPermissions(id: string) { |
|
||||
PermissionService.getPermissionsByKey('U', id).then(permission => { |
|
||||
this.userPermission = permission |
|
||||
this.hasLoadPermission = true |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
private onPermissionChanged(permissions: IPermission[]) { |
|
||||
this.userPermissionChanged = true |
|
||||
this.editUserPermissions = permissions |
|
||||
} |
|
||||
|
|
||||
private onSubmit() { |
|
||||
const frmEditUser = this.$refs.formEditUser as any |
|
||||
frmEditUser.validate(async(valid: boolean) => { |
|
||||
if (valid) { |
|
||||
const updateUserInput = this.createEditUserDto() |
|
||||
const user = await UserApiService.updateUser(this.userProfile.id, updateUserInput) |
|
||||
this.userProfile = user |
|
||||
this.$message.success(this.l('users.updateUserSuccess', { name: this.userProfile.name })) |
|
||||
if (this.userRolesChanged) { |
|
||||
await UserApiService.setUserRoles(this.userProfile.id, this.userRoles) |
|
||||
} |
|
||||
if (this.userOrganizationUnitsChanged) { |
|
||||
const changeUserOrganizationUnitDto = new ChangeUserOrganizationUnitDto() |
|
||||
changeUserOrganizationUnitDto.organizationUnitIds = this.userOrganizationUnits |
|
||||
await UserApiService.changeUserOrganizationUnits(this.userProfile.id, changeUserOrganizationUnitDto) |
|
||||
} |
|
||||
if (this.userPermissionChanged) { |
|
||||
const setUserPermissions = new UpdatePermissionsDto() |
|
||||
setUserPermissions.permissions = this.editUserPermissions |
|
||||
await PermissionService.setPermissionsByKey('U', this.userProfile.id, setUserPermissions) |
|
||||
} |
|
||||
frmEditUser.resetFields() |
|
||||
this.onCancel() |
|
||||
this.$emit('onUserProfileChanged', this.userProfile.id) |
|
||||
} else { |
|
||||
return false |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
private onCancel() { |
|
||||
this.resetForm() |
|
||||
this.$emit('onClose') |
|
||||
} |
|
||||
|
|
||||
private resetForm() { |
|
||||
this.activedTabPane = 'basic' |
|
||||
this.userRoles = new Array<string>() |
|
||||
this.userOrganizationUnits = new Array<string>() |
|
||||
const frmEditUser = this.$refs.formEditUser as any |
|
||||
frmEditUser.resetFields() |
|
||||
if (this.hasLoadPermission) { |
|
||||
const userPermission = this.$refs.permissionTree as PermissionTree |
|
||||
userPermission.resetPermissions() |
|
||||
this.hasLoadPermission = false |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private createEditUserDto() { |
|
||||
const updateUserInput = new UserUpdateDto() |
|
||||
updateUserInput.name = this.userProfile.name |
|
||||
updateUserInput.userName = this.userProfile.userName |
|
||||
updateUserInput.surname = this.userProfile.surname |
|
||||
updateUserInput.email = this.userProfile.email |
|
||||
updateUserInput.phoneNumber = this.userProfile.phoneNumber |
|
||||
updateUserInput.twoFactorEnabled = this.userProfile.twoFactorEnabled |
|
||||
updateUserInput.lockoutEnabled = this.userProfile.lockoutEnabled |
|
||||
updateUserInput.roles = this.userRoles |
|
||||
updateUserInput.concurrencyStamp = this.userProfile.concurrencyStamp |
|
||||
return updateUserInput |
|
||||
} |
|
||||
|
|
||||
private l(name: string, values?: any[] | { [key: string]: any }) { |
|
||||
return this.$t(name, values).toString() |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.confirm { |
|
||||
position: absolute; |
|
||||
right: 10px; |
|
||||
} |
|
||||
.cancel { |
|
||||
position: absolute; |
|
||||
right: 120px; |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,396 +0,0 @@ |
|||||
<template> |
|
||||
<div class="createPost-container"> |
|
||||
<el-form |
|
||||
ref="postForm" |
|
||||
:model="postForm" |
|
||||
:rules="rules" |
|
||||
class="form-container" |
|
||||
> |
|
||||
<sticky |
|
||||
:z-index="10" |
|
||||
:class-name="'sub-navbar '+postForm.status" |
|
||||
> |
|
||||
<comment-dropdown v-model="postForm.disableComment" /> |
|
||||
<platform-dropdown v-model="postForm.platforms" /> |
|
||||
<source-url-dropdown v-model="postForm.sourceURL" /> |
|
||||
<el-button |
|
||||
v-loading="loading" |
|
||||
style="margin-left: 10px;" |
|
||||
type="success" |
|
||||
@click="submitForm" |
|
||||
> |
|
||||
Publish |
|
||||
</el-button> |
|
||||
<el-button |
|
||||
v-loading="loading" |
|
||||
type="warning" |
|
||||
@click="draftForm" |
|
||||
> |
|
||||
Draft |
|
||||
</el-button> |
|
||||
</sticky> |
|
||||
|
|
||||
<div class="createPost-main-container"> |
|
||||
<el-row> |
|
||||
<warning /> |
|
||||
|
|
||||
<el-col :span="24"> |
|
||||
<el-form-item |
|
||||
style="margin-bottom: 40px;" |
|
||||
prop="title" |
|
||||
> |
|
||||
<material-input |
|
||||
v-model="postForm.title" |
|
||||
:maxlength="100" |
|
||||
name="name" |
|
||||
required |
|
||||
> |
|
||||
Title |
|
||||
</material-input> |
|
||||
</el-form-item> |
|
||||
|
|
||||
<div class="postInfo-container"> |
|
||||
<el-row> |
|
||||
<el-col :span="8"> |
|
||||
<el-form-item |
|
||||
label-width="60px" |
|
||||
label="Author:" |
|
||||
class="postInfo-container-item" |
|
||||
> |
|
||||
<el-select |
|
||||
v-model="postForm.author" |
|
||||
:remote-method="getRemoteUserList" |
|
||||
filterable |
|
||||
default-first-option |
|
||||
remote |
|
||||
placeholder="Search user" |
|
||||
> |
|
||||
<el-option |
|
||||
v-for="(item, index) in userListOptions" |
|
||||
:key="item+index" |
|
||||
:label="item" |
|
||||
:value="item" |
|
||||
/> |
|
||||
</el-select> |
|
||||
</el-form-item> |
|
||||
</el-col> |
|
||||
|
|
||||
<el-col :span="10"> |
|
||||
<el-form-item |
|
||||
label-width="120px" |
|
||||
label="Publish Time:" |
|
||||
class="postInfo-container-item" |
|
||||
> |
|
||||
<el-date-picker |
|
||||
v-model="timestamp" |
|
||||
type="datetime" |
|
||||
format="yyyy-MM-dd HH:mm:ss" |
|
||||
placeholder="Select date and time" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
</el-col> |
|
||||
|
|
||||
<el-col :span="6"> |
|
||||
<el-form-item |
|
||||
label-width="90px" |
|
||||
label="Importance:" |
|
||||
class="postInfo-container-item" |
|
||||
> |
|
||||
<el-rate |
|
||||
v-model="postForm.importance" |
|
||||
:max="3" |
|
||||
:colors="['#99A9BF', '#F7BA2A', '#FF9900']" |
|
||||
:low-threshold="1" |
|
||||
:high-threshold="3" |
|
||||
style="display:inline-block" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
</el-col> |
|
||||
</el-row> |
|
||||
</div> |
|
||||
</el-col> |
|
||||
</el-row> |
|
||||
|
|
||||
<el-form-item |
|
||||
style="margin-bottom: 40px;" |
|
||||
label-width="70px" |
|
||||
label="Summary:" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="postForm.abstractContent" |
|
||||
:rows="1" |
|
||||
type="textarea" |
|
||||
class="article-textarea" |
|
||||
autosize |
|
||||
placeholder="Please enter the content" |
|
||||
/> |
|
||||
<span |
|
||||
v-show="abstractContentLength" |
|
||||
class="word-counter" |
|
||||
>{{ abstractContentLength }}words</span> |
|
||||
</el-form-item> |
|
||||
|
|
||||
<el-form-item |
|
||||
prop="content" |
|
||||
style="margin-bottom: 30px;" |
|
||||
> |
|
||||
<tinymce |
|
||||
v-if="tinymceActive" |
|
||||
ref="editor" |
|
||||
v-model="postForm.fullContent" |
|
||||
:height="400" |
|
||||
/> |
|
||||
</el-form-item> |
|
||||
|
|
||||
<el-form-item |
|
||||
prop="imageURL" |
|
||||
style="margin-bottom: 30px;" |
|
||||
> |
|
||||
<upload-image v-model="postForm.imageURL" /> |
|
||||
</el-form-item> |
|
||||
</div> |
|
||||
</el-form> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator' |
|
||||
import { isValidURL } from '@/utils/validate' |
|
||||
import { getArticle, defaultArticleData } from '@/api/articles' |
|
||||
import UserApiService, { UsersGetPagedDto, UserDataDto } from '@/api/users' |
|
||||
import { AppModule } from '@/store/modules/app' |
|
||||
import { TagsViewModule, ITagView } from '@/store/modules/tags-view' |
|
||||
import MaterialInput from '@/components/MaterialInput/index.vue' |
|
||||
import Sticky from '@/components/Sticky/index.vue' |
|
||||
import Tinymce from '@/components/Tinymce/index.vue' |
|
||||
import UploadImage from '@/components/UploadImage/index.vue' |
|
||||
import Warning from './Warning.vue' |
|
||||
import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown' |
|
||||
import { Form } from 'element-ui' |
|
||||
|
|
||||
@Component({ |
|
||||
name: 'ArticleDetail', |
|
||||
components: { |
|
||||
CommentDropdown, |
|
||||
PlatformDropdown, |
|
||||
SourceUrlDropdown, |
|
||||
MaterialInput, |
|
||||
Sticky, |
|
||||
Tinymce, |
|
||||
UploadImage, |
|
||||
Warning |
|
||||
} |
|
||||
}) |
|
||||
export default class extends Vue { |
|
||||
@Prop({ default: false }) private isEdit!: boolean |
|
||||
private userApiService: UserApiService |
|
||||
|
|
||||
constructor() { |
|
||||
super() |
|
||||
this.userApiService = new UserApiService() |
|
||||
} |
|
||||
|
|
||||
private validateRequire = (rule: any, value: string, callback: Function) => { |
|
||||
if (value === '') { |
|
||||
if (rule.field === 'imageURL') { |
|
||||
this.$message({ |
|
||||
message: 'Upload cover image is required', |
|
||||
type: 'error' |
|
||||
}) |
|
||||
} else { |
|
||||
this.$message({ |
|
||||
message: rule.field + ' is required', |
|
||||
type: 'error' |
|
||||
}) |
|
||||
} |
|
||||
callback(new Error(rule.field + ' is required')) |
|
||||
} else { |
|
||||
callback() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private validateSourceUrl = (rule: any, value: string, callback: any) => { |
|
||||
if (value) { |
|
||||
if (isValidURL(value)) { |
|
||||
callback() |
|
||||
} else { |
|
||||
this.$message({ |
|
||||
message: 'Invalid URL', |
|
||||
type: 'error' |
|
||||
}) |
|
||||
callback(new Error('Invalid URL')) |
|
||||
} |
|
||||
} else { |
|
||||
callback() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private postForm = Object.assign({}, defaultArticleData) |
|
||||
private loading = false |
|
||||
private userListOptions = new Array<string>() |
|
||||
private rules = { |
|
||||
imageURL: [{ validator: this.validateRequire }], |
|
||||
title: [{ validator: this.validateRequire }], |
|
||||
fullContent: [{ validator: this.validateRequire }], |
|
||||
sourceURL: [{ validator: this.validateSourceUrl, trigger: 'blur' }] |
|
||||
} |
|
||||
|
|
||||
private tempTagView?: ITagView |
|
||||
private tinymceActive = true |
|
||||
|
|
||||
get abstractContentLength() { |
|
||||
return this.postForm.abstractContent.length |
|
||||
} |
|
||||
|
|
||||
get lang() { |
|
||||
return AppModule.language |
|
||||
} |
|
||||
|
|
||||
// set and get is useful when the data |
|
||||
// returned by the backend api is different from the frontend |
|
||||
// e.g.: backend return => "2013-06-25 06:59:25" |
|
||||
// frontend need timestamp => 1372114765000 |
|
||||
get timestamp() { |
|
||||
return (+new Date(this.postForm.timestamp)) |
|
||||
} |
|
||||
|
|
||||
set timestamp(value) { |
|
||||
this.postForm.timestamp = +new Date(value) |
|
||||
} |
|
||||
|
|
||||
created() { |
|
||||
if (this.isEdit) { |
|
||||
const id = this.$route.params && this.$route.params.id |
|
||||
this.fetchData(parseInt(id)) |
|
||||
} |
|
||||
// Why need to make a copy of this.$route here? |
|
||||
// Because if you enter this page and quickly switch tag, may be in the execution of this.setTagsViewTitle function, this.$route is no longer pointing to the current page |
|
||||
// https://github.com/PanJiaChen/vue-element-admin/issues/1221 |
|
||||
this.tempTagView = Object.assign({}, this.$route) |
|
||||
} |
|
||||
|
|
||||
deactivated() { |
|
||||
this.tinymceActive = false |
|
||||
} |
|
||||
|
|
||||
activated() { |
|
||||
this.tinymceActive = true |
|
||||
} |
|
||||
|
|
||||
private async fetchData(id: number) { |
|
||||
try { |
|
||||
const { data } = await getArticle(id, { /* Your params here */ }) |
|
||||
this.postForm = data.article |
|
||||
// Just for test |
|
||||
this.postForm.title += ` Article Id:${this.postForm.id}` |
|
||||
this.postForm.abstractContent += ` Article Id:${this.postForm.id}` |
|
||||
const title = this.lang === 'zh' ? '编辑文章' : 'Edit Article' |
|
||||
// Set tagsview title |
|
||||
this.setTagsViewTitle(title) |
|
||||
// Set page title |
|
||||
this.setPageTitle(title) |
|
||||
} catch (err) { |
|
||||
console.error(err) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private setTagsViewTitle(title: string) { |
|
||||
const tagView = this.tempTagView |
|
||||
if (tagView) { |
|
||||
tagView.title = `${title}-${this.postForm.id}` |
|
||||
TagsViewModule.updateVisitedView(tagView) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private setPageTitle(title: string) { |
|
||||
document.title = `${title} - ${this.postForm.id}` |
|
||||
} |
|
||||
|
|
||||
private submitForm() { |
|
||||
(this.$refs.postForm as Form).validate(valid => { |
|
||||
if (valid) { |
|
||||
this.loading = true |
|
||||
this.$notify({ |
|
||||
title: 'Success', |
|
||||
message: 'The post published successfully', |
|
||||
type: 'success', |
|
||||
duration: 2000 |
|
||||
}) |
|
||||
this.postForm.status = 'published' |
|
||||
// Just to simulate the time of the request |
|
||||
setTimeout(() => { |
|
||||
this.loading = false |
|
||||
}, 0.5 * 1000) |
|
||||
} else { |
|
||||
console.error('Submit Error!') |
|
||||
return false |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
private draftForm() { |
|
||||
if (this.postForm.fullContent.length === 0 || this.postForm.title.length === 0) { |
|
||||
this.$message({ |
|
||||
message: 'Title and detail content are required', |
|
||||
type: 'warning' |
|
||||
}) |
|
||||
return |
|
||||
} |
|
||||
this.$message({ |
|
||||
message: 'The draft saved successfully', |
|
||||
type: 'success', |
|
||||
showClose: true, |
|
||||
duration: 1000 |
|
||||
}) |
|
||||
this.postForm.status = 'draft' |
|
||||
} |
|
||||
|
|
||||
private async getRemoteUserList(name: string) { |
|
||||
const getUsersData = new UsersGetPagedDto() |
|
||||
getUsersData.filter = name |
|
||||
UserApiService.getUsers(getUsersData).then(res => { |
|
||||
if (!res.items) return |
|
||||
this.userListOptions = res.items.map((v: UserDataDto) => v.name) |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss"> |
|
||||
.article-textarea { |
|
||||
textarea { |
|
||||
padding-right: 40px; |
|
||||
resize: none; |
|
||||
border: none; |
|
||||
border-radius: 0px; |
|
||||
border-bottom: 1px solid #bfcbd9; |
|
||||
} |
|
||||
} |
|
||||
</style> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.createPost-container { |
|
||||
position: relative; |
|
||||
|
|
||||
.createPost-main-container { |
|
||||
padding: 40px 45px 20px 50px; |
|
||||
|
|
||||
.postInfo-container { |
|
||||
position: relative; |
|
||||
@include clearfix; |
|
||||
margin-bottom: 10px; |
|
||||
|
|
||||
.postInfo-container-item { |
|
||||
float: left; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.word-counter { |
|
||||
width: 40px; |
|
||||
position: absolute; |
|
||||
right: 10px; |
|
||||
top: 0px; |
|
||||
} |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,48 +0,0 @@ |
|||||
<template> |
|
||||
<el-dropdown |
|
||||
:show-timeout="100" |
|
||||
trigger="click" |
|
||||
> |
|
||||
<el-button plain> |
|
||||
{{ !disableComment?'Comment: opened':'Comment: closed' }} |
|
||||
<i class="el-icon-caret-bottom el-icon--right" /> |
|
||||
</el-button> |
|
||||
<el-dropdown-menu |
|
||||
slot="dropdown" |
|
||||
class="no-padding" |
|
||||
> |
|
||||
<el-dropdown-item> |
|
||||
<el-radio-group |
|
||||
v-model="disableComment" |
|
||||
style="padding: 10px;" |
|
||||
> |
|
||||
<el-radio :label="true"> |
|
||||
Close comment |
|
||||
</el-radio> |
|
||||
<el-radio :label="false"> |
|
||||
Open comment |
|
||||
</el-radio> |
|
||||
</el-radio-group> |
|
||||
</el-dropdown-item> |
|
||||
</el-dropdown-menu> |
|
||||
</el-dropdown> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator' |
|
||||
|
|
||||
@Component({ |
|
||||
name: 'CommentDropdown' |
|
||||
}) |
|
||||
export default class extends Vue { |
|
||||
@Prop({ required: true }) private value!: boolean |
|
||||
|
|
||||
get disableComment() { |
|
||||
return this.value |
|
||||
} |
|
||||
|
|
||||
set disableComment(value) { |
|
||||
this.$emit('input', value) |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
@ -1,51 +0,0 @@ |
|||||
<template> |
|
||||
<el-dropdown |
|
||||
:hide-on-click="false" |
|
||||
:show-timeout="100" |
|
||||
trigger="click" |
|
||||
> |
|
||||
<el-button plain> |
|
||||
Platfroms({{ platforms.length }}) |
|
||||
<i class="el-icon-caret-bottom el-icon--right" /> |
|
||||
</el-button> |
|
||||
<el-dropdown-menu slot="dropdown"> |
|
||||
<el-checkbox-group |
|
||||
v-model="platforms" |
|
||||
style="padding: 5px 15px;" |
|
||||
> |
|
||||
<el-checkbox |
|
||||
v-for="item in platformsOptions" |
|
||||
:key="item.key" |
|
||||
:label="item.key" |
|
||||
> |
|
||||
{{ item.name }} |
|
||||
</el-checkbox> |
|
||||
</el-checkbox-group> |
|
||||
</el-dropdown-menu> |
|
||||
</el-dropdown> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator' |
|
||||
|
|
||||
@Component({ |
|
||||
name: 'PlatformDropdown' |
|
||||
}) |
|
||||
export default class extends Vue { |
|
||||
@Prop({ required: true }) private value!: string[] |
|
||||
|
|
||||
private platformsOptions = [ |
|
||||
{ key: 'a-platform', name: 'a-platform' }, |
|
||||
{ key: 'b-platform', name: 'b-platform' }, |
|
||||
{ key: 'c-platform', name: 'c-platform' } |
|
||||
] |
|
||||
|
|
||||
get platforms() { |
|
||||
return this.value |
|
||||
} |
|
||||
|
|
||||
set platforms(value) { |
|
||||
this.$emit('input', value) |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
@ -1,50 +0,0 @@ |
|||||
<template> |
|
||||
<el-dropdown |
|
||||
:show-timeout="100" |
|
||||
trigger="click" |
|
||||
> |
|
||||
<el-button plain> |
|
||||
Link |
|
||||
<i class="el-icon-caret-bottom el-icon--right" /> |
|
||||
</el-button> |
|
||||
<el-dropdown-menu |
|
||||
slot="dropdown" |
|
||||
class="no-padding" |
|
||||
style="width:400px" |
|
||||
> |
|
||||
<el-form-item |
|
||||
label-width="0px" |
|
||||
style="margin-bottom: 0px" |
|
||||
prop="sourceURL" |
|
||||
> |
|
||||
<el-input |
|
||||
v-model="sourceURL" |
|
||||
placeholder="Please enter the content" |
|
||||
> |
|
||||
<template slot="prepend"> |
|
||||
URL |
|
||||
</template> |
|
||||
</el-input> |
|
||||
</el-form-item> |
|
||||
</el-dropdown-menu> |
|
||||
</el-dropdown> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator' |
|
||||
|
|
||||
@Component({ |
|
||||
name: 'SourceUrlDropdown' |
|
||||
}) |
|
||||
export default class extends Vue { |
|
||||
@Prop({ required: true }) private value!: string |
|
||||
|
|
||||
get sourceURL() { |
|
||||
return this.value |
|
||||
} |
|
||||
|
|
||||
set sourceURL(value) { |
|
||||
this.$emit('input', value) |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
@ -1,3 +0,0 @@ |
|||||
export { default as CommentDropdown } from './Comment.vue' |
|
||||
export { default as PlatformDropdown } from './Platform.vue' |
|
||||
export { default as SourceUrlDropdown } from './SourceUrl.vue' |
|
||||
@ -1,18 +0,0 @@ |
|||||
<template> |
|
||||
<aside> |
|
||||
{{ $t('example.warning') }} |
|
||||
<a |
|
||||
href="https://armour.github.io/vue-typescript-admin-docs/guide/essentials/tags-view.html" |
|
||||
target="_blank" |
|
||||
>Document</a> |
|
||||
</aside> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { Component, Vue } from 'vue-property-decorator' |
|
||||
|
|
||||
@Component({ |
|
||||
name: 'Warning' |
|
||||
}) |
|
||||
export default class extends Vue {} |
|
||||
</script> |
|
||||
@ -1,16 +0,0 @@ |
|||||
<template> |
|
||||
<article-detail :is-edit="false" /> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { Component, Vue } from 'vue-property-decorator' |
|
||||
import ArticleDetail from './components/ArticleDetail.vue' |
|
||||
|
|
||||
@Component({ |
|
||||
name: 'CreateArticle', |
|
||||
components: { |
|
||||
ArticleDetail |
|
||||
} |
|
||||
}) |
|
||||
export default class extends Vue {} |
|
||||
</script> |
|
||||
@ -1,16 +0,0 @@ |
|||||
<template> |
|
||||
<article-detail :is-edit="true" /> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { Component, Vue } from 'vue-property-decorator' |
|
||||
import ArticleDetail from './components/ArticleDetail.vue' |
|
||||
|
|
||||
@Component({ |
|
||||
name: 'EditArticle', |
|
||||
components: { |
|
||||
ArticleDetail |
|
||||
} |
|
||||
}) |
|
||||
export default class extends Vue {} |
|
||||
</script> |
|
||||
@ -1,158 +0,0 @@ |
|||||
<template> |
|
||||
<div class="app-container"> |
|
||||
<el-table |
|
||||
v-loading="listLoading" |
|
||||
:data="list" |
|
||||
border |
|
||||
fit |
|
||||
highlight-current-row |
|
||||
style="width: 100%" |
|
||||
> |
|
||||
<el-table-column |
|
||||
width="80" |
|
||||
align="center" |
|
||||
label="ID" |
|
||||
> |
|
||||
<template slot-scope="scope"> |
|
||||
<span>{{ scope.row.id }}</span> |
|
||||
</template> |
|
||||
</el-table-column> |
|
||||
|
|
||||
<el-table-column |
|
||||
width="180px" |
|
||||
align="center" |
|
||||
label="Date" |
|
||||
> |
|
||||
<template slot-scope="scope"> |
|
||||
<span>{{ scope.row.timestamp | parseTime }}</span> |
|
||||
</template> |
|
||||
</el-table-column> |
|
||||
|
|
||||
<el-table-column |
|
||||
width="180px" |
|
||||
align="center" |
|
||||
label="Author" |
|
||||
> |
|
||||
<template slot-scope="scope"> |
|
||||
<span>{{ scope.row.author }}</span> |
|
||||
</template> |
|
||||
</el-table-column> |
|
||||
|
|
||||
<el-table-column |
|
||||
width="105px" |
|
||||
label="Importance" |
|
||||
> |
|
||||
<template slot-scope="scope"> |
|
||||
<svg-icon |
|
||||
v-for="n in +scope.row.importance" |
|
||||
:key="n" |
|
||||
name="star" |
|
||||
class="meta-item__icon" |
|
||||
/> |
|
||||
</template> |
|
||||
</el-table-column> |
|
||||
|
|
||||
<el-table-column |
|
||||
class-name="status-col" |
|
||||
label="Status" |
|
||||
width="110" |
|
||||
> |
|
||||
<template slot-scope="{row}"> |
|
||||
<el-tag :type="row.status | articleStatusFilter"> |
|
||||
{{ row.status }} |
|
||||
</el-tag> |
|
||||
</template> |
|
||||
</el-table-column> |
|
||||
|
|
||||
<el-table-column |
|
||||
min-width="300px" |
|
||||
label="Title" |
|
||||
> |
|
||||
<template slot-scope="{row}"> |
|
||||
<router-link |
|
||||
:to="'/example/edit/'+row.id" |
|
||||
class="link-type" |
|
||||
> |
|
||||
<span>{{ row.title }}</span> |
|
||||
</router-link> |
|
||||
</template> |
|
||||
</el-table-column> |
|
||||
|
|
||||
<el-table-column |
|
||||
align="center" |
|
||||
label="Actions" |
|
||||
width="120" |
|
||||
> |
|
||||
<template slot-scope="scope"> |
|
||||
<router-link :to="'/example/edit/'+scope.row.id"> |
|
||||
<el-button |
|
||||
type="primary" |
|
||||
size="small" |
|
||||
icon="el-icon-edit" |
|
||||
> |
|
||||
Edit |
|
||||
</el-button> |
|
||||
</router-link> |
|
||||
</template> |
|
||||
</el-table-column> |
|
||||
</el-table> |
|
||||
|
|
||||
<pagination |
|
||||
v-show="total>0" |
|
||||
:total="total" |
|
||||
:page.sync="listQuery.page" |
|
||||
:limit.sync="listQuery.limit" |
|
||||
@pagination="getList" |
|
||||
/> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script lang="ts"> |
|
||||
import { Component, Vue } from 'vue-property-decorator' |
|
||||
import { getArticles } from '@/api/articles' |
|
||||
import { IArticleData } from '@/api/types' |
|
||||
import Pagination from '@/components/Pagination/index.vue' |
|
||||
|
|
||||
@Component({ |
|
||||
name: 'ArticleList', |
|
||||
components: { |
|
||||
Pagination |
|
||||
} |
|
||||
}) |
|
||||
export default class extends Vue { |
|
||||
private total = 0 |
|
||||
private list: IArticleData[] = [] |
|
||||
private listLoading = true |
|
||||
private listQuery = { |
|
||||
page: 1, |
|
||||
limit: 20 |
|
||||
} |
|
||||
|
|
||||
created() { |
|
||||
this.getList() |
|
||||
} |
|
||||
|
|
||||
private async getList() { |
|
||||
this.listLoading = true |
|
||||
const { data } = await getArticles(this.listQuery) |
|
||||
this.list = data.items |
|
||||
this.total = data.total |
|
||||
// Just to simulate the time of the request |
|
||||
setTimeout(() => { |
|
||||
this.listLoading = false |
|
||||
}, 0.5 * 1000) |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.edit-input { |
|
||||
padding-right: 100px; |
|
||||
} |
|
||||
|
|
||||
.cancel-btn { |
|
||||
position: absolute; |
|
||||
right: 15px; |
|
||||
top: 10px; |
|
||||
} |
|
||||
</style> |
|
||||
Loading…
Reference in new issue