update second

This commit is contained in:
2021-05-11 18:07:10 +08:00
55 changed files with 1754 additions and 1284 deletions

View File

@@ -29,7 +29,7 @@ namespace Ewide.Application.Entity
[Comment("区域ID")]
[MaxLength(36)]
[Required]
public string AreaId { get; set; }
public string AreaCode { get; set; }
[Comment("类型")]
[Required]

View File

@@ -13,7 +13,7 @@ namespace Ewide.Application.Service.HouseProjectInfo.Dto
public string Name { get; set; }
public string Note { get; set; }
public int Sort { get; set; }
public string AreaId { get; set; }
public string AreaCode { get; set; }
public int Type { get; set; }
}
public class AddProjectInput : HouseProjectInfoInput

View File

@@ -12,7 +12,7 @@ namespace Ewide.Application.Service.HouseProjectInfo.Dto
public string Name { get; set; }
public string Note { get; set; }
public int Sort { get; set; }
public string AreaId { get; set; }
public string AreaCode { get; set; }
public string AreaName { get; set; }
public int Type { get; set; }
}

View File

@@ -30,6 +30,13 @@ namespace Ewide.Core
[Required]
public string Password { get; set; }
/// <summary>
/// 密码安全级别,在注册和修改密码时生成
/// </summary>
[Comment("密码安全级别")]
[Required]
public int SecurityLevel { get; set; }
/// <summary>
/// 昵称
/// </summary>

View File

@@ -1679,6 +1679,11 @@
密码采用MD5加密
</summary>
</member>
<member name="P:Ewide.Core.SysUser.SecurityLevel">
<summary>
密码安全级别,在注册和修改密码时生成
</summary>
</member>
<member name="P:Ewide.Core.SysUser.NickName">
<summary>
昵称
@@ -3080,6 +3085,11 @@
账号
</summary>
</member>
<member name="P:Ewide.Core.Service.LoginOutput.SecurityLevel">
<summary>
密码安全级别
</summary>
</member>
<member name="P:Ewide.Core.Service.LoginOutput.NickName">
<summary>
昵称
@@ -6604,11 +6614,6 @@
用户Id
</summary>
</member>
<member name="P:Ewide.Core.Service.ChangePasswordUserInput.Id">
<summary>
用户Id
</summary>
</member>
<member name="P:Ewide.Core.Service.ChangePasswordUserInput.Password">
<summary>
密码
@@ -6824,7 +6829,7 @@
<param name="input"></param>
<returns></returns>
</member>
<member name="M:Ewide.Core.Service.SysUserService.UpdateUserInfo(Ewide.Core.Service.UpdateUserInput)">
<member name="M:Ewide.Core.Service.SysUserService.UpdateUserInfo(Ewide.Core.Service.UserInput)">
<summary>
更新用户信息
</summary>
@@ -6859,7 +6864,7 @@
<param name="input"></param>
<returns></returns>
</member>
<member name="M:Ewide.Core.Service.SysUserService.UpdateAvatar(Ewide.Core.Service.UpdateUserInput)">
<member name="M:Ewide.Core.Service.SysUserService.UpdateAvatar(Ewide.Core.Service.UserInput)">
<summary>
修改用户头像(未实现)
</summary>

View File

@@ -7,6 +7,7 @@
<IsDeleted>false</IsDeleted>
<Account>superAdmin</Account>
<Password>e10adc3949ba59abbe56e057f20f883e</Password>
<SecurityLevel>1</SecurityLevel>
<Name>superAdmin</Name>
<Birthday>1986-07-26T00:00:00</Birthday>
<Sex>1</Sex>
@@ -22,6 +23,7 @@
<IsDeleted>false</IsDeleted>
<Account>admin</Account>
<Password>e10adc3949ba59abbe56e057f20f883e</Password>
<SecurityLevel>1</SecurityLevel>
<Name>admin</Name>
<Birthday>1986-07-26T00:00:00</Birthday>
<Sex>2</Sex>
@@ -37,6 +39,7 @@
<IsDeleted>false</IsDeleted>
<Account>zuohuaijun</Account>
<Password>e10adc3949ba59abbe56e057f20f883e</Password>
<SecurityLevel>1</SecurityLevel>
<Name>zuohuaijun</Name>
<Birthday>1986-07-26T00:00:00</Birthday>
<Sex>1</Sex>

View File

@@ -115,6 +115,15 @@ namespace Ewide.Core.Service
var httpContext = App.GetService<IHttpContextAccessor>().HttpContext;
var loginOutput = user.Adapt<LoginOutput>();
// 隐藏手机号/邮箱中间几位
loginOutput.Phone = String.IsNullOrEmpty(loginOutput.Phone) ? loginOutput.Phone
: loginOutput.Phone.Substring(0, 3) + "****" + loginOutput.Phone.Substring(7, 4);
loginOutput.Email = String.IsNullOrEmpty(loginOutput.Email) ? loginOutput.Email
: String.Join("@", loginOutput.Email.Split('@').Select((p, i) =>
{
return i == 0 ? (p.Length > 3 ? p.Substring(0, 3).PadRight(p.Length, '*') : "".PadRight(3, '*')) : p;
}));
loginOutput.LastLoginTime = user.LastLoginTime = DateTime.Now;
var ip = httpContext.Request.Headers["X-Real-IP"].FirstOrDefault();
loginOutput.LastLoginIp = user.LastLoginIp = string.IsNullOrEmpty(user.LastLoginIp) ? httpContext.GetRemoteIpAddressToIPv4() : ip;
@@ -153,18 +162,18 @@ namespace Ewide.Core.Service
}
// 增加登录日志
await new SysLogVis
{
Name = "登录",
Success = true,
Message = "登录成功",
Ip = loginOutput.LastLoginIp,
Browser = loginOutput.LastLoginBrowser,
Os = loginOutput.LastLoginOs,
VisType = 1,
VisTime = loginOutput.LastLoginTime,
Account = loginOutput.Account
}.InsertAsync();
//await new SysLogVis
//{
// Name = "登录",
// Success = true,
// Message = "登录成功",
// Ip = loginOutput.LastLoginIp,
// Browser = loginOutput.LastLoginBrowser,
// Os = loginOutput.LastLoginOs,
// VisType = 1,
// VisTime = loginOutput.LastLoginTime,
// Account = loginOutput.Account
//}.InsertAsync();
return loginOutput;
}

View File

@@ -20,6 +20,11 @@ namespace Ewide.Core.Service
/// </summary>
public string Account { get; set; }
/// <summary>
/// 密码安全级别
/// </summary>
public int SecurityLevel { get; set; }
/// <summary>
/// 昵称
/// </summary>

View File

@@ -46,11 +46,13 @@ namespace Ewide.Core.Service
/// <summary>
/// 邮箱
/// </summary>
[RegularExpression(@"^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$", ErrorMessage = "")]
public virtual string Email { get; set; }
/// <summary>
/// 手机
/// </summary>
[RegularExpression(@"^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$", ErrorMessage = "")]
public virtual string Phone { get; set; }
/// <summary>
@@ -120,12 +122,6 @@ namespace Ewide.Core.Service
public class ChangePasswordUserInput
{
/// <summary>
/// 用户Id
/// </summary>
[Required(ErrorMessage = "用户Id不能为空")]
public string Id { get; set; }
/// <summary>
/// 密码
/// </summary>

View File

@@ -22,9 +22,9 @@ namespace Ewide.Core.Service
Task<dynamic> QueryUserPageList([FromQuery] UserInput input);
Task ResetUserPwd(QueryUserInput input);
Task SaveAuthUserToUser(AuthUserInput authUser, UserInput sysUser);
Task UpdateAvatar(UpdateUserInput input);
Task UpdateAvatar(UserInput input);
Task UpdateUser(UpdateUserInput input);
Task UpdateUserInfo(UpdateUserInput input);
Task UpdateUserInfo(UserInput input);
Task UpdateUserPwd(ChangePasswordUserInput input);
}
}

View File

@@ -230,16 +230,18 @@ namespace Ewide.Core.Service
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysUser/updateInfo")]
public async Task UpdateUserInfo(UpdateUserInput input)
public async Task UpdateUserInfo(UserInput input)
{
var user = input.Adapt<SysUser>();
user.Id = _userManager.UserId;
await user.UpdateExcludeAsync(new string[] {
nameof(SysUser.Account),
nameof(SysUser.SecurityLevel),
nameof(SysUser.Name),
nameof(SysUser.Password),
nameof(SysUser.AdminType),
nameof(SysUser.Status),
// 邮箱和手机号作为可能登录的方式,不能在此处直接进行修改
// 邮箱和手机号作为安全验证的方式,不能在此处直接进行修改
nameof(SysUser.Phone),
nameof(SysUser.Email)
}, true);
@@ -253,7 +255,7 @@ namespace Ewide.Core.Service
[HttpPost("/sysUser/updatePwd")]
public async Task UpdateUserPwd(ChangePasswordUserInput input)
{
var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Id == input.Id);
var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Id == _userManager.UserId);
if (MD5Encryption.Encrypt(input.Password) != user.Password)
throw Oops.Oh(ErrorCode.D1004);
if (MD5Encryption.Encrypt(input.NewPassword).Equals(user.Password))
@@ -301,9 +303,9 @@ namespace Ewide.Core.Service
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysUser/updateAvatar")]
public async Task UpdateAvatar(UpdateUserInput input)
public async Task UpdateAvatar(UserInput input)
{
var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Id == input.Id);
var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Id == _userManager.UserId);
// 调用文件上传
//sysFileInfoService.assertFile(input.Avatar);
user.Avatar = input.Avatar;

View File

@@ -6,7 +6,9 @@
"sysFileInfo:upload",
"sysFileInfo:download",
"sysFileInfo:preview",
"sysUser:updateInfo"
"sysUser:updateInfo",
"sysUser:updatePwd",
"sysUser:updateAvatar"
]
}
}

View File

@@ -3,6 +3,7 @@
@import './lib/container.less';
@import './lib/align.less';
@import './lib/font-size.less';
@import './lib/text-color.less';
@import './lib/margin.less';
@import './lib/width-height.less';
@import './lib/scrollbar.less';
@@ -15,6 +16,7 @@
.yo-nav-theme--light {
.light();
}
@import './lib/button.less';
@import './lib/card.less';
@import './lib/table.less';
@import './lib/list.less';

View File

@@ -0,0 +1,5 @@
@import (reference) '~@/assets/style/extend.less';
@btn-default-border: @border-color-split;
.ant-btn {
box-shadow: none;
}

View File

@@ -4,25 +4,17 @@
width: 660px;
margin: 0 auto;
}
h1,
.h1,
h2,
.h2,
h3,
.h3,
h4,
.h4,
h5,
.h5,
h6,
.h6 {
color: darken(@white, 40%);
}
h3,
.h3 {
font-size: 16px;
}
h4,
.h4 {
font-size: 15px;
}

View File

@@ -1,10 +1,14 @@
@import (reference) '~@/assets/style/extend.less';
.ant-list-bordered {
border-color: @border-color-split;
background-color: @white;
}
.yo-list {
&-content--h {
display: flex;
align-items: center;
&--item {
min-width: 100px;
min-width: 120px;
margin-left: @padding-xl;
>span {
line-height: 20px;

View File

@@ -0,0 +1,32 @@
@import (reference) '~@/assets/style/extend.less';
.text-primary {
color: @primary-color;
}
.text-info {
color: @info-color;
}
.text-success {
color: @success-color;
}
.text-processing {
color: @processing-color;
}
.text-error,
.text-danger {
color: @error-color;
}
.text-highlight {
color: @highlight-color;
}
.text-warning {
color: @warning-color;
}
.text-normal {
color: @normal-color;
}
.text-white {
color: @white;
}
.text-black {
color: @black;
}

View File

@@ -95,6 +95,8 @@
box-shadow: 0 0 0 2px @white;
}
&--name {
font-weight: bolder;
position: absolute;
left: 32px + @padding-sm * 2;

View File

@@ -18,6 +18,7 @@ axios.defaults.baseURL = '/api'
* api.getItemGroupType(parmas).then(...)
*/
import urls from './requests'
import { settings } from 'nprogress'
const initInstance = (options) => {
const instance = axios
@@ -45,10 +46,12 @@ const errorNotification = ({ code, message }) => {
switch (message.constructor) {
case Array:
message.map(p => {
app.$notification.error({
duration: 30,
message: p.field,
description: p.messages.join('/'),
setTimeout(() => {
app.$notification.error({
duration: 30,
message: p.field,
description: p.messages.join('/'),
})
})
})
break

View File

@@ -13,6 +13,9 @@ export default {
},
action: {
type: Function
},
successMessage: {
type: String
}
},
@@ -124,7 +127,7 @@ export default {
&& this.action(data)
.then(({ success }) => {
if (success) {
this.$message.success('保存成功');
this.$message.success(this.successMessage || '保存成功');
this.onClose();
this.$emit('ok');
}

View File

@@ -0,0 +1,129 @@
<template>
<!--
普通编辑窗体
v 1.2
2021-04-30
Lufthafen
-->
<container>
<br />
<a-card>
<a-form-model
:label-col="labelCol"
:model="form"
:rules="rules"
:wrapper-col="wrapperCol"
ref="form"
>
<a-spin :spinning="loading">
<a-icon slot="indicator" spin type="loading" />
<a-form-model-item class="ant-row-flex" label="房屋性质">
<a-input />
</a-form-model-item>
</a-spin>
</a-form-model>
</a-card>
</container>
</template>
<script>
/* 表单内容默认值 */
const defaultForm = {
/* ... */
};
export default {
props: ['param'],
data() {
return {
labelCol: { flex: '150px' },
wrapperCol: { flex: 'auto' },
/** 表单数据 */
form: {},
/** 验证格式 */
rules: {
/* ... */
},
/** 加载异步数据状态 */
loading: false,
/** 其他成员属性 */
/* ... */
};
},
methods: {
/**
* 必要的方法
* 在打开编辑页时允许填充数据
*/
onFillData(params) {
/** 将默认数据覆盖到form */
this.form = this.$_.cloneDeep({
...defaultForm,
...params.record,
/** 在此处添加其他默认数据转换 */
/* ... */
});
},
/**
* 必要方法
* 验证表单并获取表单数据
*/
onGetData() {
return new Promise((reslove, reject) => {
this.$refs.form.validate((valid) => {
if (valid) {
const record = this.$_.cloneDeep(this.form);
/** 验证通过后可以对数据进行转换得到想要提交的格式 */
/* ... */
reslove(record);
} else {
reject();
}
});
});
},
/**
* 必要的方法
* 在外部窗口进行保存时调用表单验证
*/
onValidate(callback) {
this.$refs.form.validate(callback);
},
/**
* 必要的方法
* 在外部窗口关闭或重置时对表单验证进行初始化
*/
onResetFields() {
setTimeout(() => {
this.$refs.form.resetFields();
/** 在这里可以初始化当前组件中其他属性 */
/* ... */
}, 300);
},
/**
* 必要方法
* 加载当前表单中所需要的异步数据
*/
async onInit(params) {
this.loading = true;
/** 可以在这里await获取一些异步数据 */
/* ... */
this.loading = false;
},
/** 当前组件的其他方法 */
/* ... */
},
};
</script>

View File

@@ -0,0 +1,206 @@
<template>
<!--
普通查询表格
v 1.2
2021-04-30
Lufthafen
-->
<container>
<div ref="test"></div>
<br />
<a-card :bordered="false">
<yo-table
:columns="columns"
:load-data="loadData"
@query="onQuery"
@resetQuery="onResetQuery"
ref="table"
>
<Auth auth="authCode:page" slot="query">
<!-- 此处添加查询表单控件 -->
<!-- ... -->
</Auth>
<Auth auth="authCode:add" slot="operator">
<a-button @click="onOpen('add-form')" icon="plus">新增房屋编码</a-button>
</Auth>
<!-- 格式化字段内容 -->
<!-- ... -->
<!-- 添加操作控件 -->
<span slot="action" slot-scope="text, record">
<yo-table-actions>
<Auth auth="authCode:edit">
<a @click="onOpen('edit-form', record)">编辑</a>
</Auth>
<Auth auth="authCode:delete">
<a-popconfirm @confirm="onDelete(record)" placement="topRight" title="是否确认删除">
<a>删除</a>
</a-popconfirm>
</Auth>
<!-- 可在此处添加其他操作控件 -->
<!-- ... -->
</yo-table-actions>
</span>
</yo-table>
</a-card>
</container>
</template>
<script>
/* 在此管理整个页面需要的接口名称 */
const api = {
page: 'testPageApi...',
add: 'testAddApi',
edit: 'testEditApi',
delete: 'testDeleteApi...',
/* ... */
};
export default {
props: ['paneKey'],
data() {
return {
api,
name: '...',
/* 查询条件 */
query: {},
/* 表格字段 */
columns: [],
/* 字典编码储存格式 */
codes: {
code1: [],
code2: [],
},
};
},
created() {
/** 按需加载字典编码 */
this.onLoadCodes();
/** 根据权限添加操作列 */
const flag = this.$auth(/* ... */);
if (flag) {
this.columns.push({
title: '操作',
width: '150px',
dataIndex: 'action',
scopedSlots: { customRender: 'action' },
});
}
},
methods: {
/**
* 必要的方法
* 传给yo-table以示意数据接口及其参数和返回的数据结构
*/
loadData(params) {
return this.$api[api.page]({
...params,
...this.query,
}).then((res) => {
return res.data;
});
},
/**
* 有查询功能时的必要方法
* 加载数据时初始化分页信息
*/
onQuery() {
this.$refs.table.onReloadData(true);
},
/**
* 有查询功能时的必要方法
* 重置查询条件
*/
onResetQuery() {
/** 在这里重置查询条件时,可对特殊的字段做保留处理 */
this.query = {};
this.onQuery();
},
/**
* 必要方法
* 重新列表数据
*/
onReloadData() {
this.$refs.table.onReloadData();
},
/**
* 必要方法
* 加载字典数据
* 如果不需要获取相应的字典数据,此方法内容可空
*/
onLoadCodes() {
this.$api
.$queue([
this.$api.sysDictTypeDropDownAwait({ code: 'code1' }),
this.$api.sysDictTypeDropDownAwait({ code: 'code2' }),
/* ... */
])
.then(([code1, code2]) => {
this.codes.code1 = code1.data;
this.codes.code2 = code2.data;
/* ... */
});
},
/**
* 必要方法
* 绑定数据字典值
*/
bindCodeValue(code, name) {
const c = this.codes[name].find((p) => p.code == code);
if (c) {
return c.value;
}
return null;
},
/**
* 必要方法
* 从列表页调用窗口的打开方法
*/
onOpen(formName, record) {
this.openContentWindow({
title: '新建房屋编码',
path: 'business/house/houseCode/form',
param: {
parent: this,
record,
},
});
},
/**
* 必要方法
* 可以用做一系列操作的公共回调,此方法中会重新加载当前列表
*/
onResult(success, successMessage) {
if (success) {
this.$message.success(successMessage);
this.onReloadData();
}
},
/**
* 必要方法
* 删除时调用
*/
onDelete(record) {
this.$refs.table.onLoading();
this.$api[api.delete](record)
.then(({ success }) => {
this.onResult(success, '删除成功');
})
.finally(() => {
this.$refs.table.onLoaded();
});
},
},
};
</script>

View File

@@ -12,7 +12,7 @@
<!-- 表单控件 -->
<a-form-model-item label="所属区域" prop="areaCode">
<!-- <a-tree-select :dropdown-style="{ maxHeight: '300px', overflow: 'auto' }" :tree-data="areaData" :replace-fields="{ value: 'code', title: 'name', children: 'children' }" placeholder="请选择所属区域" tree-default-expand-all v-model="form.areaId" /> -->
<a-cascader :field-names="{ label: 'name', value: 'code', children: 'children' }" :options="areaData" expand-trigger="hover" placeholder="请选择所属区域" v-model="areaCode" />
<a-cascader :field-names="{ label: 'name', value: 'code', children: 'children' }" :options="areaData" expand-trigger="hover" placeholder="请选择所属区域" v-model="form.areaCode" :display-render="displayRender" />
</a-form-model-item>
<a-form-model-item label="项目名称" prop="name">
<a-input placeholder="请输入项目名称" v-model="form.name" />
@@ -43,16 +43,18 @@ export default {
data() {
return {
/** 表单数据 */
form: { areaId: '' },
form: {},
/** 验证格式 */
rules: {
/* ... */
note: [{ required: true, message: '请输入备注' }],
name: [{ required: true, message: '请输入项目名称' }],
sort: [{ required: true, message: '请输入排序' }],
areaCode: [{ required: true, message: '请选择所属区域' }],
},
/** 加载异步数据状态 */
loading: false,
/** 其他成员属性 */
areaData: [],
/* ... */
@@ -96,10 +98,9 @@ export default {
...defaultForm,
...params.record,
/** 在此处添加其他默认数据转换 */
areaId: areaCode[3],
areaCode,
/* ... */
});
this.areaCode = areaCode;
},
/**
@@ -111,7 +112,7 @@ export default {
this.$refs.form.validate((valid) => {
if (valid) {
const record = this.$_.cloneDeep(this.form);
record.areaCode = record.areaCode[record.areaCode.length - 1];
/** 验证通过后可以对数据进行转换得到想要提交的格式 */
/* ... */
@@ -162,6 +163,9 @@ export default {
return res.data;
});
},
displayRender({ labels }) {
return labels[labels.length - 1];
},
/* ... */
},
};

View File

@@ -1,10 +1,10 @@
<template>
<container>
<br />
<a-anchor
:get-container="()=> $el.parentNode"
:offset-top="16"
:wrapper-style="{ backgroundColor: 'transparent' }"
@click.prevent
class="yo-account--anchor"
>
<a-anchor-link
:href="`#account-${key}`"
@@ -13,12 +13,27 @@
v-for="(nav, key) in navs"
/>
</a-anchor>
<br />
<section :id="`account-${key}`" :key="key" v-for="(nav, key) in navs">
<component :is="nav.component" v-if="nav.component" />
</section>
</container>
</template>
<style lang="less" scoped>
@import (reference) '~@/assets/style/extend.less';
.yo-account--anchor {
position: absolute;
width: 200px;
/deep/.ant-anchor-wrapper {
background-color: transparent;
}
/deep/.ant-anchor-ink {
display: none;
}
}
</style>
<script>
export default {
data() {
@@ -28,6 +43,10 @@ export default {
title: '我的信息',
component: () => import('./setting/info'),
},
1: {
title: '安全设置',
component: () => import('./setting/safety'),
},
},
};
},

View File

@@ -1,19 +1,15 @@
<template>
<container mode="container-xxs">
<a-form-model class="yo-form">
<h4>我的信息</h4>
<h4 class="h4">我的信息</h4>
<div class="yo-avatar-info">
<yo-image :id="form.avatar" :size="128" icon="user" type="avatar" />
<div @click="avatar.cropper = true" class="yo-avatar-info--cover">
<div @click="onAvatarStart" class="yo-avatar-info--cover">
<a-icon type="cloud-upload-o" />
</div>
</div>
<br />
<a-card :key="avatar.key" v-if="avatar.cropper">
<a-alert type="error">
<template slot="message">上传完成时无法被销毁</template>
</a-alert>
<br />
<a-card v-if="avatar.cropper">
<a-row :gutter="16" align="middle" type="flex">
<a-col :span="12">
<div class="yo-avatar-cropper">
@@ -56,7 +52,7 @@
icon="check"
type="primary"
>确认</a-button>
<a-button @click="avatar.cropper = false" icon="close">取消</a-button>
<a-button @click="onAvatarCancel" icon="close">取消</a-button>
</a-col>
</a-row>
</a-card>
@@ -79,15 +75,15 @@
<a-form-model-item label="性别" prop="sex">
<a-radio-group v-model="form.sex">
<a-radio-button :value="0">
<a-icon type="stop" />
<a-icon class="mr-xxs" type="stop" />
<span>保密</span>
</a-radio-button>
<a-radio-button :value="1">
<a-icon :style="{ color: '#1890ff' }" type="man" />
<a-icon :style="{ color: '#1890ff' }" class="mr-xxs" type="man" />
<span></span>
</a-radio-button>
<a-radio-button :value="2">
<a-icon :style="{ color: '#eb2f96' }" type="woman" />
<a-icon :style="{ color: '#eb2f96' }" class="mr-xxs" type="woman" />
<span></span>
</a-radio-button>
</a-radio-group>
@@ -99,6 +95,7 @@
<a-button :loading="saving" @click="onSaveInfo" block>更新个人信息</a-button>
</a-form-model>
<br />
</container>
</template>
<style lang="less" scoped>
@@ -171,7 +168,6 @@ export default {
form: {},
avatar: {
key: Date.now(),
cropper: false,
img: '',
autoWidth: 200,
@@ -222,6 +218,10 @@ export default {
return false;
},
onAvatarStart() {
this.avatar.cropper = true;
},
onAvatarOk() {
this.avatar.uploading = true;
this.$refs.cropper.getCropBlob(async (data) => {
@@ -232,17 +232,35 @@ export default {
const { data: fileId } = await this.$api.sysFileInfoUpload(fd);
this.form.avatar = fileId;
this.avatar.cropper = false;
this.onAvatarCancel();
} finally {
this.avatar.uploading = false;
}
});
},
onAvatarCancel() {
this.avatar = {
...this.avatar,
...{
cropper: false,
img: '',
preview: {},
file: null,
},
};
},
async onSaveInfo() {
this.saving = true;
try {
await this.$api.sysUserUpdateInfo(this.form);
await this.$api.sysUserUpdateInfo({
avatar: this.form.avatar,
nickName: this.form.nickName,
birthday: this.form.birthday,
sex: this.form.sex,
tel: this.form.tel,
});
await this.onRefreshInfo();
this.$message.success('更新个人信息成功');
} finally {

View File

@@ -0,0 +1,171 @@
<template>
<container mode="container-xxs">
<div class="yo-form">
<h4 class="h4">安全设置</h4>
<a-progress
:percent="[33.33, 66.67, 100][popSecurityLevel - 1]"
:stroke-color="strokeColor"
:stroke-width="15"
class="mb-md"
status="active"
stroke-linecap="square"
>
<span slot="format">
帐号安全级别:
<span class="text-success" v-if="popSecurityLevel === 3"></span>
<span class="text-warning" v-else-if="popSecurityLevel === 2"></span>
<span class="text-error" v-else></span>
</span>
</a-progress>
<a-list :data-source="data" bordered item-layout="vertical">
<a-list-item :extra="item.extra" slot="renderItem" slot-scope="item">
<template v-if="item.done">
<span class="text-success" slot="actions">
<a-icon class="mr-xxs" type="check-circle" />已设置
</span>
<a @click="item.action" slot="actions">修改</a>
</template>
<template v-else>
<span class="text-warning" slot="actions">
<a-icon class="mr-xxs" type="exclamation-circle" />未设置
</span>
<a @click="item.action" slot="actions">设置</a>
</template>
<a-list-item-meta :description="item.description" :title="item.title" />
</a-list-item>
</a-list>
</div>
<br />
<yo-modal-form
:action="$api.sysUserUpdatePwd"
@ok="onSetPasswordSuccess"
ref="password-form"
success-message="密码修改成功"
title="修改密码"
>
<password />
</yo-modal-form>
</container>
</template>
<style lang="less" scoped>
@import (reference) '~@/assets/style/app.less';
.ant-progress {
width: 400px;
/deep/.ant-progress-inner {
border-radius: @border-radius-base;
background-color: @white;
}
}
</style>
<script>
import { doLogout } from '@/common/login';
import Password from './password';
export default {
components: {
Password,
},
data() {
return {
data: [],
};
},
computed: {
popSecurityLevel() {
// 计算帐号安全级别
return (
Math.floor(((this.data.filter((p) => p.done).length - 1) * 3 + this.$root.global.info.securityLevel * 3) / 5) ||
1
);
},
strokeColor() {
return [
{
from: '#e280bf',
to: '#e91010',
},
{
from: '#e9ce10',
to: '#ff974d',
},
{
from: '#108ee9',
to: '#87d068',
},
][this.popSecurityLevel - 1];
},
},
created() {
const info = this.$root.global.info;
// 登录密码
this.data.push({
title: '登录密码',
description:
'安全性高的密码可以使帐号更安全。建议您定期更换密码设置一个包含字母符号或数字中至少两项且长度超过6位的密码。',
extra: (
<div>
当前密码强度
{
[
<span class="text-error"></span>,
<span class="text-warning"></span>,
<span class="text-success"></span>,
][info.securityLevel - 1]
}
</div>
),
done: true,
action: () => {
this.$refs['password-form'].onOpen({});
},
});
// 手机绑定
this.data.push({
title: '手机绑定(发送验证码到手机,未实现)',
description: (
<div>
手机号可以直接用于登录找回密码等
{info.phone && (
<span>
您已绑定了手机<b>{info.phone}</b>
</span>
)}
</div>
),
done: !!info.phone,
action: () => {},
});
// 邮箱绑定
this.data.push({
title: '安全邮箱(发送验证码到邮箱,未实现)',
description: (
<div>
安全邮箱可以直接用于登录找回密码等
{info.email && (
<span>
您已绑定了邮箱<b>{info.email}</b>
</span>
)}
</div>
),
done: !!info.email,
action: () => {},
});
},
methods: {
onSetPasswordSuccess() {
doLogout();
},
},
};
</script>

View File

@@ -0,0 +1,126 @@
<template>
<!--
普通编辑窗体
v 1.2
2021-04-30
Lufthafen
-->
<a-form-model :model="form" :rules="rules" class="yo-form" ref="form">
<a-spin :spinning="loading">
<a-icon slot="indicator" spin type="loading" />
<div class="yo-form-group">
<!-- 表单控件 -->
<!-- ... -->
<a-form-model-item label="旧密码" prop="password">
<a-input-password placeholder="请输入旧密码" v-model="form.password" />
</a-form-model-item>
<a-form-model-item label="新密码" prop="newPassword">
<a-input-password placeholder="请输入新密码" v-model="form.newPassword" />
</a-form-model-item>
<a-form-model-item label="确认新密码" prop="confirm">
<a-input-password placeholder="请确认新密码" v-model="form.confirm" />
</a-form-model-item>
</div>
</a-spin>
</a-form-model>
</template>
<script>
/* 表单内容默认值 */
const defaultForm = {
/* ... */
};
export default {
data() {
return {
/** 表单数据 */
form: {},
/** 验证格式 */
rules: {
/* ... */
password: [{ required: true, message: '请输入旧密码' }],
newPassword: [{ required: true, message: '请输入新密码' }],
confirm: [{ required: true, message: '请确认新密码' }],
},
/** 加载异步数据状态 */
loading: false,
/** 其他成员属性 */
/* ... */
};
},
methods: {
/**
* 必要的方法
* 在打开编辑页时允许填充数据
*/
onFillData(params) {
/** 将默认数据覆盖到form */
this.form = this.$_.cloneDeep({
...defaultForm,
...params.record,
/** 在此处添加其他默认数据转换 */
/* ... */
});
},
/**
* 必要方法
* 验证表单并获取表单数据
*/
onGetData() {
return new Promise((reslove, reject) => {
this.$refs.form.validate((valid) => {
if (valid) {
const record = this.$_.cloneDeep(this.form);
/** 验证通过后可以对数据进行转换得到想要提交的格式 */
/* ... */
reslove(record);
} else {
reject();
}
});
});
},
/**
* 必要的方法
* 在外部窗口进行保存时调用表单验证
*/
onValidate(callback) {
this.$refs.form.validate(callback);
},
/**
* 必要的方法
* 在外部窗口关闭或重置时对表单验证进行初始化
*/
onResetFields() {
setTimeout(() => {
this.$refs.form.resetFields();
/** 在这里可以初始化当前组件中其他属性 */
/* ... */
}, 300);
},
/**
* 必要方法
* 加载当前表单中所需要的异步数据
*/
async onInit(params) {
this.loading = true;
/** 可以在这里await获取一些异步数据 */
/* ... */
this.loading = false;
},
/** 当前组件的其他方法 */
/* ... */
},
};
</script>

View File

@@ -1,71 +0,0 @@
<template>
<a-modal
:confirmLoading="confirmLoading"
:visible="visible"
@cancel="onCancel"
@ok="onOk"
class="yo-modal-form"
title="新增应用"
>
<FormBody ref="form-body" />
</a-modal>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口
*/
onOpen() {
this.visible = true;
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.$refs['form-body'].onValidate((valid) => {
if (valid) {
this.confirmLoading = true;
this.$api
.sysAppAdd(this.$refs['form-body'].form)
.then(({ success }) => {
if (success) {
this.$message.success('新增成功');
this.onCancel();
this.$emit('ok');
}
})
.finally(() => {
this.confirmLoading = false;
});
}
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onCancel() {
this.$refs['form-body'].onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -1,74 +0,0 @@
<template>
<a-modal
:confirmLoading="confirmLoading"
:visible="visible"
@cancel="onCancel"
@ok="onOk"
class="yo-modal-form"
title="编辑应用"
>
<FormBody ref="form-body" />
</a-modal>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口,并填充外部传入的数据
*/
onOpen(record) {
this.visible = true;
this.$nextTick(() => {
this.$refs['form-body'].onFillData(record);
});
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.$refs['form-body'].onValidate((valid) => {
if (valid) {
this.confirmLoading = true;
this.$api
.sysAppEdit(this.$refs['form-body'].form)
.then(({ success }) => {
if (success) {
this.$message.success('编辑成功');
this.onCancel();
this.$emit('ok');
}
})
.finally(() => {
this.confirmLoading = false;
});
}
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onCancel() {
this.$refs['form-body'].onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -11,38 +11,33 @@
<div class="yo-form-group">
<!-- 表单控件 -->
<!-- ... -->
<a-form-model-item label="角色名" prop="name">
<a-input placeholder="请输入角色名" v-model="form.name" />
</a-form-model-item>
<a-form-model-item label="唯一编码" prop="code" :disabled="editDisabled">
<a-input placeholder="请输入唯一编码" v-model="form.code" />
</a-form-model-item>
<a-form-model-item label="系统参数" prop="sysFlag">
<a-radio-group v-model="form.sysFlag" :disabled="editDisabled">
<a-radio-button value="Y">
<a-icon :style="{ color: '#1890ff' }" />
</a-radio-button>
<a-radio-button value="N">
<a-icon :style="{ color: '#eb2f96' }" />
</a-radio-button>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="所属分类" prop="groupCode">
<a-select placeholder="请选择所属分类" v-model="form.groupCode" :disabled="editDisabled">
<a-form-model-item label="角色名" prop="name">
<a-input placeholder="请输入角色名" v-model="form.name" />
</a-form-model-item>
<a-form-model-item :disabled="editDisabled" label="唯一编码" prop="code">
<a-input placeholder="请输入唯一编码" v-model="form.code" />
</a-form-model-item>
<a-form-model-item label="系统参数" prop="sysFlag">
<a-radio-group :disabled="editDisabled" v-model="form.sysFlag">
<a-radio-button value="Y"></a-radio-button>
<a-radio-button value="N"></a-radio-button>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="所属分类" prop="groupCode">
<a-select :disabled="editDisabled" placeholder="请选择所属分类" v-model="form.groupCode">
<a-select-option
:key="i"
:value="item.code"
v-for="(item, i) in groupCode"
>{{ item.value }}</a-select-option>
</a-select>
</a-select>
</a-form-model-item>
<a-form-model-item label="参数值" prop="name">
<a-input placeholder="请输入参数值 " v-model="form.value" />
</a-form-model-item>
<a-form-model-item label="备注" prop="remark">
<a-input placeholder="请输入备注" v-model="form.remark" />
</a-form-model-item>
:key="i"
:value="item.code"
v-for="(item, i) in groupCode"
>{{ item.value }}</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="参数值" prop="name">
<a-input placeholder="请输入参数值 " v-model="form.value" />
</a-form-model-item>
<a-form-model-item label="备注" prop="remark">
<a-input placeholder="请输入备注" v-model="form.remark" />
</a-form-model-item>
</div>
</a-spin>
</a-form-model>
@@ -50,7 +45,6 @@
<script>
/* 表单内容默认值 */
const defaultForm = {
// editDisabled:false,
/* ... */
};
@@ -61,18 +55,18 @@ export default {
form: {},
/** 验证格式 */
rules: {
name: [{ required: true, message: "请输入应用名称" }],
code: [{ required: true, message: "请输入唯一编码" }],
sysFlag: [{ required: true, message: "请选择参数类型" }],
groupCode: [{ required: true, message: "请选择所属分类" }],
value: [{ required: true, message: "请输入参数值" }],
name: [{ required: true, message: '请输入应用名称' }],
code: [{ required: true, message: '请输入唯一编码' }],
sysFlag: [{ required: true, message: '请选择参数类型' }],
groupCode: [{ required: true, message: '请选择所属分类' }],
value: [{ required: true, message: '请输入参数值' }],
/* ... */
},
/** 加载异步数据状态 */
loading: false,
editDisabled:false,
groupCode:[],
editDisabled: false,
groupCode: [],
/** 其他成员属性 */
/* ... */
};
@@ -91,14 +85,8 @@ export default {
/** 在此处添加其他默认数据转换 */
/* ... */
});
if(this.form.sysFlag == 'Y')
{
this.editDisabled = true
}else
{
this.editDisabled = false
}
this.editDisabled = this.form.sysFlag == 'Y';
},
/**
@@ -158,9 +146,9 @@ export default {
/** 当前组件的其他方法 */
/* ... */
/**
*
*
* 获取所属分类字典表的内容
*/
*/
onLoadgroupCodeData() {
return this.$api.sysDictTypeDropDown({ code: 'consts_type' }).then(({ data }) => {
return data;

View File

@@ -114,18 +114,20 @@ export default {
],
/* 字典编码储存格式 */
// codes: {
// code1: [],
// code2: [],
// },
codes: {
code1: [],
code2: [],
},
};
},
created() {
/** 按需加载字典编码 */
//this.onLoadCodes();
this.onLoadCodes();
/** 根据权限添加操作列 */
const flag = this.$auth(/* ... */);
const flag = this.$auth({
sysConfig: [['edit'], ['delete']],
});
if (flag) {
this.columns.push({
title: '操作',
@@ -180,19 +182,7 @@ export default {
* 加载字典数据
* 如果不需要获取相应的字典数据,此方法内容可空
*/
//onLoadCodes() {
// this.$api
// .$queue([
// this.$api.sysDictTypeDropDownAwait({ code: 'code1' }),
// this.$api.sysDictTypeDropDownAwait({ code: 'code2' }),
// /* ... */
// ])
// .then(([code1, code2]) => {
// this.codes.code1 = code1.data;
// this.codes.code2 = code2.data;
// /* ... */
// });
// },
onLoadCodes() {},
/**
* 必要方法

View File

@@ -79,6 +79,7 @@
:get-container="()=> $el.parentNode"
:offset-top="24"
:wrapper-style="{ backgroundColor: 'transparent' }"
@click.prevent
>
<a-anchor-link
:href="`#doc-${index}`"

View File

@@ -1,84 +0,0 @@
<template>
<a-drawer
:confirmLoading="confirmLoading"
:visible="visible"
:width="800"
@close="onClose"
@ok="onOk"
class="yo-drawer-form"
title="新增菜单"
>
<FormBody ref="form-body" />
<div class="ant-drawer-footer">
<a-button @click="onClose">取消</a-button>
<a-button :loading="confirmLoading" @click="onOk" type="primary">确定</a-button>
</div>
</a-drawer>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
computed: {
formBody() {
return this.$refs['form-body'];
},
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口
*/
async onOpen(record) {
this.visible = true;
this.$nextTick(async () => {
await this.formBody.onInit(record, true);
});
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.formBody.onGetData().then((data) => {
this.confirmLoading = true;
this.$api
.sysMenuAdd(data)
.then(({ success }) => {
if (success) {
this.$message.success('新增成功');
this.onClose();
this.$emit('ok');
}
})
.finally(() => {
this.confirmLoading = false;
});
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onClose() {
this.formBody.onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -1,83 +0,0 @@
<template>
<a-drawer
:confirmLoading="confirmLoading"
:visible="visible"
:width="800"
@close="onClose"
class="yo-drawer-form"
title="编辑应用"
>
<FormBody ref="form-body" />
<div class="ant-drawer-footer">
<a-button @click="onClose">取消</a-button>
<a-button :loading="confirmLoading" @click="onOk" type="primary">确定</a-button>
</div>
</a-drawer>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
computed: {
formBody() {
return this.$refs['form-body'];
},
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口,并填充外部传入的数据
*/
onOpen(record) {
this.visible = true;
this.$nextTick(async () => {
await this.formBody.onInit(record);
this.formBody.onFillData(record);
});
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.formBody.onGetData().then((data) => {
this.confirmLoading = true;
this.$api
.sysMenuEdit(data)
.then(({ success }) => {
if (success) {
this.$message.success('编辑成功');
this.onClose();
this.$emit('ok');
}
})
.finally(() => {
this.confirmLoading = false;
});
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onClose() {
this.formBody.onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -11,7 +11,7 @@
<div class="yo-form-group">
<!-- 表单控件 -->
<!-- ... -->
<a-form-model-item label="机构名称" prop="name">
<a-form-model-item label="机构名称" prop="name">
<a-input placeholder="请输入机构名称" v-model="form.name" />
</a-form-model-item>
<a-form-model-item label="唯一编码" prop="code">
@@ -57,16 +57,15 @@ import { EMPTY_ID } from '@/util/global';
/* 表单内容默认值 */
const defaultForm = {
/* ... */
pid: undefined,
sort: 100,
};
export default {
data() {
return {
/** 表单数据 */
form: {
pid: undefined,
sort: 100,
},
form: {},
/** 验证格式 */
rules: {
name: [{ required: true, message: '请输入机构名称' }],
@@ -90,45 +89,42 @@ export default {
* 必要的方法
* 在打开编辑页时允许填充数据
*/
onFillData(params,orgId) {
if(orgId){
this.form.pid = orgId;
}else if(params){
// 从字符串areaCode查找到整个层级
const areaCode = [];
const findCode = (data, level) => {
level = level || 0;
for (let i = 0; i < data.length; i++) {
const item = data[i];
areaCode[level] = item.code;
onFillData(params) {
// 从字符串areaCode查找到整个层级
const areaCode = [];
const findCode = (data, level) => {
level = level || 0;
for (let i = 0; i < data.length; i++) {
const item = data[i];
areaCode[level] = item.code;
if (item.code === params.areaCode) {
areaCode.length = level + 1;
if (item.code === params.record.areaCode) {
areaCode.length = level + 1;
return true;
}
if (item.children && item.children.length) {
const found = findCode(item.children, level + 1);
if (found) {
return true;
}
if (item.children && item.children.length) {
const found = findCode(item.children, level + 1);
if (found) {
return true;
}
}
}
};
if (params.areaCode) {
findCode(this.areaData);
}
};
if (params.record && params.record.areaCode) {
findCode(this.areaData);
}
/** 将默认数据覆盖到form */
this.form = this.$_.cloneDeep({
...defaultForm,
...params,
areaCode,
pid: params.orgId,
...params.record,
/** 在此处添加其他默认数据转换 */
/* ... */
areaCode,
});
}
},
/**
@@ -137,14 +133,13 @@ export default {
*/
onGetData() {
return new Promise((reslove, reject) => {
this.$refs.form.validate((valid) => {
if (valid) {
const record = this.$_.cloneDeep(this.form);
record.areaCode = record.areaCode[record.areaCode.length - 1];
/** 验证通过后可以对数据进行转换得到想要提交的格式 */
/* ... */
reslove(record);
} else {
reject();
@@ -182,7 +177,7 @@ export default {
this.loading = true;
/** 可以在这里await获取一些异步数据 */
await this.onLoadOrgData();
await this.onLoadAreaData();
await this.onLoadAreaData();
/* ... */
this.loading = false;
},

View File

@@ -5,63 +5,68 @@
2021-04-30
Lufthafen
-->
<yo-tree-layout
<yo-tree-layout
:load-data="loadTreeData"
@select="onSelect"
default-expanded-keys
ref="tree-layout"
>
<container>
<br />
<a-card :bordered="false">
<yo-table
:columns="columns"
:load-data="loadData"
@query="onQuery"
@resetQuery="onResetQuery"
ref="table"
>
<Auth auth="sysOrg:page" slot="query">
<!-- 此处添加查询表单控件 -->
<!-- ... -->
<a-form-model-item label="机构名称">
<a-input allow-clear placeholder="请输入机构名称" v-model="query.name" />
</a-form-model-item>
</Auth>
<Auth auth="sysOrg:add" slot="operator">
<a-button @click="onOpen('add-form')" icon="plus">新增机构</a-button>
</Auth>
<!-- 格式化字段内容 -->
<!-- ... -->
<!-- 添加操作控件 -->
<span slot="action" slot-scope="text, record">
<yo-table-actions>
<Auth auth="sysOrg:edit">
<a @click="onOpen('edit-form', record)">编辑</a>
</Auth>
<Auth auth="sysOrg:delete">
<a-popconfirm @confirm="onDelete(record)" placement="topRight" title="是否确认删除">
<a>删除</a>
</a-popconfirm>
</Auth>
<!-- 可在此处添加其他操作控件 -->
<container>
<br />
<a-card :bordered="false">
<yo-table
:columns="columns"
:load-data="loadData"
@query="onQuery"
@resetQuery="onResetQuery"
ref="table"
>
<Auth auth="sysOrg:page" slot="query">
<!-- 此处添加查询表单控件 -->
<!-- ... -->
</yo-table-actions>
</span>
</yo-table>
</a-card>
<a-form-model-item label="机构名称">
<a-input allow-clear placeholder="请输入机构名称" v-model="query.name" />
</a-form-model-item>
</Auth>
<Auth auth="sysOrg:add" slot="operator">
<a-button @click="onOpen('add-form')" icon="plus">新增机构</a-button>
</Auth>
<!-- 格式化字段内容 -->
<!-- ... -->
<!-- 添加操作控件 -->
<span slot="action" slot-scope="text, record">
<yo-table-actions>
<Auth auth="sysOrg:edit">
<a @click="onOpen('edit-form', record)">编辑</a>
</Auth>
<Auth auth="sysOrg:delete">
<a-popconfirm @confirm="onDelete(record)" placement="topRight" title="是否确认删除">
<a>删除</a>
</a-popconfirm>
</Auth>
<!-- 可在此处添加其他操作控件 -->
<!-- ... -->
</yo-table-actions>
</span>
</yo-table>
</a-card>
<!-- 新增表单 -->
<yo-modal-form :action="$api[api.add]" :title="'新增' + name" @ok="onReloadData" ref="add-form">
<form-body />
</yo-modal-form>
<!-- 新增表单 -->
<yo-modal-form :action="$api[api.add]" :title="'新增' + name" @ok="onReloadData" ref="add-form">
<form-body />
</yo-modal-form>
<!-- 编辑表单 -->
<yo-modal-form :action="$api[api.edit]" :title="'编辑' + name" @ok="onReloadData" ref="edit-form">
<form-body />
</yo-modal-form>
</container>
</yo-tree-layout>
<!-- 编辑表单 -->
<yo-modal-form
:action="$api[api.edit]"
:title="'编辑' + name"
@ok="onReloadData"
ref="edit-form"
>
<form-body />
</yo-modal-form>
</container>
</yo-tree-layout>
</template>
<script>
import FormBody from './form';
@@ -167,7 +172,7 @@ export default {
*/
onResetQuery() {
/** 在这里重置查询条件时,可对特殊的字段做保留处理 */
Object.keys(this.query).forEach((p) => {
Object.keys(this.query).forEach((p) => {
if (p !== 'pid') {
this.query[p] = undefined;
}
@@ -219,14 +224,12 @@ export default {
* 从列表页调用窗口的打开方法
*/
onOpen(formName, record) {
this.$refs[formName].onOpen(
this.$refs[formName].onOpen({
record,
/* 按需添加其他参数 */
/* ... */
this.query['pid']
);
orgId: this.query['pid'],
});
},
/**
* 必要方法

View File

@@ -1,74 +0,0 @@
<template>
<a-modal
:confirmLoading="confirmLoading"
:visible="visible"
@cancel="onCancel"
@ok="onOk"
class="yo-modal-form"
title="新增职位"
>
<FormBody ref="form-body" />
</a-modal>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口,并填充外部传入的数据
*/
onOpen() {
this.visible = true;
// this.$nextTick(() => {
// this.$refs['form-body'].onFillData(record);
// });
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.$refs['form-body'].onValidate((valid) => {
if (valid) {
this.confirmLoading = true;
this.$api
.sysPosAdd(this.$refs['form-body'].form)
.then(({ success }) => {
this.confirmLoading = false;
if (success) {
this.$message.success('编辑成功');
this.onCancel();
this.$emit('ok');
}
})
.finally(() => {
this.confirmLoading = false;
});
}
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onCancel() {
this.$refs['form-body'].onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -1,74 +0,0 @@
<template>
<a-modal
:confirmLoading="confirmLoading"
:visible="visible"
@cancel="onCancel"
@ok="onOk"
class="yo-modal-form"
title="职位编辑"
>
<FormBody ref="form-body" />
</a-modal>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口,并填充外部传入的数据
*/
onOpen(record) {
this.visible = true;
this.$nextTick(() => {
this.$refs['form-body'].onFillData(record);
});
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.$refs['form-body'].onValidate((valid) => {
if (valid) {
this.confirmLoading = true;
this.$api
.sysPosEdit(this.$refs['form-body'].form)
.then(({ success }) => {
this.confirmLoading = false;
if (success) {
this.$message.success('编辑成功');
this.onCancel();
this.$emit('ok');
}
})
.finally(() => {
this.confirmLoading = false;
});
}
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onCancel() {
this.$refs['form-body'].onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -1,50 +1,99 @@
<template>
<!--
普通编辑窗体
v 1.2
2021-04-30
Lufthafen
-->
<a-form-model :model="form" :rules="rules" class="yo-form" ref="form">
<div class="yo-form-group">
<a-form-model-item label="职位名称" prop="name">
<a-input placeholder="请输入职位名称" v-model="form.name" />
</a-form-model-item>
<a-form-model-item label="唯一编码" prop="code">
<a-input placeholder="请输入唯一编码" v-model="form.code" />
</a-form-model-item>
<a-form-model-item label="排序" prop="sort">
<a-input-number
:max="1000"
:min="0"
class="w-100-p"
placeholder="请输入排序"
v-model="form.sort"
/>
</a-form-model-item>
<a-form-model-item
label="备注"
prop="remark"
>
<a-textarea v-model="form.remark" :rows="4" placeholder="请输入备注"></a-textarea>
<a-spin :spinning="loading">
<a-icon slot="indicator" spin type="loading" />
<div class="yo-form-group">
<!-- 表单控件 -->
<!-- ... -->
<a-form-model-item label="职位名称" prop="name">
<a-input placeholder="请输入职位名称" v-model="form.name" />
</a-form-model-item>
</div>
<a-form-model-item label="唯一编码" prop="code">
<a-input placeholder="请输入唯一编码" v-model="form.code" />
</a-form-model-item>
<a-form-model-item label="排序" prop="sort">
<a-input-number
:max="1000"
:min="0"
class="w-100-p"
placeholder="请输入排序"
v-model="form.sort"
/>
</a-form-model-item>
<a-form-model-item label="备注" prop="remark">
<a-textarea :rows="4" placeholder="请输入备注" v-model="form.remark"></a-textarea>
</a-form-model-item>
</div>
</a-spin>
</a-form-model>
</template>
<script>
/* 表单内容默认值 */
const defaultForm = {
/* ... */
sort: 100,
};
export default {
data() {
return {
form: {
active: 'N',
},
/** 表单数据 */
form: {},
/** 验证格式 */
rules: {
name: [{ required: true, message: '请输入应用名称' }],
code: [{ required: true, message: '请输入唯一编码' }],
/* ... */
name: [{ required: true, message: '请输入应用名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入唯一编码', trigger: 'blur' }],
},
/** 加载异步数据状态 */
loading: false,
/** 其他成员属性 */
/* ... */
};
},
methods: {
/**
* 必要的方法
* 在打开编辑页时允许填充数据
*/
onFillData(record) {
this.form = this.$_.cloneDeep(record);
onFillData(params) {
/** 将默认数据覆盖到form */
this.form = this.$_.cloneDeep({
...defaultForm,
...params.record,
/** 在此处添加其他默认数据转换 */
/* ... */
});
},
/**
* 必要方法
* 验证表单并获取表单数据
*/
onGetData() {
return new Promise((reslove, reject) => {
this.$refs.form.validate((valid) => {
if (valid) {
const record = this.$_.cloneDeep(this.form);
/** 验证通过后可以对数据进行转换得到想要提交的格式 */
/* ... */
reslove(record);
} else {
reject();
}
});
});
},
/**
@@ -61,10 +110,26 @@ export default {
*/
onResetFields() {
setTimeout(() => {
this.form = {};
this.$refs.form.resetFields();
/** 在这里可以初始化当前组件中其他属性 */
/* ... */
}, 300);
},
/**
* 必要方法
* 加载当前表单中所需要的异步数据
*/
async onInit(params) {
this.loading = true;
/** 可以在这里await获取一些异步数据 */
/* ... */
this.loading = false;
},
/** 当前组件的其他方法 */
/* ... */
},
};
</script>

View File

@@ -1,30 +1,36 @@
<template>
<!--
普通查询表格
v 1.2
2021-04-30
Lufthafen
-->
<container>
<br />
<a-card :bordered="false">
<Auth auth="sysPos:page">
<div class="yo-query-bar">
<a-form-model :model="query" layout="inline">
<a-form-model-item label="职位名称">
<a-input placeholder="请输入职位名称" v-model="query.name" />
</a-form-model-item>
<a-form-model-item label="唯一编码">
<a-input placeholder="请输入唯一编码" v-model="query.code" />
</a-form-model-item>
<a-form-model-item>
<a-button-group>
<a-button @click="onQuery" type="primary">查询</a-button>
<a-button @click="() => {(query = {}), onQuery();}">重置</a-button>
</a-button-group>
</a-form-model-item>
</a-form-model>
</div>
</Auth>
<yo-table :columns="columns" :load-data="loadData" ref="table">
<yo-table
:columns="columns"
:load-data="loadData"
@query="onQuery"
@resetQuery="onResetQuery"
ref="table"
>
<Auth auth="sysPos:page" slot="query">
<!-- 此处添加查询表单控件 -->
<!-- ... -->
<a-form-model-item label="职位名称">
<a-input placeholder="请输入职位名称" v-model="query.name" />
</a-form-model-item>
<a-form-model-item label="唯一编码">
<a-input placeholder="请输入唯一编码" v-model="query.code" />
</a-form-model-item>
</Auth>
<Auth auth="sysPos:add" slot="operator">
<a-button @click="onOpen('add-form')" icon="plus">新增职位</a-button>
</Auth>
<!-- 格式化字段内容 -->
<!-- ... -->
<!-- 添加操作控件 -->
<span slot="action" slot-scope="text, record">
<yo-table-actions>
<Auth auth="sysPos:edit">
@@ -35,26 +41,50 @@
<a>删除</a>
</a-popconfirm>
</Auth>
<!-- 可在此处添加其他操作控件 -->
<!-- ... -->
</yo-table-actions>
</span>
</yo-table>
</a-card>
<br />
<edit-form @ok="onReloadData" ref="edit-form" />
<add-form @ok="onReloadData" ref="add-form" />
<!-- 新增表单 -->
<yo-modal-form :action="$api[api.add]" :title="'新增' + name" @ok="onReloadData" ref="add-form">
<form-body />
</yo-modal-form>
<!-- 编辑表单 -->
<yo-modal-form :action="$api[api.edit]" :title="'编辑' + name" @ok="onReloadData" ref="edit-form">
<form-body />
</yo-modal-form>
</container>
</template>
<script>
import AddForm from './addForm';
import editForm from './editForm';
import FormBody from './form';
/* 在此管理整个页面需要的接口名称 */
const api = {
page: 'sysPosPage',
add: 'sysPosAdd',
edit: 'sysPosEdit',
delete: 'sysPosDelete',
/* ... */
};
export default {
components: {
AddForm,
editForm,
FormBody,
},
data() {
return {
api,
name: '职位',
/* 查询条件 */
query: {},
/* 表格字段 */
columns: [
{
title: '职位名称',
@@ -77,17 +107,28 @@ export default {
sorter: true,
},
],
/* 字典编码储存格式 */
codes: {
code1: [],
code2: [],
},
};
},
created() {
if (this.$auth({ sysPos: [['edit'], ['delete']] })) {
/** 按需加载字典编码 */
this.onLoadCodes();
/** 根据权限添加操作列 */
const flag = this.$auth({
sysPos: [['edit'], ['delete']],
});
if (flag) {
this.columns.push({
title: '操作',
width: '200px',
width: '150px',
dataIndex: 'action',
scopedSlots: {
customRender: 'action',
},
scopedSlots: { customRender: 'action' },
});
}
},
@@ -97,14 +138,12 @@ export default {
* 传给yo-table以示意数据接口及其参数和返回的数据结构
*/
loadData(params) {
return this.$api
.sysPosPage({
...params,
...this.query,
})
.then((res) => {
return res.data;
});
return this.$api[api.page]({
...params,
...this.query,
}).then((res) => {
return res.data;
});
},
/**
@@ -115,6 +154,16 @@ export default {
this.$refs.table.onReloadData(true);
},
/**
* 有查询功能时的必要方法
* 重置查询条件
*/
onResetQuery() {
/** 在这里重置查询条件时,可对特殊的字段做保留处理 */
this.query = {};
this.onQuery();
},
/**
* 必要方法
* 重新列表数据
@@ -124,26 +173,60 @@ export default {
},
/**
* 有编辑新增功能的必要方法
* 必要方法
* 加载字典数据
* 如果不需要获取相应的字典数据,此方法内容可空
*/
onLoadCodes() {},
/**
* 必要方法
* 绑定数据字典值
*/
bindCodeValue(code, name) {
const c = this.codes[name].find((p) => p.code == code);
if (c) {
return c.value;
}
return null;
},
/**
* 必要方法
* 从列表页调用窗口的打开方法
*/
onOpen(formName, record) {
this.$refs[formName].onOpen(record);
this.$refs[formName].onOpen({
record,
/* 按需添加其他参数 */
/* ... */
});
},
/**
* 必要方法
* 可以用做一系列操作的公共回调,此方法中会重新加载当前列表
*/
onResult(success, successMessage) {
if (success) {
this.$message.success(successMessage);
this.onReloadData();
}
this.$refs.table.onLoaded();
},
/**
* 必要方法
* 删除时调用
*/
onDelete(record) {
this.$refs.table.onLoading();
this.$api.sysPosDelete(record).then(({ success, message }) => {
this.onResult(success, '删除成功');
});
this.$api[api.delete](record)
.then(({ success }) => {
this.onResult(success, '删除成功');
})
.finally(() => {
this.$refs.table.onLoaded();
});
},
},
};

View File

@@ -1,71 +0,0 @@
<template>
<a-modal
:confirmLoading="confirmLoading"
:visible="visible"
@cancel="onCancel"
@ok="onOk"
class="yo-modal-form"
title="新增角色"
>
<FormBody ref="form-body" />
</a-modal>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口
*/
onOpen() {
this.visible = true;
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.$refs['form-body'].onValidate((valid) => {
if (valid) {
this.confirmLoading = true;
this.$api
.sysRoleAdd(this.$refs['form-body'].form)
.then(({ success }) => {
this.confirmLoading = false;
if (success) {
this.$message.success('编辑成功');
this.onCancel();
this.$emit('ok');
}
})
.finally(() => {
this.confirmLoading = false;
});
}
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onCancel() {
this.$refs['form-body'].onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -83,16 +83,16 @@ export default {
},
methods: {
async onOpen(record) {
async onOpen(params) {
this.visible = true;
this.record = record;
this.record = params.record;
this.loading = true;
await this.onLoadCodes();
this.form.dataScopeType = record.dataScopeType.toString();
this.form.dataScopeType = this.record.dataScopeType.toString();
await this.onChange(record.dataScopeType);
await this.onChange(this.record.dataScopeType);
this.loading = false;
},

View File

@@ -1,75 +0,0 @@
<template>
<a-modal
:confirmLoading="confirmLoading"
:visible="visible"
@cancel="onCancel"
@ok="onOk"
class="yo-modal-form"
title="编辑角色"
>
<FormBody ref="form-body" />
</a-modal>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口,并填充外部传入的数据
*/
onOpen(record) {
this.visible = true;
this.$nextTick(() => {
this.$refs['form-body'].onFillData(record);
});
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.$refs['form-body'].onValidate((valid) => {
if (valid) {
this.confirmLoading = true;
this.$api
.sysRoleEdit(this.$refs['form-body'].form)
.then(({ success }) => {
this.confirmLoading = false;
if (success) {
this.$message.success('编辑成功');
this.onCancel();
this.$emit('ok');
}
})
.finally(() => {
this.confirmLoading = false;
});
}
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onCancel() {
this.$refs['form-body'].onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -1,50 +1,98 @@
<template>
<!--
普通编辑窗体
v 1.2
2021-04-30
Lufthafen
-->
<a-form-model :model="form" :rules="rules" class="yo-form" ref="form">
<div class="yo-form-group">
<a-form-model-item label="角色名" prop="name">
<a-input placeholder="请输入角色名" v-model="form.name" />
</a-form-model-item>
<a-form-model-item label="唯一编码" prop="code">
<a-input placeholder="请输入唯一编码" v-model="form.code" />
</a-form-model-item>
<a-form-model-item label="排序" prop="sort">
<a-input-number
:max="1000"
:min="0"
class="w-100-p"
placeholder="请输入排序"
v-model="form.sort"
/>
</a-form-model-item>
<a-form-model-item
label="备注"
prop="remark"
>
<a-textarea v-model="form.remark" :rows="4" placeholder="请输入备注"></a-textarea>
<a-spin :spinning="loading">
<a-icon slot="indicator" spin type="loading" />
<div class="yo-form-group">
<!-- 表单控件 -->
<!-- ... -->
<a-form-model-item label="角色名" prop="name">
<a-input placeholder="请输入角色名" v-model="form.name" />
</a-form-model-item>
</div>
<a-form-model-item label="唯一编码" prop="code">
<a-input placeholder="请输入唯一编码" v-model="form.code" />
</a-form-model-item>
<a-form-model-item label="排序" prop="sort">
<a-input-number
:max="1000"
:min="0"
class="w-100-p"
placeholder="请输入排序"
v-model="form.sort"
/>
</a-form-model-item>
<a-form-model-item label="备注" prop="remark">
<a-textarea :rows="4" placeholder="请输入备注" v-model="form.remark"></a-textarea>
</a-form-model-item>
</div>
</a-spin>
</a-form-model>
</template>
<script>
/* 表单内容默认值 */
const defaultForm = {
/* ... */
};
export default {
data() {
return {
form: {
active: 'N',
},
/** 表单数据 */
form: {},
/** 验证格式 */
rules: {
name: [{ required: true, message: '请输入应用名称' }],
code: [{ required: true, message: '请输入唯一编码' }],
/* ... */
name: [{ required: true, message: '请输入应用名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入唯一编码', trigger: 'blur' }],
},
/** 加载异步数据状态 */
loading: false,
/** 其他成员属性 */
/* ... */
};
},
methods: {
/**
* 必要的方法
* 在打开编辑页时允许填充数据
*/
onFillData(record) {
this.form = this.$_.cloneDeep(record);
onFillData(params) {
/** 将默认数据覆盖到form */
this.form = this.$_.cloneDeep({
...defaultForm,
...params.record,
/** 在此处添加其他默认数据转换 */
/* ... */
});
},
/**
* 必要方法
* 验证表单并获取表单数据
*/
onGetData() {
return new Promise((reslove, reject) => {
this.$refs.form.validate((valid) => {
if (valid) {
const record = this.$_.cloneDeep(this.form);
/** 验证通过后可以对数据进行转换得到想要提交的格式 */
/* ... */
reslove(record);
} else {
reject();
}
});
});
},
/**
@@ -61,10 +109,26 @@ export default {
*/
onResetFields() {
setTimeout(() => {
this.form = {};
this.$refs.form.resetFields();
/** 在这里可以初始化当前组件中其他属性 */
/* ... */
}, 300);
},
/**
* 必要方法
* 加载当前表单中所需要的异步数据
*/
async onInit(params) {
this.loading = true;
/** 可以在这里await获取一些异步数据 */
/* ... */
this.loading = false;
},
/** 当前组件的其他方法 */
/* ... */
},
};
</script>

View File

@@ -1,31 +1,36 @@
<template>
<!--
普通查询表格
v 1.2
2021-04-30
Lufthafen
-->
<container>
<br />
<a-card :bordered="false">
<Auth auth="sysRole:page">
<div class="yo-query-bar">
<a-form-model :model="query" layout="inline">
<a-form-model-item label="角色名称">
<a-input placeholder="请输入角色名称" v-model="query.name" />
</a-form-model-item>
<a-form-model-item label="唯一编号">
<a-input placeholder="请输入唯一编码" v-model="query.code" />
</a-form-model-item>
<a-form-model-item>
<a-button-group>
<a-button @click="onQuery" type="primary">查询</a-button>
<a-button @click="() => {(query = {}), onQuery();}">重置</a-button>
</a-button-group>
</a-form-model-item>
</a-form-model>
</div>
</Auth>
<yo-table :columns="columns" :load-data="loadData" ref="table">
<Auth auth="sysRole:add" slot="operator">
<a-button @click="onOpen('add-form')" icon="plus">新建角色</a-button>
<yo-table
:columns="columns"
:load-data="loadData"
@query="onQuery"
@resetQuery="onResetQuery"
ref="table"
>
<Auth auth="sysRole:page" slot="query">
<!-- 此处添加查询表单控件 -->
<!-- ... -->
<a-form-model-item label="角色名称">
<a-input placeholder="请输入角色名称" v-model="query.name" />
</a-form-model-item>
<a-form-model-item label="唯一编号">
<a-input placeholder="请输入唯一编码" v-model="query.code" />
</a-form-model-item>
</Auth>
<Auth auth="sysRole:add" slot="operator">
<a-button @click="onOpen('add-form')" icon="plus">新增角色</a-button>
</Auth>
<!-- 格式化字段内容 -->
<!-- ... -->
<!-- 添加操作控件 -->
<span slot="action" slot-scope="text, record">
<yo-table-actions>
<Auth auth="sysRole:edit">
@@ -36,6 +41,8 @@
<a>删除</a>
</a-popconfirm>
</Auth>
<!-- 可在此处添加其他操作控件 -->
<!-- ... -->
<Auth :auth="{ sysRole: [['grantMenu'], ['grantData']] }">
<a-dropdown placement="bottomRight">
<a class="ant-dropdown-link">
@@ -60,28 +67,51 @@
</span>
</yo-table>
</a-card>
<br />
<add-form @ok="onReloadData" ref="add-form" />
<edit-form @ok="onReloadData" ref="edit-form" />
<!-- 新增表单 -->
<yo-modal-form :action="$api[api.add]" :title="'新增' + name" @ok="onReloadData" ref="add-form">
<form-body />
</yo-modal-form>
<!-- 编辑表单 -->
<yo-modal-form :action="$api[api.edit]" :title="'编辑' + name" @ok="onReloadData" ref="edit-form">
<form-body />
</yo-modal-form>
<menu-form @ok="onReloadData" ref="menu-form" />
<data-form @ok="onReloadData" ref="data-form" />
</container>
</template>
<script>
import AddForm from './addForm';
import EditForm from './editForm';
import FormBody from './form';
import MenuForm from './menuForm';
import DataForm from './dataForm';
/* 在此管理整个页面需要的接口名称 */
const api = {
page: 'getRolePage',
add: 'sysRoleAdd',
edit: 'sysRoleEdit',
delete: 'sysRoleDelete',
/* ... */
};
export default {
components: {
AddForm,
EditForm,
FormBody,
MenuForm,
DataForm,
},
data() {
return {
api,
name: '角色',
/* 查询条件 */
query: {},
/* 表格字段 */
columns: [
{
title: '角色名',
@@ -99,17 +129,26 @@ export default {
sorter: true,
},
],
/* 字典编码储存格式 */
codes: {
code1: [],
code2: [],
},
};
},
created() {
/** 按需加载字典编码 */
this.onLoadCodes();
/** 根据权限添加操作列 */
const flag = this.$auth({
sysRole: [['edit'], ['delete'], ['grantMenu'], ['grantData']],
});
if (flag) {
this.columns.push({
title: '操作',
width: '200px',
width: '150px',
dataIndex: 'action',
scopedSlots: { customRender: 'action' },
});
@@ -121,14 +160,12 @@ export default {
* 传给yo-table以示意数据接口及其参数和返回的数据结构
*/
loadData(params) {
return this.$api
.getRolePage({
...params,
...this.query,
})
.then((res) => {
return res.data;
});
return this.$api[api.page]({
...params,
...this.query,
}).then((res) => {
return res.data;
});
},
/**
@@ -139,6 +176,16 @@ export default {
this.$refs.table.onReloadData(true);
},
/**
* 有查询功能时的必要方法
* 重置查询条件
*/
onResetQuery() {
/** 在这里重置查询条件时,可对特殊的字段做保留处理 */
this.query = {};
this.onQuery();
},
/**
* 必要方法
* 重新列表数据
@@ -148,26 +195,72 @@ export default {
},
/**
* 有编辑新增功能的必要方法
* 必要方法
* 加载字典数据
* 如果不需要获取相应的字典数据,此方法内容可空
*/
onLoadCodes() {
this.$api
.$queue([
this.$api.sysDictTypeDropDownAwait({ code: 'code1' }),
this.$api.sysDictTypeDropDownAwait({ code: 'code2' }),
/* ... */
])
.then(([code1, code2]) => {
this.codes.code1 = code1.data;
this.codes.code2 = code2.data;
/* ... */
});
},
/**
* 必要方法
* 绑定数据字典值
*/
bindCodeValue(code, name) {
const c = this.codes[name].find((p) => p.code == code);
if (c) {
return c.value;
}
return null;
},
/**
* 必要方法
* 从列表页调用窗口的打开方法
*/
onOpen(formName, record) {
this.$refs[formName].onOpen(record);
this.$refs[formName].onOpen({
record,
/* 按需添加其他参数 */
/* ... */
});
},
/**
* 必要方法
* 可以用做一系列操作的公共回调,此方法中会重新加载当前列表
*/
onResult(success, successMessage) {
if (success) {
this.$message.success(successMessage);
this.onReloadData();
}
this.$refs.table.onLoaded();
},
/**
* 必要方法
* 删除时调用
*/
onDelete(record) {
this.$refs.table.onLoading();
this.$api.sysRoleDel(record).then(({ success }) => {
this.onResult(success, '删除成功');
});
this.$api[api.delete](record)
.then(({ success }) => {
this.onResult(success, '删除成功');
})
.finally(() => {
this.$refs.table.onLoaded();
});
},
},
};

View File

@@ -41,9 +41,9 @@ export default {
* 必要的方法
* 从外部调用打开本窗口
*/
onOpen(record) {
onOpen(params) {
this.visible = true;
this.record = record;
this.record = params.record;
this.$nextTick(async () => {
const view = this.$refs['authority-view'];

View File

@@ -1,84 +0,0 @@
<template>
<a-modal
:confirmLoading="confirmLoading"
:visible="visible"
:width="1024"
@cancel="onCancel"
@ok="onOk"
title="新增应用"
>
<FormBody mode="add" ref="form-body" />
</a-modal>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
computed: {
formBody() {
return this.$refs['form-body'];
},
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口
*/
onOpen(record, id) {
this.visible = true;
this.$nextTick(async () => {
await this.formBody.onInit();
// 获取外部选中的部门id
this.formBody.onFillData(record, id);
});
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.formBody.onValidate((valid) => {
if (valid) {
this.confirmLoading = true;
this.$api
.sysUserAdd(this.formBody.form)
.then(({ success }) => {
if (success) {
this.$message.success('新增成功');
this.onCancel();
this.$emit('ok');
}
})
.finally(() => {
this.confirmLoading = false;
});
}
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onCancel() {
this.formBody.onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -65,9 +65,9 @@ export default {
* 必要的方法
* 从外部调用打开本窗口
*/
async onOpen(record) {
async onOpen(params) {
this.visible = true;
this.id = record.id;
this.id = params.record.id;
this.$nextTick(() => {
this.onInit();
});

View File

@@ -1,75 +0,0 @@
<template>
<a-modal
:confirmLoading="confirmLoading"
:visible="visible"
:width="1024"
@cancel="onCancel"
@ok="onOk"
title="编辑用户"
>
<FormBody mode="eidt" ref="form-body" />
</a-modal>
</template>
<script>
import FormBody from './form';
export default {
components: {
FormBody,
},
data() {
return {
visible: false,
confirmLoading: false,
};
},
methods: {
/**
* 必要的方法
* 从外部调用打开本窗口,并填充外部传入的数据
*/
onOpen(record) {
this.visible = true;
this.$nextTick(async () => {
await this.$refs['form-body'].onInit();
this.$refs['form-body'].onFillData(record);
});
},
/**
* 必要的方法
* 点击保存时的操作
*/
onOk() {
this.$refs['form-body'].onValidate((valid) => {
if (valid) {
this.confirmLoading = true;
this.$api
.sysUserEdit(this.$refs['form-body'].form)
.then(({ success }) => {
if (success) {
this.$message.success('编辑成功');
this.onCancel();
this.$emit('ok');
}
})
.finally(() => {
this.confirmLoading = false;
});
}
});
},
/**
* 必要的方法
* 关闭窗口时的操作
*/
onCancel() {
this.$refs['form-body'].onResetFields();
this.visible = false;
},
},
};
</script>

View File

@@ -1,10 +1,16 @@
<template>
<!--
普通编辑窗体
v 1.2
2021-04-30
Lufthafen
-->
<a-form-model :model="form" :rules="rules" class="yo-form" ref="form">
<a-spin :spinning="loading">
<a-icon slot="indicator" spin type="loading" />
<a-row :gutter="16">
<a-col :span="10">
<h3>基本信息</h3>
<h3 class="h3">基本信息</h3>
<div class="yo-form-group">
<a-form-model-item label="账号" prop="account">
<a-input placeholder="请输入账号" v-model="form.account" />
@@ -33,11 +39,17 @@
</a-form-model-item>
<a-form-model-item label="性别" prop="sex">
<a-radio-group v-model="form.sex">
<a-radio-button :value="0">
<a-icon class="mr-xxs" type="stop" />
<span>保密</span>
</a-radio-button>
<a-radio-button :value="1">
<a-icon :style="{ color: '#1890ff' }" type="man" />
<a-icon :style="{ color: '#1890ff' }" class="mr-xxs" type="man" />
<span></span>
</a-radio-button>
<a-radio-button :value="2">
<a-icon :style="{ color: '#eb2f96' }" type="woman" />
<a-icon :style="{ color: '#eb2f96' }" class="mr-xxs" type="woman" />
<span></span>
</a-radio-button>
</a-radio-group>
</a-form-model-item>
@@ -52,8 +64,8 @@
</a-form-model-item>
</div>
</a-col>
<a-col :span="14">
<h3>员工信息</h3>
<a-col :span="14" v-if="form.sysEmpParam">
<h3 class="h3">员工信息</h3>
<div class="yo-form-group">
<a-form-model-item label="所属组织机构" prop="sysEmpParam.orgId">
<a-tree-select
@@ -77,43 +89,45 @@
</a-select>
</a-form-model-item>
</div>
<h4>附加信息</h4>
<a-table
:columns="extColumns"
:data-source="form.sysEmpParam.extIds"
:pagination="false"
size="small"
>
<template slot="footer">
<a-button @click="onAddExtData" block icon="plus" type="dashed">新增一项</a-button>
</template>
<template slot="orgId" slot-scope="text, record">
<a-tree-select
:default-value="text"
:dropdown-style="{ maxHeight: '300px', overflow: 'auto' }"
:tree-data="orgData"
@change="value => onChangeExtData(value, record, 'orgId')"
placeholder="请选择附加组织机构"
tree-default-expand-all
/>
</template>
<template slot="posId" slot-scope="text, record">
<a-select
:default-value="text"
@change="value => onChangeExtData(value, record, 'posId')"
placeholder="请选择附加职位信息"
>
<a-select-option
:key="i"
:value="item.id"
v-for="(item, i) in posData"
>{{ item.name }}</a-select-option>
</a-select>
</template>
<template slot="action" slot-scope="text, record">
<a-button @click="onRemoveExtData(record)" size="small" type="danger">删除</a-button>
</template>
</a-table>
<h4 class="h4">附加信息</h4>
<div class="pl-md pr-md">
<a-table
:columns="extColumns"
:data-source="form.sysEmpParam.extIds"
:pagination="false"
size="small"
>
<template slot="footer">
<a-button @click="onAddExtData" block icon="plus" type="dashed">新增一项</a-button>
</template>
<template slot="orgId" slot-scope="text, record">
<a-tree-select
:default-value="text"
:dropdown-style="{ maxHeight: '300px', overflow: 'auto' }"
:tree-data="orgData"
@change="value => onChangeExtData(value, record, 'orgId')"
placeholder="请选择附加组织机构"
tree-default-expand-all
/>
</template>
<template slot="posId" slot-scope="text, record">
<a-select
:default-value="text"
@change="value => onChangeExtData(value, record, 'posId')"
placeholder="请选择附加职位信息"
>
<a-select-option
:key="i"
:value="item.id"
v-for="(item, i) in posData"
>{{ item.name }}</a-select-option>
</a-select>
</template>
<template slot="action" slot-scope="text, record">
<a-button @click="onRemoveExtData(record)" size="small" type="danger">删除</a-button>
</template>
</a-table>
</div>
</a-col>
</a-row>
</a-spin>
@@ -127,30 +141,57 @@ const compareToFirstPassword = (rule, value, callback) => {
callback();
};
/* 表单内容默认值 */
const defaultForm = {
/* ... */
sex: 0,
sysEmpParam: {},
};
export default {
props: ['mode'],
props: {
mode: {
type: String,
default: 'edit',
},
},
data() {
return {
form: {
sysEmpParam: {},
},
/** 表单数据 */
form: {},
/** 验证格式 */
rules: {
account: [{ required: true, min: 5, message: '请输入至少五个字符的账号' }],
name: [{ required: true, message: '请输入姓名' }],
/* ... */
account: [{ required: true, min: 5, message: '请输入至少五个字符的账号', trigger: 'blur' }],
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
password: [
{ required: true, min: 5, message: '请输入至少五个字符的密码' },
{ validator: validateToNextPassword },
{ required: true, min: 5, message: '请输入至少五个字符的密码', trigger: 'blur' },
{ validator: validateToNextPassword, trigger: 'blur' },
],
confirm: [
{ required: true, message: '请确认密码', trigger: 'blur' },
{ validator: compareToFirstPassword, trigger: 'blur' },
],
phone: [
{
pattern: /^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$/,
message: '手机号格式不正确',
trigger: 'blur',
},
],
email: [
{ pattern: /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/, message: '邮箱格式不正确', trigger: 'blur' },
],
confirm: [{ required: true, message: '请确认密码' }, { validator: compareToFirstPassword }],
sex: [{ required: true, message: '请选择性别' }],
phone: [{ required: true, message: '请输入手机号' }],
'sysEmpParam.orgId': [{ required: true, message: '请选择所属组织机构' }],
'sysEmpParam.posIdList': [{ required: true, message: '请选择职位信息' }],
},
/** 加载异步数据状态 */
loading: false,
/** 其他成员属性 */
/* ... */
orgData: [],
posData: [],
@@ -176,13 +217,14 @@ export default {
],
};
},
methods: {
/**
* 必要的方法
* 在打开编辑页时允许填充数据
*/
onFillData(record, orgId) {
const form = this.$_.cloneDeep(record || {});
onFillData(params) {
const form = this.$_.cloneDeep(params.record || {});
// 日期特殊处理
if (form.birthday) {
form.birthday = this.$moment(form.birthday).format('YYYY-MM-DD');
@@ -211,10 +253,38 @@ export default {
});
}
if (orgId) {
if (params.orgId) {
form.sysEmpParam.orgId = orgId;
}
this.form = form;
/** 将默认数据覆盖到form */
this.form = this.$_.cloneDeep({
...defaultForm,
...form,
/** 在此处添加其他默认数据转换 */
/* ... */
});
},
/**
* 必要方法
* 验证表单并获取表单数据
*/
onGetData() {
return new Promise((reslove, reject) => {
this.$refs.form.validate((valid) => {
if (valid) {
const record = this.$_.cloneDeep(this.form);
/** 验证通过后可以对数据进行转换得到想要提交的格式 */
/* ... */
reslove(record);
} else {
reject();
}
});
});
},
/**
@@ -231,20 +301,32 @@ export default {
*/
onResetFields() {
setTimeout(() => {
this.form = {
sysEmpParam: {},
};
this.$refs.form.resetFields();
/** 在这里可以初始化当前组件中其他属性 */
/* ... */
this.form = {
...defaultForm,
};
}, 300);
},
async onInit() {
/**
* 必要方法
* 加载当前表单中所需要的异步数据
*/
async onInit(params) {
this.loading = true;
/** 可以在这里await获取一些异步数据 */
/* ... */
this.orgData = await this.onLoadOrgData();
this.posData = await this.onLoadPosData();
this.loading = false;
},
/** 当前组件的其他方法 */
/* ... */
onLoadOrgData() {
return this.$api.getOrgTree().then(({ data }) => {
return data;

View File

@@ -1,28 +1,41 @@
<template>
<yo-tree-layout :load-data="loadTreeData" @select="onSelect" default-expanded-keys>
<!--
普通树查询表格
v 1.2
2021-04-30
Lufthafen
-->
<yo-tree-layout
:load-data="loadTreeData"
@select="onSelect"
default-expanded-keys
ref="tree-layout"
>
<container>
<a-alert closable type="error">
<template slot="message">
后端bug:生日不填写,在保存时会默认写入0001-01-01
<br />权限问题,在这里需要用到组织架构以及职位的数据接口,所以必须配置该两项的菜单
</template>
</a-alert>
<br />
<a-card :bordered="false">
<Auth auth="sysUser:page">
<div class="yo-query-bar">
<a-form-model :model="query" layout="inline">
<a-form-model :model="query" @submit.native.prevent layout="inline">
<a-form-model-item label="关键词">
<a-input allow-clear placeholder="请输入姓名、账号、手机号" v-model="query.searchValue" />
</a-form-model-item>
<a-form-model-item label="状态">
<a-select :style="{ width: '170px' }" allow-clear placeholder="请选择状态" v-model="query.searchStatus">
<a-select-option :key="i" :value="item.code" v-for="(item, i) in codes.find((p) => p.code === 'common_status').values">{{ item.value }}</a-select-option>
<a-select
:style="{ width: '170px' }"
allow-clear
placeholder="请选择状态"
v-model="query.searchStatus"
>
<a-select-option
:key="i"
:value="item.code"
v-for="(item, i) in codes.common_status"
>{{ item.value }}</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item>
<a-button-group>
<a-button @click="onQuery" type="primary">查询</a-button>
<a-button @click="onQuery" html-type="submit" type="primary">查询</a-button>
<a-button @click="onReset">重置</a-button>
</a-button-group>
</a-form-model-item>
@@ -66,7 +79,14 @@
<a-list-item-meta>
<div slot="title">{{ record.nickName || record.name }}</div>
<div slot="description">{{ record.account }}</div>
<yo-image :id="record.avatar" :size="48" icon="user" shape="square" slot="avatar" type="avatar" />
<yo-image
:id="record.avatar"
:size="48"
icon="user"
shape="square"
slot="avatar"
type="avatar"
/>
</a-list-item-meta>
<div class="yo-list-content--h">
<div class="yo-list-content--h--item">
@@ -75,11 +95,21 @@
</div>
<div class="yo-list-content--h--item">
<span>手机</span>
<p>{{ record.phone }}</p>
<p>{{ record.phone || '未设置' }}</p>
</div>
<div class="yo-list-content--h--item">
<span>邮箱</span>
<p>{{ record.email || '未设置' }}</p>
</div>
<Auth auth="sysUser:changeStatus">
<div class="yo-list-content--h--item">
<a-switch :checked="!record.status" :checked-children="bindCodeValue(0, 'common_status')" :loading="record.statusChanging" :un-checked-children="bindCodeValue(1, 'common_status')" @change="(checked) => onSetUserStatus(record, checked)" />
<div class="yo-list-content--h--item text-center">
<a-switch
:checked="!record.status"
:checked-children="bindCodeValue(0, 'common_status')"
:loading="record.statusChanging"
:un-checked-children="bindCodeValue(1, 'common_status')"
@change="(checked) => onSetUserStatus(record, checked)"
/>
</div>
</Auth>
</div>
@@ -87,49 +117,96 @@
</yo-list>
</a-card>
</container>
<add-form @ok="onReloadData" ref="add-form" />
<edit-form @ok="onReloadData" ref="edit-form" />
<!-- 新增表单 -->
<yo-modal-form
:action="$api[api.add]"
:title="'新增' + name"
:width="1024"
@ok="onReloadData"
ref="add-form"
>
<form-body mode="add" />
</yo-modal-form>
<!-- 编辑表单 -->
<yo-modal-form
:action="$api[api.edit]"
:title="'编辑' + name"
:width="1024"
@ok="onReloadData"
ref="edit-form"
>
<form-body mode="edit" />
</yo-modal-form>
<role-form @ok="onReloadData" ref="role-form" />
<data-form @ok="onReloadData" ref="data-form" />
</yo-tree-layout>
</template>
<script>
/* 需要引用YoTreeLayout组件 */
import YoTreeLayout from '@/components/yoTreeLayout';
import YoList from '@/components/yoList';
import FormBody from './form';
import AddForm from './addForm';
import EditForm from './editForm';
import RoleForm from './roleForm';
import DataForm from './dataForm';
/* 在此管理整个页面需要的接口名称 */
const api = {
tree: 'getOrgTree',
page: 'getUserPage',
add: 'sysUserAdd',
edit: 'sysUserEdit',
delete: 'sysUserDelete',
/* ... */
};
export default {
components: {
YoTreeLayout,
YoList,
AddForm,
EditForm,
FormBody,
RoleForm,
DataForm,
},
data() {
return {
api,
name: '用户',
/* 查询条件 */
query: {},
codes: [
{
code: 'sex',
values: [],
},
{
code: 'common_status',
values: [],
},
],
/* 表格字段 */
columns: [],
/* 字典编码储存格式 */
codes: {
sex: [],
common_status: [],
},
};
},
created() {
this.onLoadCodes();
/** 根据权限添加操作列 */
const flag = this.$auth({
sysUser: [['edit'], ['delete'], ['grantRole'], ['grantData']],
});
if (flag) {
this.columns.push({
title: '操作',
width: '150px',
dataIndex: 'action',
scopedSlots: { customRender: 'action' },
});
}
},
methods: {
@@ -138,12 +215,17 @@ export default {
* 传给yo-table-layout以示意数据接口
*/
loadTreeData() {
return this.$api.getOrgTree().then((res) => {
return this.$api[api.tree]().then((res) => {
return res.data;
});
},
/**
* 树形选择界面必要的方法
* 选择树节点事件
*/
onSelect([id]) {
/** 在选择事件中可以对右侧表格添加父节点id的查询条件 */
this.query = {
'sysEmpParam.orgId': id,
};
@@ -155,14 +237,12 @@ export default {
* 传给yo-table以示意数据接口及其参数和返回的数据结构
*/
loadData(params) {
return this.$api
.getUserPage({
...params,
...this.query,
})
.then((res) => {
return res.data;
});
return this.$api[api.page]({
...params,
...this.query,
}).then((res) => {
return res.data;
});
},
/**
@@ -173,9 +253,14 @@ export default {
this.$refs.list.onReloadData(true);
},
/**
* 必要方法
* 重新列表数据
*/
onReset() {
/* 与普通查询页不同的是,这里的父节点参数不应该在重置后被清空 */
Object.keys(this.query).forEach((p) => {
if (p !== 'sysEmpParam.orgId') {
if (p !== 'pid') {
this.query[p] = undefined;
}
});
@@ -188,19 +273,34 @@ export default {
*/
onReloadData() {
this.$refs.list.onReloadData();
this.$refs['tree-layout'].onReloadData();
},
/**
* 加载字典数据时的必要方法
* 必要方法
* 加载字典数据
* 如果不需要获取相应的字典数据,此方法内容可空
*/
onLoadCodes() {
this.$api.$queue([this.$api.sysDictTypeDropDownAwait({ code: 'sex' }), this.$api.sysDictTypeDropDownAwait({ code: 'common_status' })]).then(([sex, commonStatus]) => {
this.codes.find((p) => p.code === 'sex').values = sex.data;
this.codes.find((p) => p.code === 'common_status').values = commonStatus.data;
});
this.$api
.$queue([
this.$api.sysDictTypeDropDownAwait({ code: 'sex' }),
this.$api.sysDictTypeDropDownAwait({ code: 'common_status' }),
/* ... */
])
.then(([code1, code2]) => {
this.codes.sex = code1.data;
this.codes.common_status = code2.data;
/* ... */
});
},
/**
* 必要方法
* 绑定数据字典值
*/
bindCodeValue(code, name) {
const c = this.codes.find((p) => p.code == name).values.find((p) => p.code == code);
const c = this.codes[name].find((p) => p.code == code);
if (c) {
return c.value;
}
@@ -212,13 +312,40 @@ export default {
* 从列表页调用窗口的打开方法
*/
onOpen(formName, record) {
try {
this.$refs[formName].onOpen(record, this.query['sysEmpParam.orgId']);
} catch {
console.warn('component open method not found');
this.$refs[formName].onOpen({
record,
orgId: this.query['sysEmpParam.orgId'],
/* 按需添加其他参数 */
/* ... */
});
},
/**
* 必要方法
* 可以用做一系列操作的公共回调,此方法中会重新加载当前列表
*/
onResult(success, successMessage) {
if (success) {
this.$message.success(successMessage);
this.onReloadData();
}
},
/**
* 必要方法
* 删除时调用
*/
onDelete(record) {
this.$refs.list.onLoading();
this.$api[api.delete](record)
.then(({ success }) => {
this.onResult(success, '删除成功');
})
.finally(() => {
this.$refs.list.onLoaded();
});
},
onSetUserStatus(record, checked) {
this.$set(record, 'statusChanging', true);
this.$api
@@ -252,17 +379,6 @@ export default {
}
});
},
onDelete(record) {
this.$api.sysUserDelete(record).then(({ success, message }) => {
if (success) {
this.$message.success('删除成功');
this.onReloadData();
} else {
this.$message.error(message);
}
});
},
},
};
</script>

View File

@@ -45,9 +45,9 @@ export default {
* 必要的方法
* 从外部调用打开本窗口
*/
async onOpen(record) {
async onOpen(params) {
this.visible = true;
this.id = record.id;
this.id = params.record.id;
this.$nextTick(() => {
this.onInit();
});

View File

@@ -10,6 +10,8 @@ export default {
return {
searchText: '',
searchResult: [],
timer: null
};
},
@@ -44,6 +46,14 @@ export default {
onSearch(value) {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.doSearch(value)
}, 300)
},
doSearch(value) {
this.searchText = value
const menus = this.$_.concat.apply(this, this.$_.cloneDeep(this.menus.map(p => p.menu)))

View File

@@ -39,7 +39,7 @@ export default {
key: 'account-home',
title: '个人中心',
icon: 'user',
path: '/account'
path: '/system/account'
})
},