This commit is contained in:
zhuoda
2020-01-11 09:10:29 +08:00
parent e55e1b2f33
commit 215556f73a
608 changed files with 7 additions and 3 deletions

View File

@@ -0,0 +1,33 @@
<template>
<div>
<iframe :src="url" frameborder="0" height="800" scrolling="yes" width="100%"></iframe>
</div>
</template>
<script>
import config from '@/config';
const baseUrl = config.baseUrl.apiUrl;
export default {
name: 'Swagger',
components: {},
props: {},
data() {
return {
url: baseUrl + '/swaggerApi'
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {}
};
</script>

View File

@@ -0,0 +1,214 @@
<template>
<div>
<Card class="warp-card" dis-hover>
<Form :model="searchForm" class="tools" inline ref="searchForm">
<FormItem prop="startDate">
<DatePicker placeholder="请选择开始日期" type="datetime" v-model="searchForm.startDate"></DatePicker>
</FormItem>
<FormItem prop="endDate">
<DatePicker placeholder="请选择截止日期" type="datetime" v-model="searchForm.endDate"></DatePicker>
</FormItem>
<FormItem prop="title">
<Input placeholder="请输入标题" type="text" v-model="searchForm.title" />
</FormItem>
<FormItem prop="sendStatus">
<Select style="width:200px" v-model="searchForm.sendStatus">
<Option :value="1">已发送</Option>
<Option :value="0">未发送</Option>
</Select>
</FormItem>
<FormItem>
<ButtonGroup>
<Button @click="find" icon="ios-search" type="primary" v-privilege="'email-query'">查询</Button>
<Button @click="reset" icon="md-refresh" type="default" v-privilege="'email-query'">重置</Button>
</ButtonGroup>
</FormItem>
<FormItem>
<Button @click="addNew" icon="md-add" type="primary" v-privilege="'email-add'">新增</Button>
</FormItem>
</Form>
<Table :columns="columns" :data="tableData"></Table>
<Page
:current="searchForm.pageNum"
:page-size="searchForm.pageSize"
:page-size-opts="[10, 20, 30, 50, 100]"
:total="total"
@on-change="changePage"
@on-page-size-change="changePageSize"
show-elevator
show-sizer
show-total
style="margin:24px 0;text-align:right;"
></Page>
</Card>
</div>
</template>
<script>
import { emailApi } from '@/api/email.js';
export default {
name: 'EmailList',
components: {},
props: {},
data() {
return {
// 数据量
total: null,
// 查询参数
searchForm: {
endDate: null,
pageNum: 1,
pageSize: 10,
searchCount: true, // 是否查询总条数
sendStatus: null, // 发送状态 0未发送 1已发送
startDate: null,
title: null
},
// 表头
columns: [
{
title: '创建时间',
key: 'createTime'
},
{
title: '发送状态',
key: 'sendStatus',
render(h, params) {
let str = params.row.sendStatus == 0 ? '未发送' : '已发送';
return h('span', {}, str);
}
},
{
title: '接收人',
key: 'toEmails'
},
{
title: '标题',
key: 'title'
},
{
title: '更新时间',
key: 'updateTime'
},
{
title: '邮件内容',
key: 'content'
},
{
title: '操作',
key: 'action',
width: 160,
align: 'center',
className: 'action-hide',
render: (h, params) => {
return this.$tableAction(h, [
{
title: '编辑',
directives: [
{
name: 'privilege',
value: 'email-update'
}
],
action: () => {
this.editEmail(params);
}
},
{
title: '删除',
directives: [
{
name: 'privilege',
value: 'email-delete'
}
],
action: () => {
this.deleteEmail(params);
}
}
]);
}
}
],
// table数据
tableData: [],
// 删除id
delId: null
};
},
computed: {},
watch: {},
filters: {},
created() {
this.getEmailMess();
},
methods: {
// 编辑邮件
editEmail(params) {
this.$router.push({
path: '/email/send-mail',
query: { id: params.row.id }
});
},
// 新增
addNew() {
this.$router.push({ path: '/email/send-mail' });
},
// 重置
reset() {
this.$refs.searchForm.resetFields();
this.searchForm.startDate = null;
this.searchForm.endDate = null;
this.$set(this.searchForm, 'endDate', null);
this.find();
},
// 删除确定
async deleteSure() {
this.$Spin.show();
let res = await emailApi.deleteEmail(this.delId);
this.$Message.success('删除成功');
this.$Spin.hide();
this.getEmailMess();
},
// 删除操作
deleteEmail(params) {
this.delId = params.row.id;
this.$Modal.confirm({
title: '友情提醒',
content: '确定要删除吗?',
onOk: () => {
this.deleteSure(params.row.id);
}
});
},
// 查询
find() {
this.searchForm.pageNum = 1;
this.searchForm.pageSize = 10;
this.getEmailMess();
},
// 更改分页查询条数
changePageSize(pageSize) {
this.searchForm.pageNum = 1;
this.searchForm.pageSize = pageSize;
this.getEmailMess();
},
// 获取邮件数据
async getEmailMess() {
this.$Spin.show();
let res = await emailApi.getEmail(this.searchForm);
this.$Spin.hide();
this.tableData = res.data.list;
this.total = res.data.total;
if (res.data.pages < this.searchForm.pageNum) {
this.searchForm.pageNum = res.data.pages;
}
},
// 页码改变
changePage(pageNum) {
this.searchForm.pageNum = pageNum;
this.getEmailMess();
}
}
};
</script>

View File

@@ -0,0 +1,214 @@
<template>
<div>
<Card class="warp-card" dis-hover>
<Form :label-width="80" :model="form" :rules="ruleInline">
<FormItem class="marginBottom20" label="收件人" prop="toEmails">
<Input class="addressWidth" placeholder="请输入对方邮箱" v-model="form.toEmails"></Input>
</FormItem>
<FormItem class="marginBottom20" label="标题" prop="title">
<Input class="addressWidth" placeholder="请输入标题" v-model="form.title"></Input>
</FormItem>
<FormItem label="内容" required>
<div id="editor"></div>
</FormItem>
<FormItem>
<Button
:loading="isShowSaveButtonLoading"
@click="addOrUpdateEmail"
type="primary"
v-privilege="'email-send'"
>保存</Button>
</FormItem>
</Form>
</Card>
</div>
</template>
<script>
import WangEditor from 'wangeditor';
import Cookies from 'js-cookie';
import config from '@/config';
import { fileApi } from '@/api/file';
import { emailApi } from '@/api/email';
const baseUrl = config.baseUrl.apiUrl;
export default {
name: 'SendMail',
components: {},
props: {},
data() {
let baseUrl = process.env.VUE_APP_URL;
return {
// loading
isShowSaveButtonLoading: false,
// 上传
upload: {
uploadList: [],
data: {
type: 'NEWS_PIC'
}
},
// 富文本编辑器对象
editor: null,
// 基础路径
baseUrl: baseUrl,
// 传输内容
form: {
// 收件人
toEmails: '',
title: '',
content: ''
},
// 验证规则
ruleInline: {
toEmails: [
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '请输入正确邮箱格式', trigger: 'blur' }
],
title: [{ required: true, message: '请输入标题', trigger: 'blur' }]
}
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
this.initEditor();
this.getEmailDetails();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 后退
goBack() {
this.$router.closeCurrentPage();
},
// 富文本初始化
initEditor() {
let g = this;
g.editor = new WangEditor('#editor');
g.editor.customConfig = {
// 功能键
menus: [
'head', // 标题
'bold', // 粗体
'fontSize', // 字号
'fontName', // 字体
'italic', // 斜体
'underline', // 下划线
'strikeThrough', // 删除线
'foreColor', // 文字颜色
'backColor', // 背景颜色
'list', // 列表
'justify', // 对齐方式
'emoticon', // 表情
'image', // 插入图片
'table', // 表格
'undo', // 撤销
'redo' // 重复
],
showLinkImg: false,
uploadImgShowBase64: false,
// 上传路径
uploadImgServer: g.baseUrl + fileApi.fileUploadUrl + '1',
// 上传文件名key
uploadFileName: 'file',
// 参数
uploadImgParams: {
'x-access-token': Cookies.get('token')
},
uploadImgHooks: {
customInsert: function(insertImg, result, editor) {
insertImg(result.data.url);
}
}
};
g.editor.create();
},
// 保存邮件
addOrUpdateEmail() {
// 富文本
this.form.content = this.editor.txt.html();
// 纯文本
let newsId = this.$route.query.id;
if (newsId) {
// 编辑
this.editEmail();
} else {
// 新增
this.addNew();
}
},
// 新增
async addNew() {
try {
this.$Spin.show();
this.isShowSaveButtonLoading = true;
let res = await emailApi.addEmail(this.form);
this.isShowSaveButtonLoading = false;
let sendEmailResult = await emailApi.sendEmail(res.data);
this.$Spin.hide();
this.$Message.success('发送成功');
this.goBack();
} catch (error) {
//TODO zhuoda sentry
console.error(e);
this.isShowSaveButtonLoading = false;
this.$Spin.hide();
}
},
// 编辑
async editEmail() {
this.isShowSaveButtonLoading = true;
this.$Spin.show();
try {
let res = await emailApi.updateEmail(this.form);
this.isShowSaveButtonLoading = false;
this.$Spin.hide();
this.$Message.success('编辑成功');
this.goBack();
} catch (error) {
//TODO zhuoda sentry
console.error(e);
this.isShowSaveButtonLoading = false;
} finally {
this.$Spin.hide();
}
},
// 获取详情
async getEmailDetails() {
this.$Spin.show();
try {
let id = this.$route.query.id;
if (id) {
let res = await emailApi.getEmailDetails(id);
this.form = res.data;
this.editor.txt.html(res.data.content);
}
} catch (error) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.$Spin.hide();
}
}
}
};
</script>
<style lang="less" scoped>
.addressWidth {
width: 350px;
}
.marginTop20 {
margin-top: 20px;
}
.marginBottom20 {
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,124 @@
<template>
<span @click="selectItem" style="display: inline-block">
<span
:style="itemData.selected ? selectStyle : currentStyle "
@mouseout="onMouseout"
@mouseover="onMouseover"
>
<Icon :type="itemData.icon" style="margin-right: 8px" />
{{itemData.title}}
</span>
</span>
</template>
<script>
export default {
name: 'DepartmentEmployeeTreeItem',
components: {},
props: {
// 数据样式 是否选中 图标
// {
// title: department.name,
// icon: icon,
// isEmployee: false,
// id: department.id,
// selectFunction: obj => {
// if (this.isDepartment) {
// }
// }
// }
itemData: {
type: Object,
required: true
},
// true 查部门
isDepartment: {
type: Boolean,
default: false
}
},
data() {
return {
currentStyle: {
backgroundColor: '#FFF',
color: '#495060',
padding: '3px 8px'
},
hoverStyle: {
backgroundColor: '#2d8cf0',
color: '#FFF',
padding: '3px 8px'
},
selectStyle: {
backgroundColor: '#2d8cf0',
color: '#FFF',
padding: '3px 8px'
},
style: {
backgroundColor: '#FFF',
color: '#495060',
padding: '3px 8px'
}
};
},
computed: {},
watch: {},
filters: {},
created() {
if (this.itemData.eventbus) {
this.itemData.eventbus.$on('select', this.listenSelect);
}
},
mounted() {},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {
if (this.itemData.eventbus) {
this.itemData.eventbus.$off('select', this.listenSelect);
}
},
destroyed() {},
activated() {},
methods: {
// 点击选中当前部门或成员
selectItem() {
if (
(!this.isDepartment && this.itemData.isEmployee) ||
this.isDepartment
) {
if (this.itemData.selectFunction) {
this.itemData.selectFunction({
id: this.itemData.id,
name: this.itemData.title
});
}
}
},
//
listenSelect(obj) {
if (obj.id === this.itemData.id) {
this.itemData.selected = true;
} else {
this.currentStyle = this.style;
this.itemData.selected = false;
}
},
// 鼠标移入效果
onMouseover() {
if (this.itemData.selected === null || !this.itemData.selected) {
this.currentStyle = this.hoverStyle;
}
},
// 鼠标移出效果
onMouseout() {
if (this.itemData.selected === null || !this.itemData.selected) {
this.currentStyle = this.style;
}
}
}
};
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,251 @@
<template>
<div style="height: 330px;">
<Row>
<Col span="24">
<Input
@on-change="filterEmployee"
placeholder="请输入员工名称"
style="max-width: 300px"
v-if="!isDepartment"
v-model="searchKeywords"
/>
<Tree :data="treeData"></Tree>
</Col>
</Row>
</div>
</template>
<script>
import { departmentApi } from '@/api/department';
import DepartmentEmployeeTreeItem from '../department-employee-tree-item/department-employee-tree-item.vue';
import Vue from 'vue';
export default {
name: 'DepartmentEmployeeTree',
components: {
DepartmentEmployeeTreeItem
},
props: {
// true 查部门
isDepartment: {
type: Boolean,
default: false
}
},
data() {
return {
// 查询部门及员工列表
originalData: [],
// 搜索内容
searchKeywords: '',
eventbus: new Vue(),
// 当前树形选中 部门或人员
currentSelect: null,
// 生成适配的树形数据
treeData: []
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
this.getDepartmentEmployeeList();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 重置搜索内容
resetSearch() {
this.searchKeywords = '';
this.getDepartmentEmployeeList();
},
// 获取当前选择项
getSelect() {
return this.currentSelect;
},
// 输入框输入后 搜索
filterEmployee() {
this.originalData.forEach(department => {
this.recursionFilterEmployee(department);
});
this.generateTreeData();
},
// 根据searchKeywords搜索成员和部门
recursionFilterEmployee(department) {
if (department.employees) {
department.employees.forEach(e => {
if (
this.searchKeywords === '' ||
e.actualName.indexOf(this.searchKeywords) > -1
) {
e.show = true;
} else {
e.show = false;
}
});
}
if (department.children) {
department.children.forEach(item => {
this.recursionFilterEmployee(item);
});
}
},
// 让所有部门展开所有子项
filterDepartment() {
this.originalData.forEach(department => {
this.recursionFilterDepartment(department);
});
this.generateTreeData();
},
// 展开部门所有子项
recursionFilterDepartment(department) {
if (department.children) {
department.children.forEach(e => {
e.show = true;
this.recursionFilterDepartment(e);
});
}
},
// 查询部门及员工列表
async getDepartmentEmployeeList() {
this.$Spin.show();
let res = await departmentApi.getDepartmentEmployeeList();
this.$Spin.hide();
this.originalData = res.data;
if (!this.isDepartment) {
this.filterEmployee();
} else {
this.filterDepartment();
}
},
// 生成树形图数据
generateTreeData() {
let tree = [];
this.originalData.forEach(department => {
let icon = department.type == 1 ? 'md-cube' : 'md-menu';
let obj = Object.assign(
{},
{
title: department.name,
expand: true,
children: this.constractTree(department),
render: (h, { root, node, data }) => {
return h(DepartmentEmployeeTreeItem, {
props: {
isDepartment: this.isDepartment,
itemData: {
title: department.name,
icon: icon,
isEmployee: false,
id: department.id,
selectFunction: obj => {
if (this.isDepartment) {
this.currentSelect = obj;
this.eventbus.$emit('select', obj);
this.$emit('on-select', obj);
}
}
}
},
style: {
cursor: 'pointer'
}
});
}
}
);
tree.push(obj);
});
this.treeData = tree;
},
// 根据部门构建树形
constractTree(department) {
let children = [];
if (department.children) {
department.children.forEach(departmentChild => {
if (this.isDepartment && !departmentChild.show) {
return;
}
let icon = departmentChild.type == 1 ? 'md-cube' : 'md-menu';
let obj = Object.assign(
{},
{
title: departmentChild.name,
expand: true,
disabled: false,
children: this.constractTree(departmentChild),
render: (h, { root, node, data }) => {
return h(DepartmentEmployeeTreeItem, {
props: {
isDepartment: this.isDepartment,
itemData: {
title: departmentChild.name,
icon: icon,
isEmployee: false,
id: departmentChild.id,
selectFunction: obj => {
this.currentSelect = obj;
this.eventbus.$emit('select', obj);
this.$emit('on-select', obj);
}
}
},
style: {
cursor: 'pointer'
}
});
}
}
);
children.push(obj);
});
}
if (!this.isDepartment && department.employees) {
department.employees.forEach(employeeItem => {
if (employeeItem.show) {
let obj = Object.assign(
{},
{
title: employeeItem.actualName,
render: (h, { root, node, data }) => {
return h(DepartmentEmployeeTreeItem, {
props: {
isDepartment: this.isDepartment,
itemData: {
title: employeeItem.actualName,
icon: 'md-person',
isEmployee: true,
selected: false,
id: employeeItem.id,
eventbus: this.eventbus,
selectFunction: obj => {
this.currentSelect = obj;
this.eventbus.$emit('select', obj);
this.$emit('on-select', obj);
}
}
},
style: {
cursor: 'pointer'
}
});
}
}
);
children.push(obj);
}
});
}
return children;
}
}
};
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,366 @@
<template>
<div>
<!-- Card 岗位管理内容区 start -->
<Card class="warp-card" dis-hover>
<!-- Form 搜索按钮区 start -->
<Form class="tools" inline>
<FormItem>
<Input
placeholder="请输入"
v-model="searchFrom.positionName"
v-privilege="'search-position'"
>
<Button @click="searchData" icon="ios-search" slot="append"></Button>
</Input>
</FormItem>
<FormItem>
<Button
@click="clearSearch"
icon="md-refresh"
type="default"
v-privilege="'search-position'"
>重置</Button>
</FormItem>
<FormItem>
<Button
@click="isShowAddModal=true"
icon="md-add"
type="primary"
v-privilege="'add-position'"
>添加</Button>
</FormItem>
</Form>
<!-- Form 搜索按钮区 end -->
<Table :columns="columns" :data="data" :loading="isShowTablesLoading" ref="tablesMain"></Table>
<Page
:current="searchFrom.pageNum"
:page-size="searchFrom.pageSize"
:show-total="true"
:total="pageTotal"
@on-change="changePage"
@on-page-size-change="changePageSize"
style="margin:24px 0;text-align:right;"
></Page>
</Card>
<!-- Card 内容区 end -->
<!-- Modal 编辑岗位弹窗 start -->
<Modal
:loading="isShowUpdateLoading"
@on-cancel="cancelUpdateData"
@on-ok="validateAndUpdataPosition"
title="编辑岗位"
v-model="isShowEditModal"
>
<Form :label-width="80" :model="updateItem" :rules="updateValidate" ref="updateRef">
<FormItem label="岗位名称" prop="positionName">
<Input placeholder="请输入岗位名称(必填)" v-model="updateItem.positionName"></Input>
</FormItem>
<FormItem label="岗位描述" prop="remark">
<Input
:autosize="{minRows: 4}"
:maxlength="200"
placeholder="请输入岗位描述(必填)"
type="textarea"
v-model="updateItem.remark "
></Input>
</FormItem>
</Form>
</Modal>
<!-- Modal 编辑岗位弹窗 end -->
<!-- Modal 添加岗位弹窗 start -->
<Modal
:loading="isShowSaveLoading"
@on-cancel="cancelSaveData"
@on-ok="validateAndAddPosition"
title="添加岗位"
v-model="isShowAddModal"
>
<Form :label-width="80" :model="saveItem" :rules="saveValidate" ref="saveRef">
<FormItem label="岗位名称" prop="positionName">
<Input :maxlength="30" placeholder="请输入岗位名称(必填)" v-model="saveItem.positionName"></Input>
</FormItem>
<FormItem label="岗位描述" prop="remark">
<Input
:autosize="{minRows: 4}"
:maxlength="200"
placeholder="请输入岗位描述(必填)"
type="textarea"
v-model="saveItem.remark "
></Input>
</FormItem>
</Form>
</Modal>
<!-- Modal 添加岗位弹窗 end -->
</div>
</template>
<script>
import { positionApi } from '@/api/position';
export default {
name: 'PositionList',
components: {},
props: {},
data() {
return {
isShowPage: true,
searchValue: '',
isShowEditModal: false,
isShowAddModal: false,
// table是否Loading
isShowTablesLoading: true,
isShowSaveLoading: true,
isShowUpdateLoading: true,
pageTotal: 0,
// 更新的数据
updateItem: {
id: 0,
positionName: 'positionName',
remark: ''
},
// 添加保存的数据
saveItem: {
positionName: '',
remark: ''
},
saveItemInt: {},
// table表头
columns: [
{
title: 'id',
key: 'id',
width: 100
},
{
title: '岗位名称',
key: 'positionName',
width: 200
},
{
title: '岗位描述',
key: 'remark'
},
{
title: '操作',
key: 'action',
width: 150,
align: 'center',
className: 'action-hide',
render: (h, params) => {
return this.$tableAction(h, [
{
title: '编辑',
directives: [
{
name: 'privilege',
value: 'update-position'
}
],
action: () => {
this.updateItem = {
id: params.row.id,
positionName: params.row.positionName,
remark: params.row.remark
};
this.isShowEditModal = true;
}
},
{
title: '删除',
directives: [
{
name: 'privilege',
value: 'delete-position'
}
],
action: () => {
this.$Modal.confirm({
title: '友情提醒',
content: '确定要删除吗?',
onOk: () => {
this.deletePositionById(params.row.id);
}
});
}
}
]);
}
}
],
// table数据
data: [],
updateValidate: {
positionName: [
{ required: true, message: '请输入岗位名称', trigger: 'blur' }
],
remark: [{ required: true, message: '请输入岗位描述', trigger: 'blur' }]
},
saveValidate: {
positionName: [
{ required: true, message: '请输入岗位名称', trigger: 'blur' }
],
remark: [{ required: true, message: '请输入岗位描述', trigger: 'blur' }]
},
searchFrom: {
positionName: '',
pageNum: 1,
pageSize: 10,
searchCount: true,
sort: false
},
searchFromInt: {},
isShowdeleteLoading: false
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
Object.assign(this.searchFromInt, this.searchFrom);
Object.assign(this.saveItemInt, this.saveItem);
this.getPositionListPage();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 分页查询所有岗位
async getPositionListPage() {
try {
this.isShowTablesLoading = true;
this.isShowPage = true;
let result = await positionApi.getPositionListPage(this.searchFrom);
this.isShowTablesLoading = false;
let datas = result.data;
this.data = datas.list;
this.pageTotal = datas.total;
} catch (e) {
this.isShowTablesLoading = false;
//TODO zhuoda sentry
console.error(e);
}
},
// 页码改变
changePage(pageNum) {
this.searchFrom.pageNum = pageNum;
this.getPositionListPage();
},
// 改变每页显示数据条数
changePageSize(pageSize) {
this.searchFrom.pageNum = 1;
this.searchFrom.pageSize = pageSize;
this.getPositionListPage();
},
// 检验参数后 更新岗位
validateAndUpdataPosition() {
this.$refs['updateRef'].validate(valid => {
this.isShowUpdateLoading = true;
if (valid) {
this.updatePosition();
} else {
this.isShowUpdateLoading = false;
this.$nextTick(() => {
this.isShowUpdateLoading = true;
});
}
});
},
// 更新岗位
async updatePosition() {
try {
let result = await positionApi.updatePosition(this.updateItem);
this.$Message.success('修改成功');
await this.getPositionListPage();
this.cancelUpdateData();
} catch (e) {
this.isShowUpdateLoading = false;
this.$nextTick(() => {
this.isShowUpdateLoading = true;
});
//TODO zhuoda sentry
console.error(e);
} finally {
this.isShowUpdateLoading = false;
}
},
// 清除编辑模态框数据
cancelUpdateData() {
this.updateItem = {};
// 清空form规则检查
this.$refs['updateRef'].resetFields();
this.isShowEditModal = false;
},
// 搜索
searchData() {
this.pageNum = 1;
this.getPositionListPage();
},
// 重置
clearSearch() {
Object.assign(this.searchFrom, this.searchFromInt);
this.getPositionListPage();
},
// 添加岗位
validateAndAddPosition() {
try {
this.$refs['saveRef'].validate(valid => {
this.isShowSaveLoading = true;
if (valid) {
this.addPosition();
} else {
this.isShowSaveLoading = false;
this.$nextTick(() => {
this.isShowSaveLoading = true;
});
}
});
} catch (e) {
//TODO zhuoda sentry
console.error(e);
}
},
// 添加岗位 - 异步
async addPosition() {
try {
let result = await positionApi.addPosition(this.saveItem);
this.$Message.success('添加成功');
this.isShowSaveLoading = true;
await this.getPositionListPage();
this.cancelSaveData();
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.isShowSaveLoading = false;
}
},
// 清除添加模态框数据
cancelSaveData() {
Object.assign(this.saveItem, this.saveItemInt);
// 清空form规则检查
this.$refs['saveRef'].resetFields();
this.isShowAddModal = false;
},
// 根据ID删除岗位
async deletePositionById(id) {
try {
this.isShowdeleteLoading = true;
let result = await positionApi.deletePosition(id);
this.isShowdeleteLoading = false;
this.$Message.success('删除成功');
await this.getPositionListPage();
this.cancelSaveData();
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.isShowdeleteLoading = false;
}
}
}
};
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,527 @@
<template>
<div>
<!--Modal 添加编辑成员 start-->
<Modal
:style="{top: '20px'}"
:title="formValidate.id?'编辑成员':'添加成员'"
@on-visible-change="onChangeVisibleModal"
v-model="isShowAddOrUpdateEmployeeModal"
width="740"
>
<div>
<!--Form 添加编辑成员表单 start-->
<Form :label-width="100" :model="formValidate" :rules="ruleValidate" ref="formValidate">
<Row>
<Col span="12">
<FormItem label="登录名" prop="loginName">
<Input
:disabled="formValidate.id!=null"
:maxlength="30"
@on-keydown="formValidate.loginName=formValidate.loginName.replace(/^ +| +$/g,'')"
@on-keyup="formValidate.loginName=formValidate.loginName.replace(/^ +| +$/g,'')"
class="form-width"
placeholder="请输入登录名"
v-model="formValidate.loginName"
/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="姓名" prop="actualName">
<Input
:maxlength="30"
@on-keydown="formValidate.actualName=formValidate.actualName.replace(/^ +| +$/g,'')"
@on-keyup="formValidate.actualName=formValidate.actualName.replace(/^ +| +$/g,'')"
class="form-width"
placeholder="请输入姓名"
v-model="formValidate.actualName"
/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="别名">
<Input
:maxlength="30"
@on-keyup="formValidate.nickName=formValidate.nickName.replace(/^ +| +$/g,'')"
class="form-width"
placeholder="请输入别名"
v-model="formValidate.nickName"
/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="身份证">
<Input
@on-blur="changeIdCard(formValidate.idCard)"
@on-keyup="formValidate.idCard=formValidate.idCard.replace(/^ +| +$/g,'')"
class="form-width"
placeholder="请输入身份证信息"
v-model="formValidate.idCard"
></Input>
</FormItem>
</Col>
<Col span="12">
<FormItem label="手机" prop="phone">
<Input
@on-keyup="formValidate.phone=formValidate.phone.replace(/^ +| +$/g,'')"
class="form-width"
placeholder="请输入手机号"
v-model="formValidate.phone"
></Input>
</FormItem>
</Col>
<Col span="12">
<FormItem label="E-mail" prop="email">
<Input
class="form-width"
placeholder="请输入邮箱地址"
type="email"
v-model="formValidate.email"
></Input>
</FormItem>
</Col>
<Col span="12">
<FormItem
class="select-class"
label="部门"
prop="departmentName"
v-click-outside="clickOutside"
>
<Input
@click.native="isShowTree = !isShowTree"
class="form-width"
placeholder="请选择部门"
readonly
v-model="formValidate.departmentName"
>
<Icon slot="suffix" type="ios-arrow-down" v-if="!isShowTree" />
<Icon slot="suffix" type="ios-arrow-up" v-else />
</Input>
<div class="department-wrap" v-if="isShowTree">
<DepartmentEmployeeTree
:isDepartment="true"
@on-select="selectDepartmentOrEmployee"
ref="departmentEmployeeTree"
></DepartmentEmployeeTree>
</div>
</FormItem>
</Col>
<Col span="12">
<FormItem label="状态" required>
<RadioGroup v-model="formValidate.isDisabled">
<Radio :label="0">启用</Radio>
<Radio :label="1">禁用</Radio>
</RadioGroup>
</FormItem>
</Col>
<Col span="24">
<FormItem class="select-class" label="岗位" prop="positionIdList">
<Select class="form-width" multiple v-model="formValidate.positionIdList">
<Option
:key="item.value"
:value="item.value"
v-for="item in positionListData"
>{{ item.label }}</Option>
</Select>
</FormItem>
</Col>
<Col span="12">
<FormItem label="密码" prop="loginPwd" v-if="!formValidate.id">
<Input
@on-keydown="formValidate.loginPwd=formValidate.loginPwd.replace(/^ +| +$/g,'')"
@on-keyup="formValidate.loginPwd=formValidate.loginPwd.replace(/^ +| +$/g,'')"
class="form-width"
placeholder="请输入密码"
type="password"
v-model="formValidate.loginPwd"
/>
</FormItem>
</Col>
<Col span="12">
<FormItem label="密码确认" prop="passwordAgain" v-if="!formValidate.id">
<Input
@on-keydown="formValidate.passwordAgain=formValidate.passwordAgain.replace(/^ +| +$/g,'')"
@on-keyup="formValidate.passwordAgain=formValidate.passwordAgain.replace(/^ +| +$/g,'')"
class="form-width"
placeholder="请输入确认密码"
type="password"
v-model="formValidate.passwordAgain"
/>
</FormItem>
</Col>
</Row>
</Form>
<!--Form 添加编辑成员表单 end-->
</div>
<div slot="footer">
<Button @click="isShowAddOrUpdateEmployeeModal=false" type="text">取消</Button>
<Button :loading="isShowLoading" @click="submitFormData" class="newBtn" type="primary">提交</Button>
</div>
</Modal>
<!--Modal 添加编辑成员 end-->
</div>
</template>
<script>
import $ from 'jquery';
import { departmentApi } from '@/api/department';
import { employeeApi } from '@/api/employee';
import { roleApi } from '@/api/role';
import { positionApi } from '@/api/position';
import { dateFormat, utils } from '@/lib/util';
import DepartmentEmployeeTree from '../../../components/department-employee-tree/department-employee-tree';
export default {
name: 'EmployeeTableAdd',
components: {
DepartmentEmployeeTree
},
props: {
// 选中的部门
selectDepartment: {
type: Object,
required: true
}
},
data() {
return {
isShowAddOrUpdateEmployeeModal: false,
isShowLoading: false,
roleList: [],
positionListData: [],
isShowTree: false,
formValidateBackup: {},
formValidate: {
actualName: '',
loginName: '',
nickName: '',
departmentName: '',
departmentId: '',
isDisabled: 0,
phone: '',
idCard: '',
birthday: '',
loginPwd: '',
passwordAgain: '',
email: '',
positionIdList: []
},
ruleValidate: {
loginName: [
{ required: true, message: '登录名不能为空', trigger: 'blur' }
],
actualName: [
{ required: true, message: '姓名不能为空', trigger: 'blur' }
],
idCard: [
{ required: false, message: '身份证不能为空', trigger: 'blur' },
{
pattern: /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/,
message: '身份证格式错误',
trigger: 'blur'
}
],
email: [
{ required: false, message: '邮箱不能为空', trigger: 'blur' },
{ type: 'email', message: '请输入正确邮箱格式', trigger: 'blur' }
],
departmentName: [
{ required: true, message: '部门不能为空', trigger: 'blur' },
{ required: true, message: '部门不能为空', trigger: 'change' }
],
positionIdList: [
{
required: true,
type: 'array',
min: 1,
message: '岗位不能为空',
trigger: 'change'
}
],
birthday: [
{ required: false, message: '请选择出生日期', trigger: 'blur' }
],
phone: [
{ required: true, message: '手机号不能为空', trigger: 'blur' },
{
pattern: /^(13|14|15|16|17|18)\d{9}$/,
message: '手机号格式错误',
trigger: 'blur'
}
],
loginPwd: [
{ required: true, message: '密码不能为空', trigger: 'blur' }
],
passwordAgain: [
{ required: true, message: '确认密码不能为空', trigger: 'blur' }
]
},
birthDate: {
shortcuts: [
{
text: '今天',
value() {
return new Date();
},
onClick: picker => {
this.$Message.info('你选择的是今天');
}
},
{
text: '昨天',
value() {
const date = new Date();
date.setTime(date.getTime() - 3600 * 1000 * 24);
return date;
},
onClick: picker => {
this.$Message.info('你选择的是昨天');
}
},
{
text: '一周前',
value() {
const date = new Date();
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
return date;
},
onClick: picker => {
this.$Message.info('你选择的是一周前');
}
}
]
}
};
},
computed: {},
watch: {
isShowAddOrUpdateEmployeeModal(val) {
if (val) {
this.getPositionListPage();
}
}
},
filters: {},
created() {},
mounted() {
let dateStr = utils.getDateStr(0, dateFormat.YMDHM);
this.formValidate.createDate = dateStr;
Object.assign(this.formValidateBackup, this.formValidate);
this.getAllRole();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 选择部门点击外部关闭
clickOutside() {
if (this.isShowTree) {
this.isShowTree = false;
}
},
// 选择部门或者成员
selectDepartmentOrEmployee(department) {
this.$set(this.formValidate, 'departmentId', department.id);
this.$set(this.formValidate, 'departmentName', department.name);
this.isShowTree = false;
$('.department-wrap').hide();
},
// 弹窗显示隐藏监听
onChangeVisibleModal(value) {
if (!value) {
this.formValidate = Object.assign({}, this.formValidateBackup);
}
},
// 打开Modal
showModal(row) {
if (typeof row === 'undefined') {
this.ruleValidate.loginPwd = [
{
required: true,
message: '密码长度至少为6位不允许输入空格',
trigger: 'blur',
min: 6
},
{ pattern: /^[0-9]{6,10}$/, message: '密码格式错误', trigger: 'blur' }
];
this.ruleValidate.passwordAgain = [
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
{
validator: this.loginPwd,
trigger: 'blur'
},
{
pattern: /^[0-9]{6,10}$/,
message: '确认密码格式错误',
trigger: 'blur'
}
];
this.ruleValidate.loginName = [
{ required: true, message: '登录名不能为空', trigger: 'blur' },
{
pattern: /^[A-Za-z]+[A-Za-z0-9]{5,17}$/,
message:
'请输入6位或6位字符以上(英文+数字),第一个字符必须为英文字母',
trigger: 'blur'
}
];
} else {
delete this.ruleValidate.loginPwd;
delete this.ruleValidate.passwordAgain;
this.ruleValidate.loginName = [
{ required: true, message: '登录名不能为空', trigger: 'blur' }
];
}
this.$refs['formValidate'].resetFields();
if (row && Object.keys(row).length > 0) {
let positionIdList = [];
row.positionRelationList = row.positionRelationList || [];
row.positionRelationList.map(item => {
positionIdList.push(item.positionId);
});
row.positionIdList = positionIdList;
this.formValidate = Object.assign({}, row);
} else {
if (Object.keys(this.selectDepartment).length > 0) {
if (this.selectDepartment.type === 1) {
this.formValidate.departmentId = null;
this.formValidate.departmentName = null;
this.formValidate.organizationId = this.selectDepartment.id;
this.formValidate.organizationName = this.selectDepartment.name;
} else {
this.formValidate.departmentId = this.selectDepartment.id;
this.formValidate.departmentName = this.selectDepartment.name;
this.formValidate.organizationId = this.selectDepartment.organizationId;
this.formValidate.organizationName = this.selectDepartment.organizationName;
}
}
}
this.isShowAddOrUpdateEmployeeModal = true;
},
// 点击提交
submitFormData() {
this.$refs['formValidate'].validate(valid => {
if (
!/^[A-Za-z]+[A-Za-z0-9]{5,17}$/.test(this.formValidate.loginName) &&
!this.formValidate.id
) {
this.$Message.error('登录名格式不正确!');
return;
}
if (this.formValidate.idCard) {
if (
!/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(
this.formValidate.idCard
)
) {
this.$Message.error('身份证号码格式不正确!');
return;
}
}
const reg = new RegExp(
"[`~!@#$^&*()=|{}':;',\\[\\].<>/?~@#¥……&*()——|{}【】‘;:”“'。,、?]"
);
if (reg.test(this.formValidate.actualName)) {
this.$Message.error('姓名中不能含有特殊字符!');
return false;
}
if (this.formValidate.passwordAgain !== this.formValidate.loginPwd) {
this.$Message.error('两次输入密码不一致,请重新输入!!');
return;
}
if (valid) {
if (this.formValidate.id) {
this.updateEmployee(this.formValidate);
} else {
this.addEmployee(this.formValidate);
}
}
});
},
// 修改成员
async updateEmployee(data) {
try {
this.isShowLoading = true;
let result = await employeeApi.updateEmployee(data);
this.isShowAddOrUpdateEmployeeModal = false;
this.$Message.success('修改成员成功');
this.$emit('addSuccess');
this.$refs['formValidate'].resetFields();
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.isShowLoading = false;
}
},
// 添加新用户接口
async addEmployee(param) {
try {
this.isShowLoading = true;
let result = await employeeApi.addEmployee(param);
this.isShowAddOrUpdateEmployeeModal = false;
this.$Message.success('添加成员成功');
this.$emit('addSuccess');
this.$refs['formValidate'].resetFields();
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.isShowLoading = false;
}
},
// 加载角色列表
async getAllRole() {
let result = await roleApi.getAllRole();
this.roleList = result.data;
},
// 分页查询所有岗位
async getPositionListPage() {
let result = await positionApi.getPositionListPage({
pageNum: 1,
pageSize: 200,
sort: false
});
let datas = result.data;
let list = [];
datas.list.map(item => {
list.push({
value: item.id,
label: item.positionName
});
});
this.positionListData = list;
},
// 身份证输入失焦校验
changeIdCard(value) {
let reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
if (reg.test(value)) {
let data = value.slice(6, 14);
let y = data.slice(0, 4);
let m = data.slice(4, 6);
let d = data.slice(6, 8);
this.formValidate.birthday = y + '-' + m + '-' + d;
}
}
}
};
</script>
<style lang="less" scoped>
.form-width {
width: 100%;
}
.department-wrap {
position: absolute;
background-color: #ffffff;
padding: 5px;
border: 1px solid #dedede;
width: 300px;
z-index: 9;
height: 250px;
overflow-y: scroll;
}
.select-class {
position: relative;
}
</style>

View File

@@ -0,0 +1,72 @@
<template>
<Modal :footer-hide="true" :width="700" title="职员详情" v-model="isShowDetailModal">
<Form :label-width="80">
<Row :gutter="16">
<Col span="8">
<FormItem label="姓名:">{{detail.actualName}}</FormItem>
</Col>
<Col span="8">
<FormItem label="登录名:">{{detail.loginName}}</FormItem>
</Col>
<Col span="8">
<FormItem label="别名:">{{detail.nickName}}</FormItem>
</Col>
</Row>
<Row :gutter="16">
<Col span="8">
<FormItem label="部门:">{{detail.departmentName}}</FormItem>
</Col>
<Col span="8">
<FormItem label="手机:">{{detail.phone}}</FormItem>
</Col>
<Col span="8">
<FormItem label="身份证:">{{detail.idCard}}</FormItem>
</Col>
</Row>
<Row :gutter="16">
<Col span="8">
<FormItem label="出生日期:">{{detail.birthday}}</FormItem>
</Col>
<Col span="8">
<FormItem label="电子邮箱:">{{detail.email}}</FormItem>
</Col>
<Col span="8">
<FormItem label="状态:">{{detail.isDisabled?"禁用":"启用"}}</FormItem>
</Col>
</Row>
</Form>
</Modal>
</template>
<script>
export default {
name: 'EmployeeTableDetail',
components: {},
props: {},
data() {
return {
isShowDetailModal: false,
detail: {}
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
showModal(detail) {
this.isShowDetailModal = true;
this.detail = detail;
}
}
};
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,569 @@
<template>
<div>
<Col :lg="19" :md="16">
<Card class="warp-card" dis-hover>
<!--Row 顶部操作区 start-->
<Row justify="space-between" type="flex">
<Col>
<Input
@on-change="searchEmployees"
icon="md-search"
placeholder="请输入姓名搜搜..."
style="width: 200px"
v-model="searchEmployeeName"
/>
<Button
@click="showAddEmployeeModal"
icon="md-add"
style="margin-left: 5px;"
type="primary"
v-privilege="'add-employee'"
>添加成员</Button>
</Col>
<Col>
<Button
@click="queryEmployeeListByIsDisabled(1)"
type="default"
v-privilege="'search-department'"
v-show="!currentDisabled"
>查看已禁用用户</Button>
<Button
@click="queryEmployeeListByIsDisabled(0)"
type="default"
v-privilege="'search-department'"
v-show="currentDisabled"
>查看已启用用户</Button>
<Button
@click="GroupDisable(1)"
style="margin-left:5px"
type="error"
v-privilege="'disabled-employee-batch'"
v-show="!currentDisabled"
>批量禁用</Button>
<Button
@click="GroupDisable(0)"
style="margin-left:5px"
type="success"
v-privilege="'disabled-employee-batch'"
v-show="currentDisabled"
>批量启用</Button>
</Col>
</Row>
<!--Row 顶部操作区 end-->
<!--Row 表格区 start-->
<Row style="padding-top:10px; ">
<Table
:columns="columns"
:data="employeeTable"
:loading="isShowTablesLoading"
@on-selection-change="singleSelect"
border
></Table>
</Row>
<!--Row 表格区 end-->
<!--Row 底部操作区 start-->
<Row class="page" justify="end" style="position: relative;margin-top: 10px;" type="flex">
<Page
:current="pageNum"
:page-size="pageSize"
:total="totalPage"
@on-change="changePage"
show-elevator
></Page>
</Row>
<!--Row 底部操作区 end-->
</Card>
</Col>
<!--EmployeeTableAdd 添加成员弹窗 start-->
<EmployeeTableAdd
:selectDepartment="selectDepartment"
@addSuccess="getEmployeeList"
ref="employeeTableAdd"
></EmployeeTableAdd>
<!--EmployeeTableAdd 添加成员弹窗 end-->
<!--Modal 角色管理弹窗 start-->
<Modal
@on-ok="confirmAddRole"
class-name="vertical-center-modal"
title="角色管理"
v-model="isShowManageRoleModal"
>
<CheckboxGroup v-model="employeeRole">
<Checkbox
:key="roleItem.id"
:label="roleItem.id"
style="width:104px;line-height:40px;"
v-for="roleItem in roleList"
>{{roleItem.roleName}}</Checkbox>
</CheckboxGroup>
</Modal>
<!--Modal 角色管理弹窗 end-->
<!--EmployeeTableDetail 角色详情 start-->
<EmployeeTableDetail ref="employeeTableDetail"></EmployeeTableDetail>
<!--EmployeeTableDetail 角色详情 end-->
</div>
</template>
<script>
import { departmentApi } from '@/api/department';
import { employeeApi } from '@/api/employee';
import { roleApi } from '@/api/role';
import { utils } from '@/lib/util';
import EmployeeTableAdd from '../employee-table-add/employee-table-add.vue';
import EmployeeTableDetail from '../employee-table-detail/employee-table-detail.vue';
export default {
name: 'EmployeeTable',
components: {
EmployeeTableAdd,
EmployeeTableDetail
},
props: {
// 选中的部门
selectDepartment: {
type: Object,
required: true
},
// 表格样式 按钮功能
departments: {
type: Array,
required: true
}
},
data() {
return {
employeeRole: [],
// 员工id
mid: '',
totalPage: 0,
pageSize: 10,
pageNum: 1,
isShowManageRoleModal: false,
isShowTablesLoading: false,
// 搜索框内容
searchEmployeeName: '',
columns: [
{
type: 'selection',
width: 60,
align: 'center'
},
{
title: 'ID',
width: 60,
key: 'id'
},
{
key: 'actualName',
width: 200,
title: '名称',
align: 'left'
},
{
key: 'loginName',
title: '登录名',
width: 200,
align: 'left'
},
{
key: 'isDisabled',
width: 80,
title: '状态',
align: 'left',
render: (h, params) => {
let disabled = params.row.isDisabled;
return h('span', disabled ? '禁用' : '启用');
}
},
{
key: 'phone',
width: 120,
title: '手机',
align: 'left'
},
{
key: 'departmentName',
width: 180,
title: '所属部门',
align: 'left'
},
{
key: 'positionName',
width: 180,
title: '岗位',
align: 'left',
ellipsis: true,
tooltip: true
},
{
key: 'email',
width: 180,
title: '电子邮箱',
align: 'left'
},
{
key: 'createTime',
width: 180,
title: '创建时间',
align: 'left'
},
{
title: '操作',
key: 'action',
width: 160,
fixed: 'right',
align: 'center',
className: 'action-hide',
render: (h, params) => {
let btnGroup = [];
let isSuper = params.row.super;
if (!isSuper) {
btnGroup.push({
title: '角色设置',
directives: [
{
name: 'privilege',
value: 'update-employee-role'
}
],
action: () => {
this.showManageRoleModal(h, params);
}
});
}
btnGroup.push({
title: '详情',
action: () => {
this.$refs['employeeTableDetail'].showModal(params.row);
}
});
if (params.row.isDisabled == 0 && !isSuper) {
btnGroup.push({
title: '编辑',
directives: [
{
name: 'privilege',
value: 'update-employee'
}
],
action: () => {
this.updataEmployee(params.row);
}
});
}
if (!isSuper) {
let isDisableBtn;
if (params.row.isDisabled == 0) {
isDisableBtn = {
title: '禁用',
directives: [
{
name: 'privilege',
value: 'disabled-employee'
}
],
action: () => {
this.updateEmployeeStatus(h, params);
}
};
} else {
isDisableBtn = {
title: '启用',
directives: [
{
name: 'privilege',
value: 'disabled-employee'
}
],
action: () => {
this.updateEmployeeStatus(h, params);
}
};
}
btnGroup.push(isDisableBtn);
}
if (!isSuper) {
btnGroup.push({
title: '密码重置',
directives: [
{
name: 'privilege',
value: 'reset-employee-password'
}
],
action: () => {
this.$Modal.confirm({
title: '重置密码',
content: '是否将密码重置为123456',
onOk: () => {
this.resetPassword(params.row.id);
}
});
}
});
}
if (params.row.isDisabled == 1 && !isSuper) {
btnGroup.push({
title: '删除',
directives: [
{
name: 'privilege',
value: 'delete-employee'
}
],
action: () => {
this.$Modal.confirm({
title: '删除',
content: '确认删除?',
onOk: () => {
this.deleteEmployee(params.row.id);
}
});
}
});
}
// return h('div', btnGroup)
return this.$tableAction(h, btnGroup);
}
}
],
// 表格数据
employeeTable: [],
disableId: [],
currentDisabled: 0,
roleList: []
};
},
computed: {},
watch: {
selectDepartment(newData) {
this.selectDepartment = newData;
this.pageNum = 1;
this.getEmployeeList();
}
},
filters: {},
created() {},
mounted() {
this.getEmployeeList();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 翻页
changePage(pageNum) {
this.pageNum = pageNum;
this.getEmployeeList();
},
// 根据部门ID查询部门员工
async getEmployeeList() {
let param = {
isDisabled: this.currentDisabled,
pageSize: this.pageSize,
pageNum: this.pageNum
};
param.departmentId = this.selectDepartment.id;
try {
this.isShowTablesLoading = true;
param.keyword = this.searchEmployeeName;
let result = await employeeApi.getEmployeeList(param);
this.totalPage = result.data.total;
this.employeeTable = result.data.list;
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.isShowTablesLoading = false;
}
},
// 打开子组件的模态框
showAddEmployeeModal() {
this.$refs.employeeTableAdd.showModal();
},
// 根据是否启用查询成员
queryEmployeeListByIsDisabled(type) {
this.currentDisabled = type;
this.pageNum = 1;
this.getEmployeeList();
},
// 确定添加角色
async confirmAddRole() {
let roleList = this.employeeRole;
try {
this.isShowTablesLoading = true;
let result = await employeeApi.updateRoles(
Object.assign({
employeeId: this.mid,
roleIds: roleList
})
);
this.isShowTablesLoading = false;
this.$Message.success('授权成功');
this.getEmployeeList();
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.isShowTablesLoading = false;
}
},
// 确认添加角色的后台数据接口
async getRoles(id) {
try {
this.isShowTablesLoading = true;
let result = await roleApi.getRoles(id);
this.isShowTablesLoading = false;
this.roleList = result.data;
this.employeeRole = [];
this.roleList.forEach(item => {
if (item.selected) {
this.employeeRole.push(item.id);
}
});
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.isShowTablesLoading = false;
}
},
// 点击打开员工角色选择框
showManageRoleModal(h, param) {
this.mid = param.row.id;
this.isShowManageRoleModal = true;
this.getRoles(param.row.id);
},
// 修改员工信息
updataEmployee(detail) {
if (this.departments[0].id == detail.departmentId) {
detail.departmentName = this.departments[0].name;
} else {
this.departments[0].children.forEach(item => {
if (item.id == detail.departmentId) {
detail.departmentName = item.name;
}
});
}
if (detail.birthday) {
detail.birthday = utils.getDate(new Date(detail.birthday), 'YMD');
}
this.$refs.employeeTableAdd.showModal(detail);
},
// 点击搜索
searchEmployees() {
this.pageNum = 1;
this.queryEmployeeListByIsDisabled(0);
},
// 表格左侧复选框,点击保存选中栏信息
singleSelect(row) {
this.disableId = row;
},
// 批量禁用
GroupDisable(type) {
if (this.disableId.length == 0) {
this.$Message.error('请最少选择一项');
return false;
} else {
this.currentDisabled = type;
let disableIds = this.disableId.map(e => e.id);
this.updateStatusBatch(
Object.assign({ status: type, employeeIds: disableIds })
);
}
},
// 禁用/启用单个用户
async updateEmployeeStatus(h, params) {
try {
this.isShowTablesLoading = true;
let employeeId = params.row.id;
let status = 0;
if (params.row.isDisabled == 0) {
status = 1;
this.queryEmployeeListByIsDisabled(0);
} else {
status = 0;
this.queryEmployeeListByIsDisabled(1);
}
let result = await employeeApi.updateStatus(employeeId, status);
this.isShowTablesLoading = false;
if (status) {
this.$Message.success('禁用成功');
this.getEmployeeList();
} else {
this.$Message.success('启用成功');
this.queryEmployeeListByIsDisabled(1);
}
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.isShowTablesLoading = false;
}
},
// 批量禁用多个用户
async updateStatusBatch(param) {
try {
this.isShowTablesLoading = true;
let result = await employeeApi.updateStatusBatch(param);
this.isShowTablesLoading = false;
this.$Message.success('操作成功');
this.disableId = [];
this.queryEmployeeListByIsDisabled(this.currentDisabled);
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.isShowTablesLoading = false;
}
},
// 重置密码
async resetPassword(id) {
try {
this.isShowTablesLoading = true;
let result = await employeeApi.resetPassword(id);
this.isShowTablesLoading = false;
this.$Message.success('操作成功');
this.queryEmployeeListByIsDisabled(this.currentDisabled);
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.isShowTablesLoading = false;
}
},
// 删除成员
async deleteEmployee(id) {
try {
this.isShowTablesLoading = true;
let result = await employeeApi.deleteEmployee(id);
this.isShowTablesLoading = false;
this.$Message.success('操作成功');
this.queryEmployeeListByIsDisabled(this.currentDisabled);
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.isShowTablesLoading = false;
}
}
}
};
</script>
<style lang="less" scoped>
.ivu-tree-children {
cursor: pointer;
}
.option-department {
font-size: 14px;
padding: 5px;
cursor: pointer;
}
.searchable-table-con1 {
min-height: 350px !important;
}
.option-department:hover {
background-color: rgba(5, 170, 250, 0.2);
}
</style>

View File

@@ -0,0 +1,831 @@
<template>
<div>
<!--Row 员工管理 start-->
<Row :gutter="10">
<!--Col 左侧公司组织列表 start-->
<Col :lg="5" :md="8">
<Card class="warp-card" dis-hover>
<p slot="title">公司组织</p>
<Row>
<Input
@on-change="getListEmployeeByDepartmentName"
placeholder="输入部门名称"
v-model="departmentName"
/>
</Row>
<Tree
:data="departmentGroup"
:render="renderDepartmentTreeButton"
style="height: 485px;overflow-x: scroll"
></Tree>
</Card>
</Col>
<!--Col 左侧公司组织列表 end-->
<!--EmployeeTable 表单列表模块 start-->
<EmployeeTable :departments="departmentGroup" :selectDepartment="selectDepartment"></EmployeeTable>
<!--EmployeeTable 表单列表模块 end-->
<!--Modal 添加部门弹窗 start-->
<Modal
@on-visible-change="changeVisibleOperationModal"
title="添加部门"
v-model="isShowOperationModal"
>
<Form :label-width="80">
<FormItem label="名称" required>
<Input
@on-keydown="departmentParam.name=departmentParam.name.replace(/^ +| +$/g,'')"
@on-keyup="departmentParam.name=departmentParam.name.replace(/^ +| +$/g,'')"
v-model="departmentParam.name"
></Input>
</FormItem>
<FormItem class="selectClass" label="负责人">
<Input
@click.native="showTree"
placeholder="请选择负责人"
readonly
v-model="departmentParam.managerName"
>
<Icon slot="suffix" type="ios-arrow-down" v-if="!isShowTree" />
<Icon slot="suffix" type="ios-arrow-up" v-else />
</Input>
<div class="departmentWrap">
<DepartmentEmployeeTree
:selectEmployeeId="selectEmployeeId"
@on-select="selectDepartmentEmployee"
ref="departmentEmployeeTree"
></DepartmentEmployeeTree>
</div>
</FormItem>
<FormItem label="上级部门" required>
<Input disabled v-model="departmentParam.parentName" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="getOperationDepartmentData()" size="large" type="primary">确定</Button>
<Button @click="hideDeleteAndOperationModal" size="large" type="error">取消</Button>
</div>
</Modal>
<!--Modal 添加部门弹窗 end-->
<!--Modal 删除部门弹窗 start-->
<Modal title="删除确认" v-model="isShowDeleteModal">
<p style="font-size: 16px;">
确定要将
<span style="color: red;font-size: 16px;">{{departmentParam.name}}</span>
部门删除吗
</p>
<div slot="footer">
<Button @click="deleteDepartment" size="large" type="primary">确定</Button>
<Button @click="hideDeleteAndOperationModal" size="large" type="error">取消</Button>
</div>
</Modal>
<!--Modal 删除部门弹窗 end-->
<!--Modal 公司设置弹窗 start-->
<Modal title="公司设置" v-model="isShowCompanySettingModal">
<Form :label-width="80">
<FormItem label="公司名称" required>
<Input
:disabled=" departmentParam.id!=null"
@on-keydown="departmentParam.name=departmentParam.name.replace(/^ +| +$/g,'')"
@on-keyup="departmentParam.name=departmentParam.name.replace(/^ +| +$/g,'')"
v-model="departmentParam.name"
/>
</FormItem>
</Form>
<div slot="footer">
<Button
@click="getOperationDepartmentData()"
size="large"
type="primary"
v-if=" !departmentParam.id"
>确定</Button>
<Button @click="hideCompanySettingModal" size="large" type="error">取消</Button>
</div>
</Modal>
<!--Modal 公司设置弹窗 end-->
</Row>
<!--Row 员工管理 end-->
</div>
</template>
<script>
import $ from 'jquery';
import { departmentApi } from '@/api/department';
import EmployeeTable from './components/employee-table/employee-table';
import DepartmentEmployeeTree from '../components/department-employee-tree/department-employee-tree';
export default {
name: 'RoleEmployeeManage',
components: {
EmployeeTable,
DepartmentEmployeeTree
},
props: {},
data() {
return {
departmentName: '',
isShowTree: false,
// 选中的部门
selectDepartment: {},
isShowOperationModal: false,
isShowDeleteModal: false,
// 部门参数
departmentParam: {
id: '',
name: '',
managerId: null,
managerName: '',
parentName: '',
parentId: '',
type: 1
},
selectEmployeeId: null,
// 备用的部门参数
departmentParamBackup: {},
isShowCompanySettingModal: false,
// 员工table 表格样式功能
departmentGroup: [
{
name: '',
expand: true,
render: (h, { root, node, data }) => {
let newName = data.name;
if (newName.length > 8) {
newName = data.name.substring(0, 8) + '...';
}
if (!data || Object.keys(data).length === 0 || !data.id) {
return h('span', '');
}
return h(
'Tooltip',
{
props: {
placement: 'right'
},
style: { fontSize: '12px' }
},
[
h(
'span',
{
style: {
display: 'inline-block'
}
},
[
h('span', [
h(
'div',
{
props: {
// content:'123',
// placement: 'top'
},
style: { fontSize: '12px' }
},
[
h('Icon', {
props: {
type: 'md-cube'
},
style: {
marginRight: '8px'
}
}),
h(
'Button',
{
props: Object.assign({}),
class: ['departmentSelect'],
style: {
border: 'none',
background: '#ffffff',
padding: '4px 5px'
},
on: {
click: event => {
this.loadEmployeeTable(
event,
root,
node,
data
);
}
}
},
newName
)
]
)
])
]
),
h(
'div',
{
slot: 'content'
},
[
h(
'a',
{
props: {},
style: {
color: '#fff',
margin: '0 5px 5px'
},
directives: [
{
name: 'privilege',
value: 'addDepartment'
}
],
on: {
click: () => {
this.addOrUpdataDepartment(data, false);
}
}
},
'添加'
)
]
),
h(
'div',
{
slot: 'content'
},
[
h(
'a',
{
props: {},
style: {
color: '#fff',
margin: '0 5px 5px'
},
directives: [
{
name: 'privilege',
value: 'update-department'
}
],
on: {
click: () => {
data.parentId = 0;
this.addOrUpdataDepartment(data, true);
}
}
},
'编辑'
)
]
)
]
);
},
children: []
}
]
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
Object.assign(this.departmentParamBackup, this.departmentParam);
this.getListEmployeeByDepartmentName();
let self = this;
$('.selectClass').click(event => {
event = event || window.event;
event.stopPropagation();
});
// 点击层外,隐藏这个层。由于层内的事件停止了冒泡,所以不会触发这个事件
$(document).click(function(e) {
self.isShowTree = false;
$('.departmentWrap').hide();
});
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 初始化加载数据
async getListEmployeeByDepartmentName() {
this.$Spin.show();
let result = await departmentApi.getListEmployeeByDepartmentName(
this.departmentName
);
this.$Spin.hide();
let data = result.data;
this.departmentGroup[0].id = null;
this.departmentGroup[0].name = '';
this.departmentGroup[0].children = [];
this.departmentGroup[0].organizationId = [];
this.departmentGroup[0].organizationName = [];
this.departmentGroup[0].type = 1;
if (data && data[0]) {
let dateFirst = data[0];
this.departmentGroup[0].id = dateFirst.id;
this.departmentGroup[0].name = dateFirst.name;
this.departmentGroup[0].children = dateFirst.children;
this.departmentGroup[0].organizationId = dateFirst.organizationId;
this.departmentGroup[0].organizationName = dateFirst.organizationName;
this.departmentGroup[0].type = dateFirst.type;
}
},
// 渲染部门树形图功能按钮
renderDepartmentTreeButton(h, { root, node, data }) {
let newName = data.name;
if (newName.length > 8) {
newName = data.name.substring(0, 8) + '...';
}
let icon = '';
if (data.type === 1) {
icon = 'md-cube';
} else {
icon = 'md-menu';
}
let buttonList = [
h(
'div',
{
style: {
margin: '0 5px 5px 5px'
}
},
[
h(
'a',
{
props: {},
style: {
color: '#fff',
margin: '0 5px 5px'
},
directives: [
{
name: 'privilege',
value: 'update-department'
}
],
on: {
click: () => {
this.addOrUpdataDepartment(data, true);
}
}
},
'编辑'
)
]
),
h(
'div',
{
style: {
margin: '0 5px 5px 5px'
}
},
[
h(
'a',
{
props: {},
style: {
color: '#fff',
margin: '0 5px 5px'
},
directives: [
{
name: 'privilege',
value: 'add-department'
}
],
on: {
click: () => {
this.addOrUpdataDepartment(data, false);
}
}
},
'添加'
)
]
),
h(
'div',
{
style: {
margin: '0 5px 5px 5px'
}
},
[
h(
'a',
{
props: {},
style: {
color: '#fff',
margin: '0 5px 5px'
},
directives: [
{
name: 'privilege',
value: 'delete-department'
}
],
on: {
click: () => {
this.showDeleteModal(data);
}
}
},
'删除'
)
]
)
];
if (data.preId) {
buttonList.push(
h(
'div',
{
style: {
margin: '0 5px 5px 5px'
}
},
[
h(
'a',
{
props: {},
style: {
color: '#fff',
margin: '0 5px 5px'
},
directives: [
{
name: 'privilege',
value: 'delete-department'
}
],
on: {
click: () => {
this.upOrDownDepartment(data.id, data.preId);
}
}
},
'上移'
)
]
)
);
}
if (data.nextId) {
buttonList.push(
h(
'div',
{
style: {
margin: '0 5px 5px 5px'
}
},
[
h(
'a',
{
props: {},
style: {
color: '#fff',
margin: '0 5px 5px'
},
directives: [
{
name: 'privilege',
value: 'delete-department'
}
],
on: {
click: () => {
this.upOrDownDepartment(data.id, data.nextId);
}
}
},
'下移'
)
]
)
);
}
if (data.parentId && data.parentId !== 1) {
buttonList.push(
h(
'div',
{
style: {
margin: '0 5px 5px 5px'
}
},
[
h(
'a',
{
props: {},
style: {
color: '#fff',
margin: '0 5px 5px'
},
directives: [
{
name: 'privilege',
value: 'delete-department'
}
],
on: {
click: () => {
this.upDepartmentGrade(data.id);
}
}
},
'升级'
)
]
)
);
}
if (data.preId) {
buttonList.push(
h(
'div',
{
style: {
margin: '0 5px 5px 5px'
}
},
[
h(
'a',
{
props: {},
style: {
color: '#fff',
margin: '0 5px 5px'
},
directives: [
{
name: 'privilege',
value: 'delete-department'
}
],
on: {
click: () => {
this.downDepartmentGrade(data.id, data.preId);
}
}
},
'降级'
)
]
)
);
}
return h(
'Tooltip',
{
props: {
placement: 'right'
},
style: { fontSize: '12px' }
},
[
h(
'span',
{
style: {
display: 'inline-block'
}
},
[
h('span', [
h(
'div',
{
props: {
// content:'123',
// placement: 'top'
},
style: { fontSize: '12px' }
},
[
h('Icon', {
props: {
type: icon
},
style: {
marginRight: '8px'
}
}),
h(
'Button',
{
props: Object.assign({}),
class: ['departmentSelect'],
style: {
border: 'none',
background: '#ffffff',
padding: '4px 5px'
},
on: {
click: event => {
console.log('11');
this.loadEmployeeTable(event, root, node, data);
}
}
},
newName
)
]
)
])
]
),
h(
'div',
{
slot: 'content'
},
buttonList
)
]
);
},
// 选中部门 更新员工table
loadEmployeeTable(event, root, node, data) {
$('.departmentSelect').css({ background: '#ffffff', color: 'black' });
let target = event.target;
let tagName = target.tagName;
if (tagName !== 'BUTTON') {
target.parentNode.style.backgroundColor = '#5cadff';
target.parentNode.style.color = '#ffffff';
} else {
target.style.backgroundColor = '#5cadff';
target.style.color = '#ffffff';
}
this.selectDepartment = data;
},
// 关闭模态框
hideDeleteAndOperationModal() {
this.isShowDeleteModal = false;
this.isShowOperationModal = false;
},
// 隐藏公司设置弹窗
hideCompanySettingModal() {
this.isShowCompanySettingModal = false;
},
// 获取部门数据
async getOperationDepartmentData() {
if (!this.departmentParam.name) {
this.$Message.error('名称不能为空');
return;
}
this.$Spin.show();
let result;
if (this.departmentParam.id) {
result = await departmentApi.updateDepartment(this.departmentParam);
} else {
result = await departmentApi.addDepartment(this.departmentParam);
}
this.$Spin.hide();
this.$Message.success('操作成功');
this.getListEmployeeByDepartmentName();
this.hideDeleteAndOperationModal();
this.hideCompanySettingModal();
},
// 删除部门
async deleteDepartment() {
this.$Spin.show();
let result = await departmentApi.deleteDepartment(
this.departmentParam.id
);
this.$Message.success('删除成功');
this.$Spin.hide();
this.getListEmployeeByDepartmentName();
this.hideDeleteModal();
},
// 添加或编辑部门 并显示弹窗
addOrUpdataDepartment(data, update) {
this.selectEmployeeId = null;
if (update) {
this.departmentParam = Object.assign({}, data);
this.selectEmployeeId = data.managerId;
} else {
this.departmentParam = {
parentName: data.name,
parentId: data.id,
type: 1
};
}
this.isShowOperationModal = true;
},
// 显示删除弹窗
showDeleteModal(val) {
let data;
if (!val) {
data = Object.assign({}, this.departmentGroup[0]);
} else {
data = Object.assign({}, val);
}
this.departmentParam = {
id: data.id,
name: data.name
};
this.isShowDeleteModal = true;
},
// 关闭删除弹窗
hideDeleteModal() {
this.departmentParam = Object.assign({}, this.departmentParamBackup);
this.isShowDeleteModal = false;
},
// 部门树形图的显示与隐藏
showTree() {
if (this.isShowTree) {
this.isShowTree = false;
$('.departmentWrap').hide();
} else {
this.isShowTree = true;
$('.departmentWrap').show();
this.$refs.departmentEmployeeTree.resetSearch();
}
},
// 选中树形一项
selectDepartmentEmployee(emp) {
this.$set(this.departmentParam, 'managerId', emp.id);
this.$set(this.departmentParam, 'managerName', emp.name);
this.isShowTree = false;
$('.departmentWrap').hide();
},
// 添加部门弹窗 显示隐藏监听
changeVisibleOperationModal(showStatus) {
if (!showStatus) {
this.isShowTree = false;
$('.departmentWrap').hide();
this.$refs.departmentEmployeeTree.generateTreeData();
}
},
// 部门上移或者下移
async upOrDownDepartment(departmentId, swapId) {
this.$Spin.show();
let result = await departmentApi.upOrDown(departmentId, swapId);
this.$Spin.hide();
this.$Message.success('操作成功');
this.getListEmployeeByDepartmentName();
},
// 升级
async upDepartmentGrade(departmentId) {
this.$Spin.show();
let result = await departmentApi.upGrade(departmentId);
this.$Message.success('升级成功');
this.$Spin.hide();
this.getListEmployeeByDepartmentName();
},
// 降级
async downDepartmentGrade(departmentId, preId) {
this.$Spin.show();
let result = await departmentApi.downGrade(departmentId, preId);
this.$Message.success('降级成功');
this.$Spin.hide();
this.getListEmployeeByDepartmentName();
}
}
};
</script>
<style lang="less" scoped>
.ivu-tree-children {
cursor: pointer;
width: 100%;
}
.option-department {
font-size: 14px;
padding: 5px;
cursor: pointer;
}
.option-department:hover {
background-color: rgba(5, 170, 250, 0.2);
}
.departmentWrap {
position: absolute;
background-color: #ffffff;
padding: 5px;
border: 1px solid #dedede;
width: 100%;
z-index: 9;
display: none;
height: 250px;
overflow-y: scroll;
}
</style>

View File

@@ -0,0 +1,144 @@
<template>
<!--div tab切换数据范围部分 start-->
<div>
<Row>
<Col span="16" style="margin:20px 0;font-size: 15px;color: #95a5a6;"></Col>
<Col class="button-style" span="8">
<Button
@click.native="updateDataScope"
style="margin-right: 20px;"
type="primary"
v-privilege="'update-data-scope'"
>保存</Button>
<Button @click.native="getDataScope()" type="warning" v-privilege="'query-data-scope'">刷新</Button>
</Col>
</Row>
<Row style="border-bottom: 1px solid #f2f2f2;font-weight: 600; margin: 10px 0px;">
<Col class="tab-margin" span="4">业务单据</Col>
<Col class="tab-data" span="6">查看数据范围</Col>
<Col class="tab-margin" span="14"></Col>
</Row>
<div class="no-scrollbar" style="height:680px;overflow-y: scroll">
<Row
:key="item.dataScopeType "
style="border-bottom: 1px solid #f2f2f2; margin: 10px 0px;"
v-for="item in this.dataScopeList"
>
<Col span="4" style="line-height:100px; text-align: center">{{item.dataScopeTypeName}}</Col>
<!--Col 数据范围选中 start-->
<Col class="tab-data" span="6">
<RadioGroup v-model="item.viewType" vertical>
<Radio
:key="index+'viewType'"
:label="scope.viewType"
v-for="(scope,index) in item.viewTypeList"
>{{scope.viewTypeName }}</Radio>
</RadioGroup>
</Col>
<!--Col 数据范围选中 end-->
<Col span="14" style="text-indent:2rem;line-height:30px;font-size:16px;color: #a3a3a3;">
<p style="padding: 30px 0;">{{item.dataScopeTypeDesc}}</p>
</Col>
</Row>
</div>
</div>
<!--div tab切换数据范围部分 end-->
</template>
<script>
import { dataScopeApi } from '@/api/data-scope';
export default {
name: 'RoleDataScope',
components: {},
props: {
// 角色id
roleId: {
type: Number,
required: true,
validator: value => {
return value >= 0;
}
}
},
data() {
return {
dataScopeList: {}
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
this.getDataScope();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 更新
async updateDataScope() {
try {
let data = {
roleId: this.roleId,
batchSetList: this.dataScopeList
};
await dataScopeApi.updateDataScope(data);
this.$Message.success('保存成功');
this.getDataScope(this.employeeId);
} catch (e) {
//TODO zhuoda sentry
console.error(e);
}
},
// 获取数据
async getDataScope() {
try {
let result = await dataScopeApi.getDataScopeList();
this.dataScopeList = result.data;
this.getRoleDataScope();
} catch (e) {
//TODO zhuoda sentry
console.error(e);
}
},
// 获取数据范围根据角色id
async getRoleDataScope() {
try {
let result = await dataScopeApi.getDataScopeByRoleId(this.roleId);
let data = result.data;
this.dataScopeList.forEach((item, i) => {
let find = data.find(e => e.dataScopeType == item.dataScopeType);
if (find) {
this.$set(item, 'viewType', find.viewType);
} else {
this.$set(item, 'viewType', '');
}
});
} catch (e) {
//TODO zhuoda sentry
console.error(e);
}
}
}
};
</script>
<style lang="less" scoped>
.button-style {
margin: 20px 0 20px 0;
padding-left: 20px;
text-align: right;
}
.tab-data {
padding-left: 30px;
margin: 10px 0px;
}
.tab-margin {
text-align: center;
margin: 10px 0px;
}
</style>

View File

@@ -0,0 +1,407 @@
<template>
<!--div tab切换成员列表部分 start-->
<div>
<Row>
<Col span="16" style="margin:20px 0;font-size: 15px;color: #95a5a6;">管理拥有当前角色权限的人员列表</Col>
<Col span="8" style="margin:20px 0; text-align: right;">
<Button
@click="addEmployee"
type="primary"
v-privilege="'add-employee-role'"
>添加成员</Button>
<Button
@click="deleteEmployees()"
style="margin-left:10px"
type="error"
v-privilege="'delete-employee-role-batch'"
>批量移除</Button>
</Col>
</Row>
<!--Table 表格列表 start-->
<Table
:columns="columns"
:data="tableData"
:loading="isShowTablesLoading"
@on-selection-change="selectChange"
border
ref="selection"
></Table>
<!--Table 表格列表 end-->
<Row class="page" justify="end" type="flex">
<Col order="2" span="24" style="text-align: right;margin-top:20px;">
<Page
:current="currentPage"
:page-size="pageSize"
:total="total"
@on-change="changePage"
show-elevator
></Page>
</Col>
</Row>
<!--modal 添加成员 start-->
<Modal
@on-ok="confirmPrepAddEmployees()"
style="min-width:1800px"
title="添加成员"
v-model="isShowEmployeeModal"
width="700"
>
<Row class="shuttle-box">
<!--Col 左侧员工列表 start-->
<Col class="box" span="11">
<Row>
<Col class="title" span="24">员工列表</Col>
</Row>
<Row>
<Col
@dblclick.native="getCharge"
class="no-scrollbar"
id="goRight"
span="24"
style="height: 290px;overflow-y:scroll"
>
<DepartmentEmployeeTree ref="departmentEmployeeTree"></DepartmentEmployeeTree>
</Col>
</Row>
</Col>
<!--Col 左侧员工列表 end-->
<!--Col 转移按钮 start-->
<Col span="2" style="text-align: center;">
<Icon
@click.native="addPrepEmployees"
size="30"
style="line-height: 350px"
type="md-arrow-round-forward"
></Icon>
</Col>
<!--Col 转移按钮 end-->
<!--Col 右侧预添加列表 start-->
<Col class="box" span="11">
<Row>
<Col class="title" span="24">成员列表</Col>
<Col
class="no-scrollbar"
span="24"
style="overflow-y:scroll;height: 290px;text-align: center"
>
<Row :key="index" v-for="(item,index) in prepAddEmployees">
<Col span="24" style="font-size: 15px;text-align: center;">
<icon type="ios-people"></icon>
{{item.manageName}}
<Button @click.native="deletePrepEmployee(index)" icon="md-close" type="text"></Button>
</Col>
</Row>
</Col>
</Row>
</Col>
<!--Col 右侧预添加列表 end-->
</Row>
</Modal>
<!--modal 添加成员 end-->
</div>
<!--div tab切换成员列表部分 end-->
</template>
<script>
import DepartmentEmployeeTree from '../../../components/department-employee-tree/department-employee-tree';
import { roleApi } from '@/api/role';
export default {
name: 'RoleList',
components: {
DepartmentEmployeeTree
},
props: {
// 角色id
roleId: {
type: Number,
required: true,
validator: value => {
return value >= 0;
}
}
},
// 数据
data() {
return {
// 是否显示添加成员弹窗
isShowEmployeeModal: false,
currentPage: 1,
isShowTablesLoading: false,
columns: [
{
type: 'selection',
width: 60,
align: 'center'
},
{
title: '登录名',
key: 'loginName'
},
{
title: '姓名',
key: 'actualName'
},
{
title: '手机号',
key: 'phone'
},
{
title: '邮箱',
key: 'email'
},
{
title: '部门',
key: 'departmentName'
},
{
title: '操作',
key: 'operation ',
width: 100,
align: 'center',
className: 'action-hide',
render: (h, params) => {
return this.$tableAction(h, [
{
title: '移除',
directives: [
{
name: 'privilege',
value: 'delete-employee-role'
}
],
action: () => {
this.deleteEmployee(params.index);
}
}
]);
}
}
],
employeeData: {
// orderby:'',
roleId: '',
pageNum: 1,
pageSize: 10,
sort: ''
},
// 表格数据
tableData: [],
// 待添加的成员列表
prepAddEmployees: [],
// 提交添加成员数据
submitPrepAddEmployeesData: {
// 提交成员id列表
employeeIds: [],
// 当前roleId
roleId: 0
},
// 批量删除的id列表
deleteIds: [],
total: 0,
pageSize: 0
};
},
computed: {},
watch: {
roleId(val) {
if (val) {
this.employeeData.roleId = this.roleId;
this.getListEmployee(this.employeeData);
}
}
},
filters: {},
created() {},
mounted() {
this.employeeData.roleId = this.roleId;
this.getListEmployee(this.employeeData);
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 批量删除
deleteEmployees() {
let object = {};
object.employeeIds = this.deleteIds.slice(0, this.deleteIds.length);
object.roleId = this.roleId;
if (object.employeeIds.length <= 0) {
this.$Message.error('请先选择要移除的成员');
} else {
this.deleteEmployeeList(object); // 删除
}
},
// 删除待添加列表中 人员
deletePrepEmployee(index) {
this.prepAddEmployees.splice(index, 1);
},
// 批量删除方法
async deleteEmployeeList(param) {
this.isShowTablesLoading = true;
try {
await roleApi.deleteEmployeeList(param);
await this.getListEmployee(this.employeeData); // 刷新数据
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.isShowTablesLoading = false;
}
},
// 选项框多选移除
selectChange(selection) {
this.deleteIds = [];
for (let i = 0; i < selection.length; i++) {
this.deleteIds.push(selection[i].id);
}
console.log(this.deleteIds);
},
// 移除当前项
deleteEmployee(index) {
let object = {};
object.employeeId = this.tableData[index].id;
object.roleId = this.roleId;
this.deleteEmployeeRole(object);
},
// 删除角色成员方法
async deleteEmployeeRole(param) {
this.isShowTablesLoading = true;
try {
await roleApi.deleteEmployeeRole(param);
this.$Message.success('移除成功');
await this.getListEmployee(this.employeeData);
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.isShowTablesLoading = false;
}
},
// 分页改变获取数据方法
// 分页器
changePage(number) {
console.log(number);
let object = {};
object.roleId = this.roleId;
this.currentPage = number;
object.pageNum = number;
object.pageSize = this.pageSize;
object.sort = '';
this.getListEmployee(object);
},
// 确定添加角色成员
confirmPrepAddEmployees() {
this.submitPrepAddEmployeesData.employeeIds = [];
this.prepAddEmployees.forEach(e => {
console.log(e);
this.submitPrepAddEmployeesData.employeeIds.push(e.manageId);
});
console.log(this.prepAddEmployees);
this.submitPrepAddEmployeesData.roleId = this.roleId;
this.addEmployeeListRole(this.submitPrepAddEmployeesData);
this.getListEmployee(this.employeeData); // 刷新表格
},
// 添加角色成员方法
async addEmployeeListRole(param) {
this.isShowTablesLoading = true;
try {
await roleApi.addEmployeeListRole(param);
this.$Message.success('添加成功');
this.employeeData.roleId = this.roleId;
await this.getListEmployee(this.employeeData);
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.isShowTablesLoading = false;
}
},
// 穿梭框穿梭方法
addPrepEmployees() {
let obj = this.$refs.departmentEmployeeTree.getSelect();
let Obj = {};
let notHave = true;
Obj.manageName = obj.name;
Obj.manageId = obj.id;
for (let i = 0; i < this.prepAddEmployees.length; i++) {
if (this.prepAddEmployees[i].manageId === Obj.manageId) {
notHave = false;
break;
}
}
if (notHave === true) {
notHave = false;
this.submitPrepAddEmployeesData.employeeIds.push(Obj.manageId);
this.prepAddEmployees.push(Obj);
}
},
// 获取角色id对应的成员列表方法
async getListEmployee(param) {
this.isShowTablesLoading = true;
try {
let response = await roleApi.getListEmployee(param);
this.roleList = response.data;
this.total = response.data.total;
this.pageSize = response.data.pageSize;
this.tableData = this.roleList.list;
await this.getListAllEmployee();
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.isShowTablesLoading = false;
}
},
// 获取角色id对应的全部成员列表方法
async getListAllEmployee() {
this.$isShowTablesLoading = true;
try {
let response = await roleApi.getAllListEmployee(this.roleId);
let list = response.data;
this.prepAddEmployees = [];
for (let i = 0; i < list.length; i++) {
let object = {};
object.manageName = list[i].actualName;
object.manageId = list[i].id;
this.prepAddEmployees.push(object);
}
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.isShowTablesLoading = false;
}
},
// 添加成员方法,
addEmployee() {
this.isShowEmployeeModal = true;
this.getListAllEmployee();
this.submitPrepAddEmployeesData.employeeIds = [];
}
}
};
</script>
<style lang="less" scoped >
.shuttle-box {
position: relative;
.box {
border: 1px solid #f0f0f0;
border-radius: 10px;
height: 330px;
}
.title {
padding: 10px 0;
background: #426783;
font-size: 14px;
color: #fff;
text-align: center;
}
}
</style>

View File

@@ -0,0 +1,403 @@
<template>
<!--div 功能权限部分 start-->
<div id="tree">
<Row>
<Col class="col-desc" span="16">设置角色对应的功能操作后台管理权限</Col>
<Col class="button-style" span="8">
<Button
@click.native="saveChange()"
style="margin-right: 20px;"
type="primary"
v-privilege="'update-role-privilege'"
>保存</Button>
</Col>
</Row>
<!--CheckboxGroup 功能权限勾选部分 start-->
<CheckboxGroup v-model="checkedData">
<div class="checked-box">
<ul>
<!--li 一级权限模块 start-->
<li :key="module.key" v-for="(module, moduleIndex) in tree">
<div class="level-one">
<Checkbox
:label="module.key"
@click.prevent.native="selectCheckbox(tree, moduleIndex)"
>{{module.name}}</Checkbox>
</div>
<!--div 二级权限模块 start-->
<div
:key="childrenModule.key"
class="level-teo"
v-for="(childrenModule, childrenModuleIndex) in module.children"
>
<Checkbox
:label="childrenModule.key"
@click.prevent.native="selectCheckbox(module.children,childrenModuleIndex,tree)"
class="level-teo-label"
>{{childrenModule.name}}</Checkbox>
<!--div 三级权限模块 start-->
<div class="level-three">
<template v-for="(pages,pagesIndex) in childrenModule.children">
<div
:key="pages.key"
class="isLevel-four"
v-if="pages.children && pages.children.length > 0"
>
<Checkbox
:key="pages.key"
:label="pages.key"
@click.prevent.native="selectCheckbox(childrenModule.children,pagesIndex,module.children)"
class="level-three-label"
>{{pages.name}}</Checkbox>
<div :key="pagesIndex" class="Level-four" v-if="pages.children.length > 0">
<template v-for="(page, pageIndex) in pages.children">
<Checkbox
:key="page.key"
:label="page.key"
@click.prevent.native="selectCheckbox(pages.children, pageIndex,childrenModule.children,module.children)"
>{{page.name}}</Checkbox>
</template>
</div>
</div>
<Checkbox
:key="pages.key"
:label="pages.key"
@click.prevent.native="selectCheckbox(childrenModule.children,pagesIndex,module.children)"
v-else
>{{pages.name}}</Checkbox>
</template>
</div>
<!--div 三级权限模块 end-->
</div>
<!--div 二级权限模块 end-->
</li>
<!--li 一级权限模块 end-->
</ul>
</div>
</CheckboxGroup>
<!--CheckboxGroup 功能权限勾选部分 end-->
</div>
<!--div 功能权限部分 end-->
</template>
<script>
import { roleApi } from '@/api/role';
import { privilegeApi } from '@/api/privilege';
export default {
name: 'RoleTree',
props: {
// 角色id
roleId: {
type: Number,
required: true,
validator: value => {
return value >= 0;
}
}
},
components: {},
// 父级组件数据传递
data() {
return {
// 权限数据
tree: [],
loading: false,
// 提交保存数据
rolePower: {
privilegeKeyList: [],
roleId: ''
},
// 已选项
checkedData: []
};
},
computed: {},
watch: {
roleId(newVal) {
if (newVal) {
this.getListPrivilegeByRoleId(newVal);
}
}
},
filters: {},
created() {},
mounted() {
this.getListPrivilegeByRoleId(this.roleId);
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 勾选权限
selectCheckbox(
currentModuleList,
moduleIndex,
upOneModuleList,
upTwoModuleList
) {
let module = currentModuleList[moduleIndex];
// 是否勾选
let findIndex = this.checkedData.indexOf(module.key);
if (findIndex !== -1) {
this.spliceCheck(module);
// 取消的上级ID
// 判断同级是否全部已取消勾选
let currentLevelAllUnchecked = this.isUnCheckedThisLevel(
currentModuleList
);
if (currentLevelAllUnchecked && upOneModuleList) {
// 判断上级是否全部已取消勾选
let upOneLevelAllUnchecked = this.isUnCheckedThisLevel(
upOneModuleList
);
if (upOneLevelAllUnchecked && upTwoModuleList) {
// 判断上上级是否全部已取消勾选
this.isUnCheckedThisLevel(upTwoModuleList);
}
}
} else {
// 选中子级所有checkBox
this.addCheck(module);
// 选中上级组件
if (module.parentKey) {
if (
this.checkedData.findIndex(val => val === module.parentKey) === -1
) {
this.checkedData.push(module.parentKey);
}
}
// 选中上上级组件
if (upOneModuleList) {
let upOneFindIndex = upOneModuleList.findIndex(
e => e.key === module.parentKey
);
let upOneFind =
upOneFindIndex === -1 ? null : upOneModuleList[upOneFindIndex];
if (
upOneFind &&
upOneFind.parentKey &&
this.checkedData.findIndex(val => val === upOneFind.parentKey) ===
-1
) {
this.checkedData.push(upOneFind.parentKey);
}
// 选中上上上级组件
if (upTwoModuleList) {
console.log(1111, upTwoModuleList, upOneFind);
let upTwoFindIndex = upTwoModuleList.findIndex(
e => e.key === upOneFind.parentKey
);
let upTwoFind =
upTwoFindIndex === -1 ? null : upTwoModuleList[upTwoFindIndex];
if (
upTwoFind &&
upTwoFind.parentKey &&
this.checkedData.findIndex(val => val === upTwoFind.parentKey) ===
-1
) {
this.checkedData.push(upTwoFind.parentKey);
}
}
}
}
},
// 判断同级是否全部已取消勾选
isUnCheckedThisLevel(moduleList) {
let thisLevelAllUnchecked = true;
moduleList.forEach(e => {
let brotherIndex = this.checkedData.findIndex(val => val == e.key);
if (brotherIndex != -1) {
thisLevelAllUnchecked = false;
}
});
if (thisLevelAllUnchecked) {
let number = this.checkedData.findIndex(
e => e == moduleList[0].parentKey
);
if (number != -1) {
this.checkedData.splice(number, 1);
}
}
return thisLevelAllUnchecked;
},
// 选中子级所有checkBox
addCheck(module) {
let findIndex = this.checkedData.findIndex(val => val == module.key);
if (findIndex == -1) {
this.checkedData.push(module.key);
}
if (module.children) {
module.children.forEach(item => {
this.addCheck(item);
});
}
},
// 取消自己和下级勾选
spliceCheck(module) {
let findIndex = this.checkedData.findIndex(val => val == module.key);
if (findIndex != -1) {
this.checkedData.splice(findIndex, 1);
}
if (module.children) {
module.children.forEach(item => {
this.spliceCheck(item);
});
}
},
// 保存改变权限
saveChange() {
if (this.checkedData.length == 0) {
this.$Message.error('还未选择任何权限');
return;
}
this.rolePower.roleId = this.roleId;
this.rolePower.privilegeKeyList = this.checkedData.concat();
this.getRolePower(this.rolePower);
},
// 更新角色功能权限方法
async getRolePower(data) {
this.$Spin.show();
try {
await privilegeApi.getRolePower(data);
this.$Message.info('保存成功');
this.rolePower.privilegeKeyList = [];
await this.getListPrivilegeByRoleId(this.roleId);
} catch (e) {
console.error(e);
} finally {
this.$Spin.hide();
}
},
// 获取角色可选的功能权限
async getListPrivilegeByRoleId(id) {
try {
let response = await privilegeApi.getListPrivilegeByRoleId(id);
let datas = response.data;
this.tree = datas.privilege;
console.log('tree', this.tree);
this.checkedData = datas.selectedKey || [];
} catch (e) {
console.error(e);
}
}
}
};
</script>
<style lang="less" scoped>
#tree {
border: 1px solid #dcdee2;
border-top: none;
}
.col-desc {
margin: 20px 0;
font-size: 15px;
color: #95a5a6;
padding: 0 20px;
}
.button-style {
margin: 20px 0 20px 0;
padding-left: 20px;
text-align: right;
}
.check-right {
margin-right: 20px;
}
.row-border {
border: 1px solid #f0f0f0;
}
.col-border {
line-height: 50px;
padding-left: 20px;
border-right: 1px solid #f0f0f0;
}
.col-left {
line-height: 50px;
padding-left: 40px;
border-right: 1px solid #f0f0f0;
}
.col-right {
padding-left: 20px;
border-right: 1px solid #f0f0f0;
}
.ivu-tree ul li {
white-space: normal;
}
.ivu-tree {
> ul {
> li {
> ul {
> li {
.ivu-tree-title {
vertical-align: middle;
font-weight: bold;
}
> ul {
display: inline-block;
.ivu-tree-title {
font-weight: normal;
}
}
}
}
}
}
}
.checked-box {
padding: 0 15px;
ul {
padding: 0;
margin: 0;
li {
list-style: none;
padding: 0;
margin: 0;
.level-one {
border-bottom: 1px solid rgb(240, 240, 240);
padding: 10px 0;
}
.level-teo {
display: flex;
align-items: center;
margin-left: 4%;
position: relative;
border-bottom: 1px solid rgb(240, 240, 240);
line-height: 40px;
.level-teo-label {
width: 12%;
min-width: 120px;
}
// &:before{ content: ''; position: absolute; height: 100%; width: 1px; background: rgb(240, 240, 240); left: 12%; top: 0; }
.level-three {
padding-left: 4%;
display: block;
flex: 1;
min-height: 40px;
border-left: 1px rgb(240, 240, 240) solid;
.isLevel-four {
display: flex;
align-items: center;
.level-three-label {
width: 12%;
min-width: 120px;
}
.Level-four {
padding-left: 4%;
flex: 1;
min-height: 40px;
border-left: 1px rgb(240, 240, 240) solid;
}
}
}
}
.ivu-checkbox-wrapper {
margin-right: 15px;
}
}
}
}
</style>

View File

@@ -0,0 +1,339 @@
<template>
<!-- Row 角色管理 start -->
<Row :gutter="10">
<!-- Col 左侧角色列表模块 start -->
<Col :lg="5" :md="8">
<Card class="warp-card" dis-hover style=" position: relative;">
<p slot="title">角色列表</p>
<div slot="extra">
<Button
@click="showAddRoleModal()"
size="small"
type="primary"
v-privilege="'add-role'"
>添加</Button>
</div>
<!-- Menu 角色列表 start -->
<Menu
:active-name="0"
class="left role-list no-scrollbar"
ref="sideMenu"
style="height: 666px;overflow-y: scroll;width:100%;"
>
<!-- MenuItem 角色列表项 start -->
<MenuItem
:key="item.id"
:name="index"
@click.native="selectRole(item,index)"
v-for="(item,index) in roleList"
>
<Row>
<Col span="24">
<span class="role-name">
<Tooltip placement="right">
<span>{{item.roleName}}</span>
<div class="suspension-box" slot="content">
<p>
<span @click="deleteSingleRole(item)" v-privilege="'delete-role'">删除</span>
</p>
<p>
<span @click="showUpdateRoleModal(item)" v-privilege="'update-role'">编辑</span>
</p>
</div>
</Tooltip>
</span>
</Col>
</Row>
</MenuItem>
<!-- MenuItem 角色列表项 end -->
</Menu>
<!-- Menu 角色列表 end -->
<!--Modal 添加角色 start-->
<Modal title="添加角色" v-model="isShowAddRoleModal">
<Form :label-width="100" :model="ruleDetail" label-position="left">
<FormItem label="角色名称" required>
<Input
@on-keydown="ruleDetail.name=ruleDetail.name.replace(/^ +| +$/g,'')"
@on-keyup="ruleDetail.name=ruleDetail.name.replace(/^ +| +$/g,'')"
placeholder="请输入角色名称..."
v-model="ruleDetail.name"
></Input>
</FormItem>
<FormItem label="备注">
<Input placeholder="请输入备注" type="textarea" v-model="ruleDetail.detail"></Input>
</FormItem>
</Form>
<div slot="footer">
<Button @click="submitRole" type="primary">确认</Button>
<Button @click="hideAddRoleModal()">返回</Button>
</div>
</Modal>
<!--Modal 添加角色 end-->
<!--Modal 修改角色 start-->
<Modal title="修改角色" v-model="isShowUpdateRoleModal">
<Form :label-width="100" :model="ruleDetail" label-position="left">
<FormItem label="角色名称" required>
<Input
@on-keydown="ruleDetail.name=ruleDetail.name.replace(/^ +| +$/g,'')"
@on-keyup="ruleDetail.name=ruleDetail.name.replace(/^ +| +$/g,'')"
placeholder="请输入角色名称..."
v-model="ruleDetail.name"
></Input>
</FormItem>
<FormItem label="备注">
<Input placeholder="请输入备注" type="textarea" v-model="ruleDetail.detail"></Input>
</FormItem>
</Form>
<div slot="footer">
<Button @click="updateRole" type="primary">确认</Button>
<Button @click="hideUpdateRoleModal()">返回</Button>
</div>
</Modal>
<!--Modal 修改角色 end-->
<!--Modal 删除角色 start-->
<Modal
@on-cancel="cancelDeleteRole()"
@on-ok="confirmDeleteRole()"
title="删除角色"
v-model="isShowRemoveRoleModal"
>确定删除"{{ruleDetail.name}}"这个角色吗</Modal>
<!--Modal 删除角色 end-->
</Card>
</Col>
<!-- Col 左侧角色列表模块 end -->
<!-- Col 功能列表 start -->
<Col :lg="19" :md="16">
<Card class="warp-card" dis-hover>
<div class="card-title" slot="title">功能列表</div>
<!-- Menu 切换功能 start -->
<Menu :active-name="displayTab" @on-select="selectTab" mode="horizontal" style="z-index: 1">
<MenuItem :name="1">
<Icon type="ios-hammer" />功能权限
</MenuItem>
<MenuItem :name="2">
<Icon type="ios-paper" />数据范围
</MenuItem>
<MenuItem :name="3">
<Icon type="ios-people" />成员管理
</MenuItem>
</Menu>
<!-- Menu 切换功能 end -->
<!--功能权限-->
<RoleTree :roleId="roleId" v-if="displayTab==1"></RoleTree>
<!--数据范围-->
<RoleDataScope :roleId="roleId" v-if="displayTab==2"></RoleDataScope>
<!--成员管理-->
<RoleList :roleId="roleId" v-if="displayTab==3"></RoleList>
</Card>
</Col>
<!-- Col 功能列表 end -->
</Row>
<!-- Row 角色管理 end -->
</template>
<script>
import RoleTree from './components/role-tree/role-tree';
import RoleList from './components/role-list/role-list';
import RoleDataScope from './components/role-data-scope/role-data-scope';
import { roleApi } from '@/api/role';
export default {
name: 'RoleManage',
components: {
RoleTree,
RoleList,
RoleDataScope
},
props: {},
data() {
return {
roleList: {},
roleId: 0,
// 删除角色对话框隐藏
isShowRemoveRoleModal: false,
// 修改角色对话框隐藏
isShowUpdateRoleModal: false,
// 增加角色对话框隐藏
isShowAddRoleModal: false,
// 增加角色信息
ruleDetail: {
name: '',
id: '',
detail: ''
},
// 默认选中Menu标签为功能权限
displayTab: 1,
// 是否第一次请求数据
isFirst: true
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
// 初始化加载数据
this.getAllRole();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 初始化加载数据方法
async getAllRole() {
try {
let response = await roleApi.getAllRole();
this.roleList = response.data;
if (this.roleList && this.roleList.length > 0) {
this.roleId = this.roleList[0].id;
if (this.isFirst) {
this.$nextTick(() => {
this.$refs.sideMenu.updateOpened();
this.$refs.sideMenu.updateActiveName();
});
this.isFirst = false;
}
}
} catch (e) {
//TODO zhuoda sentry
console.error(e);
}
},
// 增加角色方法
async addRole(roleDesc, roleName) {
this.$Spin.show();
try {
await roleApi.addRole(roleDesc, roleName);
this.hideAddRoleModal();
await this.getAllRole(); // 刷新
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.$Spin.hide();
}
},
// 提交添加角色
submitRole() {
// 添加
if (this.ruleDetail.name !== '' && this.ruleDetail.name.length <= 20) {
this.addRole(this.ruleDetail.detail, this.ruleDetail.name);
} else {
this.$Message.warning('请先完善角色信息');
}
},
// 编辑角色方法
async updateRole() {
this.$Spin.show();
try {
let response = await roleApi.updateRole(
this.ruleDetail.id,
this.ruleDetail.detail,
this.ruleDetail.name
);
this.roleList = response.data;
this.hideUpdateRoleModal();
await this.getAllRole(); // 刷新
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.$Spin.hide();
}
},
// 删除角色方法
async deleteRole(id) {
this.$Spin.show();
try {
await roleApi.deleteRole(id);
this.$Message.success('删除成功');
await this.getAllRole(); // 刷新
} catch (e) {
//TODO zhuoda sentry
console.error(e);
} finally {
this.$Spin.hide();
}
},
// 删除单个角色
deleteSingleRole(item) {
this.isShowRemoveRoleModal = true;
this.ruleDetail.id = item.id;
this.ruleDetail.name = item.roleName;
},
// 编辑角色页面
showUpdateRoleModal(item) {
console.log(item);
this.isShowUpdateRoleModal = true;
this.ruleDetail.id = item.id;
this.ruleDetail.name = item.roleName;
this.ruleDetail.detail = item.remark;
},
// 添加角色页面
showAddRoleModal() {
this.isShowAddRoleModal = true;
this.ruleDetail.name = '';
this.ruleDetail.detail = '';
},
// 关闭更新弹窗
hideUpdateRoleModal() {
this.isShowUpdateRoleModal = false;
},
// 关闭添加弹窗
hideAddRoleModal() {
this.isShowAddRoleModal = false;
},
// 功能选择
selectTab(position) {
this.displayTab = position;
},
// 角色选择
selectRole(item, index) {
this.roleId = item.id;
},
// 确定删除
confirmDeleteRole() {
this.deleteRole(this.ruleDetail.id);
this.isShowRemoveRoleModal = false;
},
// 取消删除
cancelDeleteRole() {
this.isShowRemoveRoleModal = false;
}
}
};
</script>
<style lang="less" scoped>
.role-list {
line-height: 30px;
padding: 10px 0;
.role-name {
position: relative;
}
&::after {
display: none;
}
button {
margin-left: 3px;
}
}
.ivu-menu-item-active:not(.ivu-menu-submenu) {
z-index: 0 !important;
}
.suspension-box {
z-index: 999;
padding: 0 8px;
p {
padding: 3px 0;
}
}
</style>

View File

@@ -0,0 +1,19 @@
<template>
<error-content code="401" desc="Oh~~您没有浏览这个页面的权限~" :src="src" />
</template>
<script>
import error401 from '@/assets/images/error-page/error-401.svg';
import ErrorContent from './error-content.vue';
export default {
name: 'error401',
components: {
ErrorContent
},
data () {
return {
src: error401
};
}
};
</script>

View File

@@ -0,0 +1,19 @@
<template>
<error-content code="404" desc="Oh~~您的页面好像飞走了~" :src="src" />
</template>
<script>
import error404 from '@/assets/images/error-page/error-404.svg';
import ErrorContent from './error-content.vue';
export default {
name: 'error404',
components: {
ErrorContent
},
data () {
return {
src: error404
};
}
};
</script>

View File

@@ -0,0 +1,19 @@
<template>
<error-content code="500" desc="Oh~~鬼知道服务器经历了什么~" :src="src"/>
</template>
<script>
import error404 from '@/assets/images/error-page/error-500.svg';
import ErrorContent from './error-content.vue';
export default {
name: 'error500',
components: {
ErrorContent
},
data () {
return {
src: error404
};
}
};
</script>

View File

@@ -0,0 +1,40 @@
<template>
<div>
<Button size="large" type="text" @click="backHome">返回首页</Button>
<Button size="large" type="text" @click="backPrev">返回上一页({{ second }}s)</Button>
</div>
</template>
<script>
import './error.less';
export default {
name: 'backBtnGroup',
data () {
return {
second: 5,
timer: null
};
},
mounted () {
this.timer = setInterval(() => {
if (this.second === 0) this.backPrev();
else this.second--;
}, 1000);
},
beforeDestroy () {
clearInterval(this.timer);
},
methods: {
// 回到首页
backHome () {
this.$router.replace({
name: this.$config.homeName
});
},
// 返回上一页
backPrev () {
this.$router.go(-1);
}
}
};
</script>

View File

@@ -0,0 +1,28 @@
<template>
<div class="error-page">
<div class="content-con">
<img :src="src" :alt="code">
<div class="text-con">
<h4>{{ code }}</h4>
<h5>{{ desc }}</h5>
</div>
<back-btn-group class="back-btn-group"></back-btn-group>
</div>
</div>
</template>
<script>
import './error.less';
import BackBtnGroup from './back-btn-group.vue';
export default {
name: 'errorContent',
components: {
BackBtnGroup
},
props: {
code: String,
desc: String,
src: String
}
};
</script>

View File

@@ -0,0 +1,46 @@
.error-page{
width: 100%;
height: 100%;
position: relative;
background: #f8f8f9;
.content-con{
width: 700px;
height: 600px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -60%);
img{
display: block;
width: 100%;
height: 100%;
}
.text-con{
position: absolute;
left: 0px;
top: 0px;
h4{
position: absolute;
left: 0px;
top: 0px;
font-size: 80px;
font-weight: 700;
color: #348EED;
}
h5{
position: absolute;
width: 700px;
left: 0px;
top: 100px;
font-size: 20px;
font-weight: 700;
color: #67647D;
}
}
.back-btn-group{
position: absolute;
right: 0px;
bottom: 20px;
}
}
}

View File

@@ -0,0 +1,279 @@
<template>
<div>
<Card class="warp-card" dis-hover>
<Form :model="searchForm" class="tools" inline ref="searchForm">
<FormItem prop="fileName">
<Input placeholder="请输入文件名" type="text" v-model="searchForm.fileName" />
</FormItem>
<FormItem prop="sendStatus">
<Select placeholder="请选择业务类型" style="width:200px" v-model="searchForm.moduleType">
<Option value>全部</Option>
<Option :key="item.value" :value="item.value" v-for="item in moduleTypes">{{item.desc}}</Option>
</Select>
</FormItem>
<FormItem prop="sendStatus">
<Select placeholder="请选择文件上传位置" style="width:200px" v-model="searchForm.fileLocationType">
<Option value>全部</Option>
<Option
:key="item.value"
:value="item.value"
v-for="item in fileLocationTypes"
>{{item.desc}}</Option>
</Select>
</FormItem>
<FormItem>
<ButtonGroup>
<Button
@click="find"
icon="ios-search"
type="primary"
v-privilege="'file-filePage-query'"
>查询</Button>
<Button
@click="reset"
icon="md-refresh"
type="default"
v-privilege="'file-filePage-query'"
>重置</Button>
</ButtonGroup>
</FormItem>
<FormItem v-privilege="'file-filePage-upload'">
<Upload
:action="uploadUrl"
:headers="uploadHeader"
:onError="uploadError"
:onSuccess="uploadSuccess"
:showUploadList="false"
>
<Button icon="ios-cloud-upload-outline" type="primary">上传新文件</Button>
</Upload>
</FormItem>
</Form>
<Table :columns="columns" :data="tableData" :loading="tableLoading"></Table>
<Page
:current="searchForm.pageNum"
:page-size="searchForm.pageSize"
:page-size-opts="[10, 20, 30, 50, 100]"
:total="total"
@on-change="changePage"
@on-page-size-change="changePageSize"
show-elevator
show-sizer
show-total
style="margin:24px 0;text-align:right;"
></Page>
</Card>
</div>
</template>
<script>
import { fileApi } from '@/api/file.js';
import { FILE_TYPE, SERVICE_TYPE } from '@/constants/file';
import Cookies from 'js-cookie';
import { TOKEN_KEY } from '@/lib/cookie';
export default {
name: 'FileList',
data() {
let that = this;
return {
// 数据量
total: null,
// 文件表格加载
tableLoading: false,
// 查询参数
searchForm: {
pageNum: 1,
pageSize: 10,
// 是否查询总条数
searchCount: true,
fileName: null,
// 业务类型 BACK_USER:1 backUser/config
moduleType: 1,
// 文件位置 LOCAL:1 本地文件服务 ALI_OSS:2 阿里OSS文件服务 QI_NIU_OSS:3七牛文件服务
fileLocationType: null
},
// 表头
columns: [
{
title: '业务类型',
key: 'moduleType',
render(h, params) {
return h('span', {}, that.getModuleTypeDesc(params.row.moduleType));
}
},
{
title: '文件位置',
key: 'fileLocationType',
render(h, params) {
return h(
'span',
{},
that.getFileLocationTypeDesc(params.row.fileLocationType)
);
}
},
{
title: '文件名称',
key: 'fileName'
},
{
title: '上传时间',
key: 'createTime'
},
{
title: '操作',
key: 'action',
width: 160,
align: 'center',
className: 'action-hide',
render: (h, params) => {
return this.$tableAction(h, [
{
title: '下载',
directives: [
{
name: 'privilege',
value: 'file-filePage-download'
}
],
action: () => {
this.downloadFile(params.row.id);
}
}
]);
}
}
],
// table数据
tableData: []
};
},
computed: {
uploadHeader: function() {
let header = {
'x-access-token': Cookies.get(TOKEN_KEY)
};
return header;
},
uploadUrl: function() {
let baseUrl = fileApi.fileUploadLocalUrl;
switch (this.searchForm.fileLocationType) {
case 2:
baseUrl = fileApi.fileUploadAliUrl;
break;
case 3:
baseUrl = fileApi.fileUploadQiNiuUrl;
break;
default:
break;
}
let url = baseUrl + this.searchForm.moduleType;
return url;
},
// 文件业务类型
moduleTypes: function() {
let array = [];
for (let item in SERVICE_TYPE) {
let obj = {};
obj.desc = SERVICE_TYPE[item].desc;
obj.value = SERVICE_TYPE[item].value;
array.push(obj);
}
return array;
},
fileLocationTypes: function() {
let array = [];
for (const item in FILE_TYPE) {
let obj = {};
obj.desc = FILE_TYPE[item].desc;
obj.value = FILE_TYPE[item].value;
array.push(obj);
}
return array;
}
},
created() {
this.getFileList();
},
methods: {
// 上传成功钩子
async uploadSuccess(e) {
if (!e.success) {
console.error(e);
return this.uploadError();
}
this.$Spin.show();
let reqBody = {
moduleId: 1,
moduleType: this.searchForm.moduleType ? this.searchForm.moduleType : 1,
fileLocationType: this.searchForm.fileLocationType
? this.searchForm.fileLocationType
: 1,
fileName: e.data.fileName,
filePath: e.data.filePath
};
let rep = await fileApi.addFile(reqBody);
this.$Spin.hide();
this.$Message.success('上传成功');
this.find();
},
// 上传失败钩子
uploadError(e) {
this.$Message.error('上传出错,请重试!');
console.error(e);
this.find();
},
// 下载文件
downloadFile(id) {
fileApi.downLoadFile(id);
},
// 根据数字获取业务类型描述
getModuleTypeDesc(value) {
return this.$enum.getDescByValue('SERVICE_TYPE', value);
},
// 根据数字获取文件位置描述
getFileLocationTypeDesc(value) {
return this.$enum.getDescByValue('FILE_TYPE', value);
},
// 重置
reset() {
this.searchForm.fileName = null;
this.searchForm.moduleType = 1;
this.searchForm.fileLocationType = null;
this.$refs.searchForm.resetFields();
this.find();
},
// 查询
find() {
this.searchForm.pageNum = 1;
this.searchForm.pageSize = 10;
this.getFileList();
},
// 更改分页查询条数
changePageSize(pageSize) {
this.searchForm.pageNum = 1;
this.searchForm.pageSize = pageSize;
this.getFileList();
},
// 获取文件数据
async getFileList() {
try {
this.tableLoading = true;
let res = await fileApi.queryFileList(this.searchForm);
this.tableData = res.data.list;
this.total = res.data.total;
this.tableLoading = false;
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.tableLoading = false;
}
},
// 页码改变
changePage(pageNum) {
this.searchForm.pageNum = pageNum;
this.getFileList();
}
}
};
</script>

View File

@@ -0,0 +1,115 @@
<template>
<Card class="warp-card" dis-hover>
<Form class="tools" inline>
<FormItem>
<Button
@click="queryHeartBeatRecord"
icon="md-refresh"
type="primary"
v-privilege="'heart-beat-query'"
>刷新</Button>
</FormItem>
</Form>
<Alert>
<h3>Smart-Heart-Beat 心跳服务介绍</h3>
<pre>
简介Smart-Heart-Beat 是心跳服务用于监测Java应用的状态等其他信息
原理
- Java后端会在项目启动的时候开启一个线程每隔一段时间将该应用的IP进程号更新到数据库t_heart_beat_record表中
用途
· 在各个环境无论开发测试生产能统一看到所有启动的服务列表
· 检测Java应用是否存活
· 强烈推荐 当某些业务只允许有一个服务启动的时候用于排查是否别人也启动的服务
</pre>
</Alert>
<Table :columns="columns" :data="tableData" :loading="tableLoading" border></Table>
<Page
:current="searchFrom.pageNum"
:page-size="searchFrom.pageSize"
:show-sizer="true"
:show-total="true"
:total="pageTotal"
@on-change="changePage"
@on-page-size-change="changePageSize"
style="margin:24px 0;text-align:right;"
></Page>
</Card>
</template>
<script>
import { heartBeatApi } from '@/api/heart-beat';
export default {
name: 'HeartBeatList',
components: {},
props: {},
data() {
return {
searchFrom: {
pageSize: 10,
pageNum: 1
},
tableLoading: false,
pageTotal: 1,
tableData: [],
columns: [
{
title: 'Id',
key: 'id',
width: 100
},
{
title: '路径',
key: 'projectPath'
},
{
title: '进程号',
key: 'processNo'
},
{
title: '服务器ip',
key: 'serverIp'
},
{
title: '进程启动时间',
key: 'processStartTime'
},
{
title: '心跳时间 ',
key: 'heartBeatTime'
}
]
};
},
mounted() {
this.queryHeartBeatRecord();
},
methods: {
// 查询心跳记录
async queryHeartBeatRecord() {
try {
this.tableLoading = true;
let result = await heartBeatApi.queryHeartBeatRecord(this.searchFrom);
this.tableData = result.data.list;
this.pageTotal = result.data.total;
this.tableLoading = false;
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.tableLoading = false;
}
},
// 页码改变
changePage(pageNum) {
this.searchFrom.pageNum = pageNum;
this.queryHeartBeatRecord();
},
// 改变每页显示数据条数
changePageSize(pageSize) {
this.searchFrom.pageNum = 1;
this.searchFrom.pageSize = pageSize;
this.queryHeartBeatRecord();
}
}
};
</script>

View File

@@ -0,0 +1,43 @@
<template>
<div class="card-main">
<div class="title">
{{title}}
<span>{{desc}}</span>
</div>
<slot></slot>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: '标题'
},
desc: {
type: String,
default: '描述'
}
}
};
</script>
<style lang='less'>
.card-main {
border-radius: 8px;
background: #fff;
margin-bottom: 20px;
padding-bottom: 10px;
}
.title {
color: #060606;
font-size: 16px;
padding: 20px 32px;
span {
padding-left: 17px;
font-size: 12px;
color: #dededf;
}
}
</style>

View File

@@ -0,0 +1,140 @@
<template>
<div class="bar-main"
id="box"
ref="dom"></div>
</template>
<script>
import echarts from 'echarts';
import tdTheme from './theme.json';
import { on, off } from '@/lib/util';
echarts.registerTheme('tdTheme', tdTheme);
export default {
props: {
value: Object,
text: String,
subtext: String
},
mounted() {
this.initChart();
},
methods: {
resize() {
this.dom.resize();
},
initChart() {
this.$nextTick(() => {
let xAxisData = Object.keys(this.value);
let seriesData = Object.values(this.value);
let option = {
grid: {
left: '1%',
right: '1%',
top: '2%',
bottom: '1%',
containLabel: true
},
title: {
text: this.text,
subtext: this.subtext,
x: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{c}人',
// position: ['30%', '90%'],
position: 'top',
backgroundColor: '#FAFBFE',
textStyle: {
fontSize: 14,
color: '#6d6d6d'
}
},
xAxis: {
// show: false,
type: 'category',
data: xAxisData,
splitLine: {
show: false
}
},
yAxis: [
{
// show: false,
type: 'value',
splitLine: {
show: true,
lineStyle: {
// 设置刻度线粗度(粗的宽度)
width: 1,
// 颜色数组,数组数量要比刻度线数量大才能不循环使用
color: [
'rgba(0, 0, 0, 0)',
'#eee',
'#eee',
'#eee',
'#eee',
'#eee',
'#eee',
'#eee',
'#eee'
]
}
}
}
],
series: [
{
data: seriesData,
type: 'bar',
barWidth: 36,
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#f2f5ff' },
{ offset: 1, color: '#fff' }
])
}
},
itemStyle: {
normal: {
barBorderRadius: [50],
color: new echarts.graphic.LinearGradient(
0,
1,
0,
0,
[
{
offset: 0,
color: '#3AA1FF' // 0% 处的颜色
},
{
offset: 1,
color: '#36CBCB' // 100% 处的颜色
}
],
false
)
}
}
}
]
};
this.dom = echarts.init(this.$refs.dom, 'tdTheme');
this.dom.setOption(option);
on(window, 'resize', this.resize);
});
}
}
};
</script>
<style>
.bar-main {
width: 100%;
height: 360px;
padding: 28px;
background: #fff;
}
</style>

View File

@@ -0,0 +1,106 @@
<template>
<div class="funnel-main"
id="box"
ref="dom"></div>
</template>
<script>
import echarts from 'echarts';
import tdTheme from './theme.json';
import { on, off } from '@/lib/util';
echarts.registerTheme('tdTheme', tdTheme);
export default {
props: {
value: Array,
text: String,
subtext: String
},
mounted() {
this.initChart();
},
methods: {
resize() {
this.dom.resize();
},
initChart() {
this.$nextTick(() => {
let legend = this.value.map(_ => _.name);
let option = {
grid: {
left: '1%',
right: '1%',
top: '2%',
bottom: '1%',
containLabel: true
},
title: {
text: this.text,
subtext: this.subtext,
x: 'center'
},
tooltip: {
show: false,
trigger: 'item',
formatter: '{c} ({d}%)',
// position: ['30%', '90%'],
position: 'right',
backgroundColor: 'transparent',
textStyle: {
fontSize: 14,
color: '#666'
}
},
legend: {
orient: 'vertical',
left: 'right',
bottom: 0,
// data: legend,
backgroundColor: 'transparent',
icon: 'circle'
},
series: [
{
name: '访问来源',
type: 'funnel',
radius: ['50%', '65%'],
avoidLabelOverlap: false,
label: {
normal: {
show: false,
position: 'right',
formatter: '{c} ({d}%)'
}
},
// labelLine: {
// normal: {
// show: false
// }
// },
data: [
{ value: 400, name: '交易完成' },
{ value: 300, name: '支付订单' },
{ value: 200, name: '生成订单' },
{ value: 100, name: '放入购物车' },
{ value: 100, name: '浏览网站' }
]
}
]
};
this.dom = echarts.init(this.$refs.dom, 'tdTheme');
this.dom.setOption(option);
on(window, 'resize', this.resize);
});
}
}
};
</script>
<style>
.funnel-main {
width: 100%;
height: 295px;
padding: 28px;
background: #fff;
}
</style>

View File

@@ -0,0 +1,87 @@
<template>
<div class="gauge-main"
id="box"
ref="dom"></div>
</template>
<script>
import echarts from 'echarts';
import tdTheme from './theme.json';
import { on, off } from '@/lib/util';
echarts.registerTheme('tdTheme', tdTheme);
export default {
props: {
value: Object,
text: String,
subtext: String
},
mounted () {
this.initChart();
},
methods: {
resize () {
this.dom.resize();
},
initChart () {
this.$nextTick(() => {
let option = {
grid: {
left: 0,
right: 0,
top: 0,
bottom: 0
// containLabel: true
},
tooltip: {
formatter: '{a} <br/>{b} : {c}%'
},
toolbox: {},
series: [
{
name: '业务指标',
startAngle: 195,
endAngle: -15,
axisLine: {
show: true,
lineStyle: {
color: [[0.6, '#4ECB73'], [0.8, '#FBD437'], [1, '#F47F92']],
width: 16
}
},
pointer: {
length: '80%',
width: 3,
color: 'auto'
},
axisTick: {
show: false
},
splitLine: { show: false },
type: 'gauge',
detail: {
formatter: '{value}%',
textStyle: {
color: '#595959',
fontSize: 32
}
},
data: [{ value: 10 }]
}
]
};
this.dom = echarts.init(this.$refs.dom, 'tdTheme');
this.dom.setOption(option);
on(window, 'resize', this.resize);
});
}
}
};
</script>
<style>
.gauge-main {
width: 100%;
height: 360px;
background: #fff;
}
</style>

View File

@@ -0,0 +1,123 @@
<template>
<div class="line-main"
id="box"
ref="dom"></div>
</template>
<script>
import echarts from 'echarts';
import tdTheme from './theme.json';
import { on, off } from '@/lib/util';
echarts.registerTheme('tdTheme', tdTheme);
export default {
props: {
value: Object,
text: String,
subtext: String
},
mounted() {
this.initChart();
},
methods: {
resize() {
this.dom.resize();
},
initChart() {
this.$nextTick(() => {
let xAxisData = Object.keys(this.value);
let seriesData = Object.values(this.value);
let option = {
grid: {
left: '1%',
right: '1%',
top: '2%',
bottom: '1%',
containLabel: true
},
title: {
text: this.text,
subtext: this.subtext,
x: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{c}人',
// position: ['30%', '90%'],
position: 'top',
backgroundColor: '#387DE1',
textStyle: {
fontSize: 18,
color: '#fff'
}
},
xAxis: {
// show: false,
type: 'category',
data: xAxisData,
splitLine: {
show: false
}
},
yAxis: [
{
// show: false,
type: 'value',
splitLine: {
show: true,
lineStyle: {
// 设置刻度线粗度(粗的宽度)
width: 1,
// 颜色数组,数组数量要比刻度线数量大才能不循环使用
color: [
'rgba(0, 0, 0, 0)',
'#eee',
'#eee',
'#eee',
'#eee',
'#eee',
'#eee',
'#eee',
'#eee'
]
}
}
}
],
series: [
{
data: seriesData,
type: 'line',
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#f2f5ff' },
{ offset: 1, color: '#fff' }
])
}
},
lineStyle: {
normal: {
width: 5,
color: '#36CBCB'
}
}
}
]
};
this.dom = echarts.init(this.$refs.dom, 'tdTheme');
this.dom.setOption(option);
on(window, 'resize', this.resize);
});
}
}
};
</script>
<style>
.line-main {
width: 100%;
height: 360px;
padding: 28px;
background: #fff;
}
</style>

View File

@@ -0,0 +1,110 @@
<template>
<div class="pie-main"
id="box"
ref="dom"></div>
</template>
<script>
import echarts from 'echarts';
import tdTheme from './theme.json';
import { on, off } from '@/lib/util';
echarts.registerTheme('tdTheme', tdTheme);
export default {
props: {
value: Array,
text: String,
subtext: String
},
mounted () {
this.initChart();
},
methods: {
resize () {
this.dom.resize();
},
initChart () {
this.$nextTick(() => {
let legend = this.value.map(_ => _.name);
let option = {
title: {
text: this.text,
subtext: this.subtext,
x: 'center'
},
position: {
top: 40
},
tooltip: {
trigger: 'item',
formatter: '{c} ({d}%)',
// position: ['30%', '90%'],
position: function (point, params, dom, rect, size) {
console.log(size);
let leftWidth = size.viewSize[0] / 2 - size.contentSize[0] / 2;
console.log(leftWidth);
return { left: leftWidth, bottom: 0 };
},
backgroundColor: 'transparent',
textStyle: {
fontSize: 24,
color: '#666'
}
},
legend: {
// orient: 'vertical',
top: 0,
data: legend,
backgroundColor: 'transparent',
icon: 'circle'
},
series: [
{
name: '访问来源',
type: 'pie',
radius: ['45%', '60%'],
center: ['50%', '52%'],
avoidLabelOverlap: false,
label: {
normal: {
show: false,
position: 'center'
},
emphasis: {
show: true,
textStyle: {
fontSize: '24'
}
}
},
labelLine: {
normal: {
show: false
}
},
data: [
{ value: 335, name: '直接访问' },
{ value: 310, name: '邮件营销' },
{ value: 234, name: '联盟广告' },
{ value: 135, name: '视频广告' },
{ value: 1548, name: '搜索引擎' }
]
}
]
};
this.dom = echarts.init(this.$refs.dom, 'tdTheme');
this.dom.setOption(option);
on(window, 'resize', this.resize);
});
}
}
};
</script>
<style>
.pie-main {
width: 100%;
height: 360px;
padding: 28px;
background: #fff;
}
</style>

View File

@@ -0,0 +1,41 @@
<template>
<div class="circle-main">
<i-circle :percent="80"
:size="164"
:trail-width="1"
stroke-color="#3AA1FF"
stroke-linecap="round"
trail-color="#ededed">
<span class="demo-Circle-inner"
style="font-size:24px">80%</span>
</i-circle>
<div>
<button>查看详情</button>
</div>
</div>
</template>
<script>
export default {};
</script>
<style lang="less">
.circle-main {
background: #fff;
height: 295px;
text-align: center;
padding-top: 20px;
button {
margin-top: 45px;
width: 156px;
cursor: pointer;
outline: none;
height: 36px;
background: rgba(241, 241, 241, 1);
border-radius: 18px;
color: #808080;
font-size: 18px;
border: none;
}
}
</style>

View File

@@ -0,0 +1,59 @@
<template>
<div class="progress-list">
<div :key="item.name"
class="item"
v-for="item in value">
<p>
<i :style="{background:item.color}"></i>
{{item.name}}
<span>{{item.value}}</span>
</p>
<Progress :percent="45"
:stroke-color="item.color"
:stroke-width="16"
class="progress"
hide-info
status="active" />
</div>
</div>
</template>
<script>
export default {
props: {
value: Array
}
};
</script>
<style lang="less">
.progress-list {
height: 295px;
padding: 28px;
.item {
display: flex;
font-size: 16px;
color: #595959;
margin-bottom: 16px;
span {
color: #808080;
margin: 0 29px;
}
p {
width: 400px;
i {
display: inline-block;
border-radius: 5px;
width: 10px;
height: 10px;
margin-right: 13px;
background: #f66;
}
padding-right: 200px;
}
.progress {
flex: 1;
}
}
}
</style>

View File

@@ -0,0 +1,490 @@
{
"color": [
"#2d8cf0",
"#19be6b",
"#ff9900",
"#E46CBB",
"#9A66E4",
"#ed3f14"
],
"backgroundColor": "rgba(0,0,0,0)",
"textStyle": {},
"title": {
"textStyle": {
"color": "#516b91"
},
"subtextStyle": {
"color": "#93b7e3"
}
},
"line": {
"itemStyle": {
"normal": {
"borderWidth": "2"
}
},
"lineStyle": {
"normal": {
"width": "2"
}
},
"symbolSize": "6",
"symbol": "emptyCircle",
"smooth": true
},
"radar": {
"itemStyle": {
"normal": {
"borderWidth": "2"
}
},
"lineStyle": {
"normal": {
"width": "2"
}
},
"symbolSize": "6",
"symbol": "emptyCircle",
"smooth": true
},
"bar": {
"itemStyle": {
"normal": {
"barBorderWidth": 0,
"barBorderColor": "#ccc"
},
"emphasis": {
"barBorderWidth": 0,
"barBorderColor": "#ccc"
}
}
},
"pie": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"scatter": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"boxplot": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"parallel": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"sankey": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"funnel": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"gauge": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"candlestick": {
"itemStyle": {
"normal": {
"color": "#edafda",
"color0": "transparent",
"borderColor": "#d680bc",
"borderColor0": "#8fd3e8",
"borderWidth": "2"
}
}
},
"graph": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
}
},
"lineStyle": {
"normal": {
"width": 1,
"color": "#aaa"
}
},
"symbolSize": "6",
"symbol": "emptyCircle",
"smooth": true,
"color": [
"#2d8cf0",
"#19be6b",
"#f5ae4a",
"#9189d5",
"#56cae2",
"#cbb0e3"
],
"label": {
"normal": {
"textStyle": {
"color": "#eee"
}
}
}
},
"map": {
"itemStyle": {
"normal": {
"areaColor": "#f3f3f3",
"borderColor": "#516b91",
"borderWidth": 0.5
},
"emphasis": {
"areaColor": "rgba(165,231,240,1)",
"borderColor": "#516b91",
"borderWidth": 1
}
},
"label": {
"normal": {
"textStyle": {
"color": "#000"
}
},
"emphasis": {
"textStyle": {
"color": "rgb(81,107,145)"
}
}
}
},
"geo": {
"itemStyle": {
"normal": {
"areaColor": "#f3f3f3",
"borderColor": "#516b91",
"borderWidth": 0.5
},
"emphasis": {
"areaColor": "rgba(165,231,240,1)",
"borderColor": "#516b91",
"borderWidth": 1
}
},
"label": {
"normal": {
"textStyle": {
"color": "#000"
}
},
"emphasis": {
"textStyle": {
"color": "rgb(81,107,145)"
}
}
}
},
"categoryAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#cccccc"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"textStyle": {
"color": "#999999"
}
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#eeeeee"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"valueAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#cccccc"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"textStyle": {
"color": "#999999"
}
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#eeeeee"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"logAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#cccccc"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"textStyle": {
"color": "#999999"
}
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#eeeeee"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"timeAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#cccccc"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"textStyle": {
"color": "#999999"
}
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#eeeeee"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"toolbox": {
"iconStyle": {
"normal": {
"borderColor": "#999"
},
"emphasis": {
"borderColor": "#666"
}
}
},
"legend": {
"textStyle": {
"color": "#999999"
}
},
"tooltip": {
"axisPointer": {
"lineStyle": {
"color": "#ccc",
"width": 1
},
"crossStyle": {
"color": "#ccc",
"width": 1
}
}
},
"timeline": {
"lineStyle": {
"color": "#8fd3e8",
"width": 1
},
"itemStyle": {
"normal": {
"color": "#8fd3e8",
"borderWidth": 1
},
"emphasis": {
"color": "#8fd3e8"
}
},
"controlStyle": {
"normal": {
"color": "#8fd3e8",
"borderColor": "#8fd3e8",
"borderWidth": 0.5
},
"emphasis": {
"color": "#8fd3e8",
"borderColor": "#8fd3e8",
"borderWidth": 0.5
}
},
"checkpointStyle": {
"color": "#8fd3e8",
"borderColor": "rgba(138,124,168,0.37)"
},
"label": {
"normal": {
"textStyle": {
"color": "#8fd3e8"
}
},
"emphasis": {
"textStyle": {
"color": "#8fd3e8"
}
}
}
},
"visualMap": {
"color": [
"#516b91",
"#59c4e6",
"#a5e7f0"
]
},
"dataZoom": {
"backgroundColor": "rgba(0,0,0,0)",
"dataBackgroundColor": "rgba(255,255,255,0.3)",
"fillerColor": "rgba(167,183,204,0.4)",
"handleColor": "#a7b7cc",
"handleSize": "100%",
"textStyle": {
"color": "#333"
}
},
"markPoint": {
"label": {
"normal": {
"textStyle": {
"color": "#eee"
}
},
"emphasis": {
"textStyle": {
"color": "#eee"
}
}
}
}
}

View File

@@ -0,0 +1,154 @@
<template>
<div>
<Row>
<Col>
<HomeCard desc="Pending transaction" title="用户活跃量">
<ActivePlate :infoList="infoCardData" />
</HomeCard>
</Col>
</Row>
<Row :gutter="20">
<i-col :lg="6" :md="24">
<HomeCard desc="User from" title="用户来源">
<ChartPie :value="pieData" />
</HomeCard>
</i-col>
<i-col :lg="18" :md="24">
<HomeCard desc="User active" title="每周用户活跃量">
<ChartLine :value="lineData" />
</HomeCard>
</i-col>
</Row>
<Row :gutter="20">
<i-col :lg="18" :md="24">
<HomeCard desc="User from" title="柱状图">
<ChartBar :value="lineData" />
</HomeCard>
</i-col>
<i-col :lg="6" :md="24">
<HomeCard desc="complete" title="完成率">
<ChartGauge />
</HomeCard>
</i-col>
</Row>
<Row :gutter="20">
<i-col :lg="12" :md="24">
<HomeCard desc="progress" title="进度条">
<HomeProgress :value="pieData" />
</HomeCard>
</i-col>
<i-col :lg="6" :md="24">
<HomeCard desc="progress" title="目标完成度">
<Home-circle />
</HomeCard>
</i-col>
<i-col :lg="6" :md="24">
<HomeCard desc="progress" title="漏斗图">
<ChartFunnel :value="pieData" />
</HomeCard>
</i-col>
</Row>
<Modal
v-model="adModal" width="800">
<Ad/>
</Modal>
</div>
</template>
<script>
import ActivePlate from '_c/active-plate/active-plate';
import CountTo from '_c/count-to';
import HomeCard from './components/card';
import ChartPie from './components/chart-pie';
import ChartLine from './components/chart-line';
import ChartGauge from './components/chart-gauge';
import ChartBar from './components/chart-bar';
import HomeCircle from './components/home-circle';
import HomeProgress from './components/home-progress';
import ChartFunnel from './components/chart-funnel';
import Ad from '@/components/smart-admin-ad';
export default {
name: 'Home',
components: {
HomeCard,
ActivePlate,
CountTo,
ChartPie,
ChartFunnel,
ChartLine,
HomeCircle,
ChartGauge,
ChartBar,
HomeProgress,
Ad
},
props: {},
data() {
return {
adModal:true,
infoCardData: [
{
title: '新增用户',
icon: 'md-person-add',
count: 803,
color: '#11A0F8'
},
{ title: '累计点击', icon: 'md-locate', count: 232, color: '#FFBB44 ' },
{
title: '新增问答',
icon: 'md-help-circle',
count: 142,
color: '#7ACE4C'
},
{ title: '分享统计', icon: 'md-share', count: 657, color: '#11A0F8' },
{
title: '新增互动',
icon: 'md-chatbubbles',
count: 12,
color: '#91AFC8'
},
{ title: '新增页面', icon: 'md-map', count: 14, color: '#91AFC8' }
],
pieData: [
{ value: 335, name: '直接访问', color: '#3AA1FF' },
{ value: 310, name: '邮件营销', color: '#36CBCB' },
{ value: 234, name: '联盟广告', color: '#4ECB73' },
{ value: 135, name: '视频广告', color: '#F47F92' },
{ value: 1548, name: '搜索引擎', color: '#FBD437' }
],
lineData: {
Mon: 13253,
Tue: 34235,
Wed: 26321,
Thu: 12340,
Fri: 24643,
Sat: 1322,
Sun: 1324
}
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {}
};
</script>
<style lang="less">
.count-style {
font-size: 50px;
}
</style>

View File

@@ -0,0 +1,2 @@
import home from './home.vue';
export default home;

View File

@@ -0,0 +1,94 @@
<template>
<Card class="warp-card" dis-hover>
<Alert>
<h3>让我们测试一下</h3>
<pre>
1 Query保存 之后刷新KeepAlive页面即router query传入noKeepAlive=true参数
2 Param保存 之后刷新KeepAlive页面即router param传入noKeepAlive=true参数
3 返回 之后不刷新KeepAlive页面不用传入任何参数
</pre>
</Alert>
<Form class="tools" inline>
<FormItem>
<Input placeholder="请输入" v-model="addData" />
</FormItem>
<FormItem>
<Button @click="addContent4Query" type="primary">Query保存</Button>
<Button @click="addContent4Param" style="margin-left:10px" type="primary">Param保存</Button>
</FormItem>
<FormItem>
<Button @click="backPage" type="default">返回</Button>
</FormItem>
</Form>
</Card>
</template>
<script>
export default {
name: 'KeepAliveAddContent',
components: {},
props: {},
data() {
return {
addData: ''
};
},
computed: {},
watch: {},
filters: {},
methods: {
addContent4Query() {
if (this.addData === '') {
this.$Message.error('请输入内容');
return;
}
this.$Notice.success({
title: 'noKeepAlive跳转',
render: h => {
return h('pre', [
'this.$router.closeCurrentPageAndPush({\r\n' +
' name: "KeepAliveContentList",\r\n' +
' query: { noKeepAlive: true }\r\n' +
'});'
]);
}
});
this.$router.closeCurrentPageAndPush({
name: 'KeepAliveContentList',
query: { noKeepAlive: true }
});
},
addContent4Param() {
if (this.addData === '') {
this.$Message.error('请输入内容');
return;
}
this.$Notice.success({
title: 'noKeepAlive跳转',
render: h => {
return h('pre', [
'this.$router.closeCurrentPageAndPush({\r\n' +
' name: "KeepAliveContentList",\r\n' +
' params: { noKeepAlive: true }\r\n' +
'});'
]);
}
});
this.$router.closeCurrentPageAndPush({
name: 'KeepAliveContentList',
params: { noKeepAlive: true }
});
},
backPage() {
// 返回
this.$router.closeCurrentPage();
}
}
};
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,80 @@
<template>
<Card class="warp-card" dis-hover>
<Alert>
<h3>keep-alive介绍</h3>
<pre>
简介我们修改了原版iview-admin缓存策略,使之更灵活
用途
· 用于关闭页面后跳转指定页面,可控制是否刷新跳转页面
· 点击左侧菜单栏打开已有页面,标签页切换依旧会缓存
</pre>
</Alert>
<Form class="tools" inline>
<FormItem>
<Input placeholder="请输入" v-model="searchString">
<Button @click="searchContent" icon="ios-search" slot="append"></Button>
</Input>
</FormItem>
<FormItem>
<Button @click="addContent" icon="md-add" type="primary">添加</Button>
</FormItem>
</Form>
<Table :columns="columns" :data="tableData" border></Table>
</Card>
</template>
<script>
export default {
name: 'KeepAliveContentList',
components: {},
props: {},
data() {
return {
sourceData: [
{ name: '张三' },
{ name: '李四' },
{ name: '王二' },
{ name: '唐三' },
{ name: '赵大' },
{ name: '李二' }
],
columns: [
{
title: '名称',
key: 'name'
}
],
tableData: [],
searchString: ''
};
},
mounted() {
if (this.$route.query.data) {
this.sourceData = [{ name: this.$route.query.data }, ...this.sourceData];
}
this.searchContent();
},
methods: {
searchContent() {
this.tableData = [];
this.sourceData.forEach(val => {
if (
val.name.indexOf(this.searchString) !== -1 ||
this.searchString == ''
) {
this.tableData.push(val);
}
});
},
addContent() {
this.$router.push({
path: '/keep-alive/add-content',
query: {}
});
}
}
};
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,289 @@
// 离子波浪
export const lonWave = () => {
var starlings = function (n, r, t, o, e, u, i, f) {
var a = f.onSetup
void 0 === a && (a = null)
var v = f.onRepeat
void 0 === v && (v = null)
var c = f.modifier
void 0 === c && (c = null)
var l = f.perspective
void 0 === l && (l = 1)
var d = f.pixelRatio
void 0 === d && (d = 1)
var m = f.triangles
void 0 === m && (m = !1)
var s
var p
var y = r.length
var w = function (n, r) {
let t = s.createShader(n)
return s.shaderSource(t, r), s.compileShader(t), t
}
var b = function () {
for (var n = 0; n < o.length; n += 1) {
for (var r = s.createBuffer(), e = o[n], u = e.data(0, 0).length, i = new Float32Array(t * y * u), f = 0; f < t; f += 1) {
for (var a = e.data(f, t), v = f * y * u, l = 0; l < y; l += 1) {
for (var d = 0; d < u; d += 1) {
c !== null && e.name === c.attribute ? i[v] = c.value(i[v], a, d, l) : i[v] = a[d]
v += 1
}
}
}
s.bindBuffer(s.ARRAY_BUFFER, r)
s.bufferData(s.ARRAY_BUFFER, i, s.STATIC_DRAW)
var m = s.getAttribLocation(p, o[n].name)
s.enableVertexAttribArray(m)
s.vertexAttribPointer(m, u, s.FLOAT, !1, !1, 0, 0)
}
}
var A = function () {
e.push({
name: 'uMVP',
type: 'mat4'
})
for (var n = 0; n < e.length; n += 1) {
var r = s.getUniformLocation(p, e[n].name)
e[n].location = r
}
}
var F = {
float: function (n, r) {
return s.uniform1f(n, r)
},
vec2: function (n, r) {
return s.uniform2fv(n, r)
},
vec3: function (n, r) {
return s.uniform3fv(n, r)
},
vec4: function (n, r) {
return s.uniform4fv(n, r)
},
mat2: function (n, r) {
return s.uniformMatrix2fv(n, !1, r)
},
mat3: function (n, r) {
return s.uniformMatrix3fv(n, !1, r)
},
mat4: function (n, r) {
return s.uniformMatrix4fv(n, !1, r)
}
}
var g = function () {
s.clear(16640)
s.useProgram(p)
v !== null && v(s, p, e)
for (var n = 0; n < e.length; n += 1) F[e[n].type](e[n].location, e[n].value)
s.drawArrays(m ? s.TRIANGLES : s.POINTS, 0, y * t)
requestAnimationFrame(g)
}
var h = function () {
n.width = n.clientWidth * d
n.height = n.clientHeight * d
var r = s.drawingBufferWidth
var t = s.drawingBufferHeight
s.viewport(0, 0, r, t)
e[e.length - 1].value = [l / (r / t), 0, 0, 0, 0, l, 0, 0, 0, 0, -1, -1, 0, 0, 1, 1]
}
s = n.getContext('webgl')
p = s.createProgram()
s.attachShader(p, w(s.VERTEX_SHADER, u))
s.attachShader(p, w(s.FRAGMENT_SHADER, i))
s.linkProgram(p)
A()
h()
b()
a !== null && a(s)
g()
window.addEventListener('resize', h, !1)
}
// Do you like rainbow waves?
var rainbow = false
// Need more performance?
var HD = true
var canvas = document.getElementById('canvas')
var background = document.querySelector('.background')
var bar = document.querySelector('.progress')
var initialize = function initialize (vertices) {
var pixelRatio = HD ? window.devicePixelRatio : 1
var rows = HD ? 90 : 90
var multiplier = rows * rows
var duration = 0.4
var geometry = [{
x: 0,
y: 0,
z: 0
}]
var pointSize = (HD ? 6 : 2).toFixed(1)
var step = 0.004
var size = 5
var attributes = [{
name: 'aPositionStart',
data: function data (i, total) {
return [size - (i % rows / rows + 0.5 / rows) * (size * 2), -1, (size - (Math.floor(i / rows) / rows + 0.5 / rows) * size * 2) * -1]
}
},
{
name: 'aControlPointOne',
data: function data (i) {
return [size - (i % rows / rows + 0.5 / rows) * (size * 2), -0.5 + getRandom(0.2), (size - (Math.floor(i / rows) / rows + 0.5 / rows) * size * 2) * -1]
}
},
{
name: 'aControlPointTwo',
data: function data (i) {
return [size - (i % rows / rows + 0.5 / rows) * (size * 2), -0.5 + getRandom(0.2), (size - (Math.floor(i / rows) / rows + 0.5 / rows) * size * 2) * -1]
}
},
{
name: 'aPositionEnd',
data: function data (i) {
return [size - (i % rows / rows + 0.5 / rows) * (size * 2), -1, (size - (Math.floor(i / rows) / rows + 0.5 / rows) * size * 2) * -1]
}
},
{
name: 'aOffset',
data: function data (i) {
return [i * ((1 - duration) / (multiplier - 1))]
}
},
{
name: 'aColor',
data: function data (i, total) {
return getHSL(rainbow ? i / total * 1.0 : 0.5 + i / total * 0.4, 0.5, 0.5)
}
}]
var uniforms = [{
name: 'uProgress',
type: 'float',
value: 0.8
}]
var vertexShader = '\n attribute vec3 aPositionStart;\n attribute vec3 aControlPointOne;\n attribute vec3 aControlPointTwo;\n attribute vec3 aPositionEnd;\n attribute float aOffset;\n attribute vec3 aColor;\n\n uniform float uProgress;\n uniform mat4 uMVP;\n\n varying vec3 vColor;\n\n vec3 bezier4(vec3 a, vec3 b, vec3 c, vec3 d, float t) {\n return mix(mix(mix(a, b, t), mix(b, c, t), t), mix(mix(b, c, t), mix(c, d, t), t), t);\n }\n\n float easeInOutQuint(float t){\n return t < 0.5 ? 16.0 * t * t * t * t * t : 1.0 + 16.0 * (--t) * t * t * t * t;\n }\n\n void main () {\n float tProgress = easeInOutQuint(min(1.0, max(0.0, (uProgress - aOffset)) / ' + duration + '));\n vec3 newPosition = bezier4(aPositionStart, aControlPointOne, aControlPointTwo, aPositionEnd, tProgress);\n gl_PointSize = ' + pointSize + ' + ((newPosition.y + 1.0) * 80.0);\n gl_Position = uMVP * vec4(newPosition, 1.0);\n vColor = aColor;\n }\n'
var fragmentShader = '\n precision mediump float;\n\n varying vec3 vColor;\n\n void main() {\n vec2 pc = 2.0 * gl_PointCoord - 1.0;\n gl_FragColor = vec4(vColor, 1.0 - dot(pc, pc));\n }\n'
var onSetup = function onSetup (gl) {
gl.blendFunc(gl.SRC_ALPHA, gl.ONE)
gl.enable(gl.BLEND)
}
var onRepeat = function onRepeat () {
rotateY(uniforms[uniforms.length - 1].value, 0.002)
if (uniforms[0].value < 0) {
uniforms[0].value = 1
}
uniforms[0].value -= step
}
var options = {
onSetup: onSetup,
onRepeat: onRepeat,
pixelRatio: pixelRatio
}
starlings(canvas, geometry, multiplier, attributes, uniforms, vertexShader, fragmentShader, options)
}
var getRandom = function getRandom (value) {
return Math.random() * value - value / 2
}
var rotateY = function rotateY (matrix, angle) {
var sin = Math.sin(angle)
var cos = Math.cos(angle)
var clone = JSON.parse(JSON.stringify(matrix))
matrix[0] = clone[0] * cos - clone[8] * sin
matrix[1] = clone[1] * cos - clone[9] * sin
matrix[2] = clone[2] * cos - clone[10] * sin
matrix[3] = clone[3] * cos - clone[11] * sin
matrix[8] = clone[0] * sin + clone[8] * cos
matrix[9] = clone[1] * sin + clone[9] * cos
matrix[10] = clone[2] * sin + clone[10] * cos
matrix[11] = clone[3] * sin + clone[11] * cos
}
var h2r = function h2r (p, q, t) {
if (t < 0) t += 1
if (t > 1) t -= 1
if (t < 1 / 6) return p + (q - p) * 6 * t
if (t < 1 / 2) return q
if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t)
return p
}
var getHSL = function getHSL (h, s, l) {
h = (h % 1 + 1) % 1
s = Math.max(0, Math.min(1, s))
l = Math.max(0, Math.min(1, l))
if (s === 0) return [l, l, l]
var p = l <= 0.5 ? l * (1 + s) : l + s - l * s
var q = 2 * l - p
return [h2r(q, p, h + 1 / 3), h2r(q, p, h), h2r(q, p, h - 1 / 3)]
}
initialize()
}
// 随机线条
export const canvasParticle = (function () {
function getElementByTag (name) { return document.getElementsByTagName(name) }
function getELementById (id) { return document.getElementById(id) }
function canvasInit (canvasConfig) {
canvasConfig = canvasConfig || {}
var html = getElementByTag('html')[0]
var body = document.getElementById('canvasView')
var canvasObj = document.createElement('canvas')
var canvas = { element: canvasObj, points: [], config: { vx: canvasConfig.vx || 4, vy: canvasConfig.vy || 4, height: canvasConfig.height || 2, width: canvasConfig.width || 2, count: canvasConfig.count || 100, color: canvasConfig.color || '121, 162, 185', stroke: canvasConfig.stroke || '130,255,255', dist: canvasConfig.dist || 6000, e_dist: canvasConfig.e_dist || 20000, max_conn: 10 } }; if (canvas.element.getContext('2d')) { canvas.context = canvas.element.getContext('2d') } else { return null }
body.style.padding = '0'; body.style.margin = '0'; body.appendChild(canvas.element); canvas.element.style = 'position: fixed; top: 0; left: 0; z-index: -1;'; canvasSize(canvas.element); window.onresize = function () { canvasSize(canvas.element) }
body.onmousemove = function (e) { var event = e || window.event; canvas.mouse = { x: event.clientX, y: event.clientY } }
document.onmouseleave = function () { canvas.mouse = undefined }
setInterval(function () { drawPoint(canvas) }, 40)
}
function canvasSize (canvas) {
var width = document.getElementById('canvasView').style.width
var height = document.getElementById('canvasView').style.height
width = parseInt(width); height = parseInt(height)
canvas.width = width || window.innerWeight || document.documentElement.clientWidth || document.body.clientWidth; canvas.height = height || window.innerWeight || document.documentElement.clientHeight || document.body.clientHeight
}
function drawPoint (canvas) {
var context = canvas.context
var point
var dist
context.clearRect(0, 0, canvas.element.width, canvas.element.height); context.beginPath(); context.fillStyle = 'rgb(' + canvas.config.color + ')'; for (var i = 0, len = canvas.config.count; i < len; i++) {
if (canvas.points.length != canvas.config.count) { point = { x: Math.floor(Math.random() * canvas.element.width), y: Math.floor(Math.random() * canvas.element.height), vx: canvas.config.vx / 2 - Math.random() * canvas.config.vx, vy: canvas.config.vy / 2 - Math.random() * canvas.config.vy } } else { point = borderPoint(canvas.points[i], canvas) }
context.fillRect(point.x - canvas.config.width / 2, point.y - canvas.config.height / 2, canvas.config.width, canvas.config.height); canvas.points[i] = point
}
drawLine(context, canvas, canvas.mouse); context.closePath()
}
function borderPoint (point, canvas) {
var p = point; if (point.x <= 0 || point.x >= canvas.element.width) { p.vx = -p.vx; p.x += p.vx } else if (point.y <= 0 || point.y >= canvas.element.height) { p.vy = -p.vy; p.y += p.vy } else { p = { x: p.x + p.vx, y: p.y + p.vy, vx: p.vx, vy: p.vy } }
return p
}
function drawLine (context, canvas, mouse) {
let dist
context = context || canvas.context; for (var i = 0, len = canvas.config.count; i < len; i++) {
canvas.points[i].max_conn = 0; for (var j = 0; j < len; j++) {
if (i != j) {
dist = Math.round(canvas.points[i].x - canvas.points[j].x) * Math.round(canvas.points[i].x - canvas.points[j].x) +
Math.round(canvas.points[i].y - canvas.points[j].y) * Math.round(canvas.points[i].y - canvas.points[j].y); if (dist <= canvas.config.dist && canvas.points[i].max_conn < canvas.config.max_conn) {
canvas.points[i].max_conn++; context.lineWidth = 0.5 - dist / canvas.config.dist; context.strokeStyle = 'rgba(' + canvas.config.stroke + ',' + (1 - dist / canvas.config.dist) + ')'
context.beginPath(); context.moveTo(canvas.points[i].x, canvas.points[i].y); context.lineTo(canvas.points[j].x, canvas.points[j].y); context.stroke()
}
}
}
if (mouse) {
dist = Math.round(canvas.points[i].x - mouse.x) * Math.round(canvas.points[i].x - mouse.x) +
Math.round(canvas.points[i].y - mouse.y) * Math.round(canvas.points[i].y - mouse.y); if (dist > canvas.config.dist && dist <= canvas.config.e_dist) { canvas.points[i].x = canvas.points[i].x + (mouse.x - canvas.points[i].x) / 20; canvas.points[i].y = canvas.points[i].y + (mouse.y - canvas.points[i].y) / 20 }
if (dist <= canvas.config.e_dist) { context.lineWidth = 1; context.strokeStyle = 'rgba(' + canvas.config.stroke + ',' + (1 - dist / canvas.config.e_dist) + ')'; context.beginPath(); context.moveTo(canvas.points[i].x, canvas.points[i].y); context.lineTo(mouse.x, mouse.y); context.stroke() }
}
}
}
return canvasInit
})()

View File

@@ -0,0 +1,133 @@
<template>
<div>
<Form :model="formData" :rules="rules" @keydown.enter.native="login" ref="loginForm">
<FormItem prop="loginName">
<Input placeholder="请输入用户名" v-model="formData.loginName"></Input>
</FormItem>
<FormItem prop="loginPwd">
<Input placeholder="请输入密码" type="password" v-model="formData.loginPwd"></Input>
</FormItem>
<FormItem prop="code">
<Input class="code-input" placeholder="请输入验证码" v-model="formData.code"></Input>
<img :src="codeUrl" @click="verificationCode" class="codeUrl" v-if="codeUrl" />
</FormItem>
<FormItem class="remember">
<Checkbox>记住密码</Checkbox>
</FormItem>
<FormItem>
<Button :loading="btnLoading" @click="login" long type="primary">登录</Button>
</FormItem>
<div class="other-way">
<p class="inline" style="float:left">其他方式登陆</p>
<div class="inline align" style="float:right">
<img alt class="marginLeft" src="../../../assets/images/login-taobao.png" />
<img alt class="marginLeft" src="../../../assets/images/login-alipay.png" />
<img alt class="marginLeft" src="../../../assets/images/login-sina.png" />
</div>
</div>
</Form>
</div>
</template>
<script>
import { loginApi } from '@/api/login';
import { PRIVILEGE_TYPE } from '@/constants/login';
export default {
name: 'LoginForm',
props: {
// 登录名规则
loginNameRules: {
type: Array,
default: () => {
return [{ required: true, message: '账号不能为空', trigger: 'blur' }];
}
},
// 密码规则
loginPwdRules: {
type: Array,
default: () => {
return [{ required: true, message: '密码不能为空', trigger: 'blur' }];
}
},
// 验证码规则
codedRules: {
type: Array,
default: () => {
return [{ required: true, message: '验证码不能为空', trigger: 'blur' }];
}
}
},
data() {
return {
// 防止重复提交 按钮加载状态
btnLoading: false,
formData: {
code: '',
codeUuid: '',
loginName: 'sa',
loginPwd: '123456'
},
codeUrl: ''
};
},
computed: {
rules() {
return {
loginName: this.loginNameRules,
loginPwd: this.loginPwdRules,
code: this.codedRules
};
}
},
mounted() {
this.verificationCode();
},
methods: {
// 获取验证码
async verificationCode() {
let result = await loginApi.getVerificationCode();
let datas = result.data;
this.formData.codeUuid = datas.uuid;
this.codeUrl = datas.code;
},
// 登录
login() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loginSuccess();
}
});
},
// 登录 - 异步
async loginSuccess() {
try {
this.btnLoading = true;
let loginResult = await loginApi.login(this.formData);
let loginInfo = loginResult.data;
localStorage.clear();
this.$store.commit('setToken', loginInfo.xaccessToken);
// 保存用户登录
this.$store.commit('setUserLoginInfo', {
id: loginInfo.id,
loginName: loginInfo.loginName,
nickName: loginInfo.nickName,
actualName: loginInfo.actualName,
phone: loginInfo.phone,
isSuperMan: loginInfo.isSuperMan
});
//设置权限
this.$store.commit('setUserPrivilege', loginInfo.privilegeList);
this.btnLoading = false;
// 跳转到首页
this.$router.push({
name: this.$config.homeName
});
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.btnLoading = false;
this.verificationCode();
}
}
}
};
</script>

View File

@@ -0,0 +1,128 @@
.center {
text-align: center;
}
.login {
font-family: Arial, "PingFang SC", "Microsoft YaHei";
width: 100%;
height: 100%;
background: url(../../assets/images/login-bg.jpg) no-repeat fixed;
background-size: cover;
.content {
width: 424px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -56%)
}
.ivu-card {
background: rgba(255, 255, 255, 0.95);
border-radius: 10px;
box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.3);
}
.form-con {
margin: 32px 0;
width: 424px;
background: #fff;
padding: 32px 32px;
border-radius: 0 8px 8px 0;
.ivu-input {
border: 1px solid #E8E8EE;
border-radius: 4px;
font-size: 16px;
color: #525252;
padding: 0 20px;
&:focus {
border: 1px solid #0097F6;
box-shadow: none;
}
}
.ivu-form-item-error-tip {
height: 30px;
line-height: 30px;
padding: 0;
color: #f66;
top:90%;
}
.ivu-form-item {
margin-bottom: 22px;
}
.remember {
margin: -10px 0 10px;
}
.ivu-input,
.ivu-btn {
height: 48px;
}
.ivu-btn {
font-size: 16px;
}
.ivu-input-group-prepend {
padding: 4px 15px;
}
.code-input {
width: 172px;
}
.codeUrl {
height: 80%;
position: absolute;
z-index: 3;
top: 0;
bottom: 0;
margin: auto;
right: 24px;
border-radius: 0 4px 4px 0;
cursor: pointer;
}
}
.login-tip {
font-size: 10px;
text-align: center;
color: #c3c3c3;
}
.footerDesc {
font-family: "MicrosoftYaHei";
color: #A6A6A8;
font-size: 14px;
}
.otherWay {
font-size: 14px;
font-family: "Microsoft YaHei";
.inline {
display: inline-block;
}
.align {
vertical-align: middle;
}
.marginLeft {
margin-left: 20px;
float:right;
}
}
.remember {
font-size: 14px;
}
}
.draw {
position: fixed;
width: 1px;
z-index: 99999;
line-height: 1px;
pointer-events: none;
}
@keyframes floatOne {
0% {
opacity: 1;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
transform: translate3D(0, -20px, 0) scale(5) rotate(45deg);
}
}

View File

@@ -0,0 +1,52 @@
<style lang="less">
@import './login.less';
</style>
<template>
<div class="login">
<div class="content">
<header class="logo center">
<img alt
src="../../assets/images/login-logo.png" />
</header>
<div class="form-con">
<login-form @on-success-valid="handleSubmit"></login-form>
</div>
<footer class="center footerDesc">SmartAdmin 由1024创新实验室强力驱动</footer>
</div>
</div>
</template>
<script>
import LoginForm from './components/login-form';
import { mapActions } from 'vuex';
import $ from 'jquery';
import { lonWave, canvasParticle } from './canvas';
export default {
name: 'login',
props: {},
components: {
LoginForm
},
data () {
return {};
},
computed: {},
watch: {},
filters: {},
created () {},
mounted () {
this.$Spin.hide();
},
methods: {
...mapActions(['handleLogin']),
// 提交
handleSubmit (params) {
this.handleLogin(params).then(res => {
this.$router.push({
name: this.$config.homeName
});
});
}
}
};
</script>

View File

@@ -0,0 +1,152 @@
<template>
<div>
<Card class="warp-card" dis-hover>
<Form class="tools" inline v-privilege="'online-user-search'">
<FormItem>
<Input placeholder="请输入用户姓名" v-model="searchData.actualName">
<Button @click="getOnlineUserList" icon="ios-search" slot="append"></Button>
</Input>
</FormItem>
<FormItem>
<Button @click="refresh" type="primary">重置</Button>
</FormItem>
</Form>
<p class="online-num">
当前在线人数:
<span>
<count-to :end="pageTotal" />
</span>
</p>
<Table :columns="columns" :data="data" :loading="loading"></Table>
<Page
:current="pageNum"
:page-size="pageSize"
:page-size-opts="[10, 20, 30, 50, 100]"
:total="pageTotal"
@on-change="changePage"
@on-page-size-change="changePageSize"
show-elevator
show-sizer
show-total
style="margin:24px 0;text-align:right;"
></Page>
</Card>
</div>
</template>
<script>
import { onlineUserApi } from '@/api/online-user';
import CountTo from '_c/count-to';
export default {
name: 'OnlineUser',
props: {},
components: {
CountTo
},
data() {
return {
searchData: {
actualName: ''
},
modalType: 'add',
editModal: false,
addModal: false,
loading: true,
pageNum: 1,
pageSize: 10,
pageTotal: 0,
formData: {
title: '',
content: ''
},
// table表头
columns: [
{
title: 'ID',
key: 'id',
width: 60,
align: 'center'
},
{
title: '姓名',
key: 'actualName'
},
{
title: '登录账户',
key: 'loginName'
},
{
title: '手机号',
key: 'phone'
},
{
title: '部门',
key: 'departmentName'
}
],
// table数据
data: []
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
this.getOnlineUserList();
},
methods: {
// 获取在线用户集合
async getOnlineUserList() {
this.loading = true;
try {
let result = await onlineUserApi.getOnlineUserList({
...this.searchData,
pageNum: this.pageNum,
pageSize: this.pageSize
});
this.loading = false;
this.data = result.data.list;
this.pageTotal = result.data.total;
this.pageNum = result.data.pageNum;
this.pageSize = result.data.pageSize;
} catch (e) {
this.loading = true;
//TODO zhuoda sentry
console.error(e);
}
},
// 分页
changePage(pageNum) {
this.pageNum = pageNum;
this.getOnlineUserList();
},
changePageSize(pageSize) {
this.pageNum = 1;
this.pageSize = pageSize;
this.getOnlineUserList();
},
refresh() {
this.pageNum = 1;
this.searchData.actualName = '';
this.getOnlineUserList();
}
}
};
</script>
<style lang="less" scoped>
.online-num {
display: flex;
margin: 10px 0;
line-height: 0;
font-size: 15px;
padding: 10px 0;
span {
font-size: 18px;
color: #2d8cf0;
padding: 0 10px;
}
}
</style>

View File

@@ -0,0 +1,33 @@
<template>
<div>
<iframe :src="url" frameborder="0" height="800" scrolling="yes" width="100%"></iframe>
</div>
</template>
<script>
import config from '@/config';
const baseUrl = config.baseUrl.apiUrl;
export default {
name: 'Sql',
components: {},
props: {},
data() {
return {
url: baseUrl + '/druidMonitor'
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {}
};
</script>

View File

@@ -0,0 +1,397 @@
<template>
<div>
<Card class="warp-card" dis-hover>
<Form class="tools" inline>
<FormItem v-privilege="'notice-query'">
<Input placeholder="请输入消息标题" v-model="searchData.title"></Input>
</FormItem>
<FormItem>
<ButtonGroup>
<Button
@click="getNoticeList"
icon="ios-search"
type="primary"
v-privilege="'notice-query'"
>查询</Button>
<Button
@click="refresh"
icon="md-refresh"
type="default"
v-privilege="'notice-query'"
>重置</Button>
</ButtonGroup>
</FormItem>
<FormItem>
<Button
@click="openModal('add')"
icon="md-add"
type="primary"
v-privilege="'notice-add'"
>添加消息</Button>
</FormItem>
</Form>
<Table :columns="columns" :data="data" :loading="loading" border></Table>
<Page
:current="pageNum"
:page-size="pageSize"
:page-size-opts="[10, 20, 30, 50, 100]"
:total="pageTotal"
@on-change="changePage"
@on-page-size-change="changePageSize"
show-elevator
show-sizer
show-total
style="margin:24px 0;text-align:right;"
></Page>
</Card>
<!-- 添加修改消息 -->
<Modal
:loading="saveLoading"
:title="modalType=='add'?'添加':'修改'"
@on-cancel="cancelSave"
@on-ok="addOrUpdateNotice"
v-model="editModal"
>
<Form :label-width="80" :model="formData" :rules="formValidate" ref="formRef">
<FormItem label="标题" prop="title">
<Input placeholder="请输入消息标题" v-model="formData.title"></Input>
</FormItem>
<FormItem label="内容" prop="content">
<Input
:autosize="{minRows: 2,maxRows: 5}"
placeholder="请输入消息内容"
type="textarea"
v-model="formData.content"
></Input>
</FormItem>
</Form>
<!-- 添加修改消息 -->
<!-- 消息详情 -->
</Modal>
<Modal
:loading="saveLoading"
:title="formData.title"
class="detail-modal"
v-model="detailModal"
>
<div class="detail">{{formData.content}}</div>
<p class="time">{{formData.updateTime}}</p>
<div slot="footer">
<Button @click="cancelSave" size="large" type="primary">知道了</Button>
</div>
</Modal>
<!-- 消息详情 -->
</div>
</template>
<script>
import { noticeApi } from '@/api/notice';
import { NOTICE_STATUS } from '@/constants/notice';
export default {
name: 'NoticeList',
components: {},
props: {},
data() {
return {
detailModal: false,
searchData: {
title: ''
},
modalType: 'add',
editModal: false,
addModal: false,
loading: true,
saveLoading: true,
updateLoading: true,
pageNum: 1,
pageSize: 10,
pageTotal: 0,
updateItem: {},
saveItem: {},
formData: {
title: '',
content: ''
},
// table表头
columns: [
{
title: 'ID',
width: 60,
key: 'id'
},
{
title: '消息标题',
key: 'title'
},
{
title: '创建时间',
key: 'createTime'
},
{
title: '消息创建人',
key: 'createUserName'
},
{
title: '是否发送',
key: 'sendStatus',
render: (h, params) => {
return h(
'span',
this.$enum.getDescByValue('NOTICE_STATUS', params.row.sendStatus)
);
}
},
{
title: '操作',
key: 'action',
align: 'center',
width: 200,
className: 'action-hide',
render: (h, params) => {
let btns = [
{
title: '详情',
directives: [
{
name: 'privilege',
value: 'notice-detail'
}
],
action: () => {
this.openModal('detail', params.row);
}
},
{
title: '删除',
directives: [
{
name: 'privilege',
value: 'notice-delete'
}
],
action: () => {
this.$Modal.confirm({
title: '删除',
content: '确认删除该条消息么?',
onOk: () => {
this.deleteNotice(params.row.id);
}
});
}
}
];
let editBtn = {
title: '编辑',
directives: [
{
name: 'privilege',
value: 'notice-edit'
}
],
action: () => {
this.openModal('edit', params.row);
}
};
let sendBtn = {
title: '发送',
directives: [
{
name: 'privilege',
value: 'notice-send'
}
],
action: () => {
this.$Modal.confirm({
title: '确认',
content: '确认发送条消息么?',
onOk: () => {
this.sendNotice(params.row.id);
}
});
}
};
if (params.row.sendStatus != NOTICE_STATUS['YES'].value) {
btns.push(editBtn);
}
if (params.row.sendStatus != NOTICE_STATUS['YES'].value) {
btns.push(sendBtn);
}
return this.$tableAction(h, btns);
}
}
],
// table数据
data: [],
formValidate: {
title: [{ required: true, message: '请输入消息标题', trigger: 'blur' }],
content: [
{ required: true, message: '请输入消息内容', trigger: 'blur' }
]
}
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
this.getNoticeList();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 查询消息列表
async getNoticeList() {
try {
this.loading = true;
let result = await noticeApi.getNoticeList({
...this.searchData,
pageNum: this.pageNum,
pageSize: this.pageSize
});
this.loading = false;
this.data = result.data.list;
this.pageTotal = result.data.total;
this.pageNum = result.data.pageNum;
this.pageSize = result.data.pageSize;
} catch (e) {
//TODO zhuoda sentry
console.error(e);
}
},
// 获取通知详情
async getNoticeDetail(id) {
try {
this.loading = true;
let result = await noticeApi.getNoticeDetail(id);
this.loading = false;
this.formData = result.data;
} catch (e) {
this.loading = true;
//TODO zhuoda sentry
console.error(e);
}
},
// 添加消息
openModal(type, data) {
this.modalType = type;
switch (type) {
case 'add':
this.formData = {};
this.editModal = true;
break;
case 'edit':
this.getNoticeDetail(data.id);
this.editModal = true;
break;
case 'detail':
this.getNoticeDetail(data.id);
this.detailModal = true;
break;
}
},
// 分页
changePage(pageNum) {
this.pageNum = pageNum;
this.getNoticeList();
},
// 更改分页查询条数
changePageSize(pageSize) {
this.pageNum = 1;
this.pageSize = pageSize;
this.getNoticeList();
},
// 删除通知
async deleteNotice(id) {
this.$Spin.show();
let result = await noticeApi.deleteNotice(id);
this.$Spin.hide();
this.$Message.success('删除消息成功!');
this.getNoticeList();
},
// 发送消息
async sendNotice(id) {
this.$Spin.show();
let result = await noticeApi.sendNotice(id);
this.$Message.success('发送消息成功!');
this.$Spin.hide();
this.getNoticeList();
},
// 保存任务
addOrUpdateNotice() {
try {
this.$refs['formRef'].validate(valid => {
if (valid) {
if (this.modalType == 'add') {
this.addNotice();
} else {
this.editNotice();
}
}
});
} catch (e) {
//TODO zhuoda sentry
console.error(e);
}
},
// 修改消息
async editNotice() {
try {
this.loading = true;
let result = await noticeApi.updateNotice(this.formData);
this.$Message.success('修改成功');
this.editModal = false;
this.getNoticeList();
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.loading = false;
}
},
// 保存消息
async addNotice() {
try {
this.loading = true;
let result = await noticeApi.addNotice(this.formData);
this.$Message.success('添加成功');
this.editModal = false;
this.getNoticeList();
this.loading = false;
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.loading = false;
}
},
// 取消添加/修改
cancelSave() {
this.editModal = false;
this.detailModal = false;
this.$refs['formRef'].resetFields();
},
// 重置
refresh() {
this.pageNum = 1;
this.searchData.title = '';
this.getNoticeList();
}
}
};
</script>
<style lang="less" scoped>
.detail-modal {
.detail {
margin-bottom: 20px;
}
.time {
text-align: right;
color: #999;
}
}
</style>

View File

@@ -0,0 +1,221 @@
<template>
<div>
<Card class="warp-card" dis-hover>
<Form class="tools" inline>
<FormItem v-privilege="'person-notice-query'">
<Input placeholder="请输入消息标题" v-model="searchData.title">
<Button @click="getPersonNoticeList" icon="ios-search" slot="append"></Button>
</Input>
</FormItem>
<FormItem>
<Button
@click="refresh"
icon="md-refresh"
type="primary"
v-privilege="'person-notice-query'"
>重置</Button>
</FormItem>
</Form>
<Table :columns="columns" :data="data" :loading="loading" border></Table>
<Page
:current="pageNum"
:page-size="pageSize"
:page-size-opts="[10, 20, 30, 50, 100]"
:total="pageTotal"
@on-change="changePage"
@on-page-size-change="changePageSize"
show-elevator
show-sizer
show-total
style="margin:24px 0;text-align:right;"
></Page>
</Card>
<Modal :loading="saveLoading" :title="formData.title" class="detail-modal" v-model="editModal">
<div class="detail">{{formData.content}}</div>
<p class="time">{{formData.updateTime}}</p>
<div slot="footer">
<Button @click="cancelSave" size="large" type="primary">知道了</Button>
</div>
</Modal>
</div>
</template>
<script>
import { noticeApi } from '@/api/notice';
export default {
name: 'PersonNotice',
components: {},
props: {},
data() {
return {
searchData: {
title: ''
},
modalType: 'add',
editModal: false,
addModal: false,
loading: true,
saveLoading: true,
updateLoading: true,
pageNum: 1,
pageSize: 10,
pageTotal: 0,
updateItem: {},
saveItem: {},
formData: {
title: '',
content: ''
},
// table表头
columns: [
{
title: 'ID',
width: 60,
key: 'id'
},
{
title: '消息标题',
key: 'title'
},
{
title: '创建时间',
key: 'receiveTime'
},
{
title: '消息创建人',
key: 'createUserName'
},
{
title: '状态',
key: 'createUser',
render: (h, params) => {
return h('span', params.row.readStatus ? '已读' : '未读');
}
},
{
title: '操作',
key: 'action',
align: 'center',
width: 200,
className: 'action-hide',
render: (h, params) => {
return this.$tableAction(h, [
{
title: '查看',
directives: [
{
name: 'privilege',
value: 'person-notice-detail'
}
],
action: () => {
this.getNoticeDetail(params.row.id);
}
}
]);
}
}
],
// table数据
data: [],
formValidate: {
title: [{ required: true, message: '请输入消息标题', trigger: 'blur' }],
content: [
{ required: true, message: '请输入消息内容', trigger: 'blur' }
]
}
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
this.getPersonNoticeList();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 查询个人消息列表
async getPersonNoticeList() {
try {
this.loading = true;
let result = await noticeApi.getPersonNoticeList({
...this.searchData,
pageNum: this.pageNum,
pageSize: this.pageSize
});
this.loading = false;
this.data = result.data.list;
this.pageTotal = result.data.total;
this.pageNum = result.data.pageNum;
this.pageSize = result.data.pageSize;
} catch (e) {
//TODO zhuoda sentry
this.loading = false;
console.error(e);
}
},
// 获取通知详情
async getNoticeDetail(id) {
try {
this.loading = true;
let result = await noticeApi.getNoticeDetail(id);
this.loading = false;
this.formData = result.data;
this.openModal(result.data);
} catch (e) {
//TODO zhuoda sentry
this.loading = false;
console.error(e);
}
},
// 标记已读
async openModal(data) {
this.editModal = true;
this.$Spin.show();
let result = await noticeApi.addNoticeRecord(data.id);
this.$Spin.hide();
},
// 分页
changePage(pageNum) {
this.pageNum = pageNum;
this.getPersonNoticeList();
},
// 更改分页查询条数
changePageSize(pageSize) {
this.pageNum = 1;
this.pageSize = pageSize;
this.getPersonNoticeList();
},
// 重置
refresh() {
this.pageNum = 1;
this.searchData.title = '';
this.getPersonNoticeList();
},
// 关闭详情
cancelSave() {
this.editModal = false;
this.getPersonNoticeList();
}
}
};
</script>
<style lang="less" scoped>
.detail-modal {
.detail {
margin-bottom: 20px;
}
.time {
text-align: right;
color: #999;
}
}
</style>

View File

@@ -0,0 +1,273 @@
<template>
<Card class="warp-card" dis-hover>
<Form class="tools" inline>
<FormItem>
<Button
@click="getSmartReloadList"
icon="md-refresh"
type="primary"
v-privilege="'smart-reload-search'"
>刷新</Button>
</FormItem>
</Form>
<Alert>
<h3>SmartReload介绍</h3>
<pre>
简介SmartReload是一个可以在不重启进程的情况下动态重新加载配置或者执行某些预先设置的代码
原理
- Java后端会在项目启动的时候开启一个Daemon线程这个Daemon线程会每隔几秒轮询t_smart_item表的状态
- 如果状态标识上次状态标识比较发生变化会将参数传入SmartReload实现类进行自定义操作
用途
· 用于刷新内存中的缓存
· 用于执行某些后门代码
· 用于进行Java热加载前提是类结构不发生变化
· 其他不能重启服务的应用
</pre>
</Alert>
<Table :columns="columns" :data="tableData" :loading="tableLoading" border></Table>
<Modal
:closable="false"
:mask-closable="false"
@on-cancel="editModal=false"
@on-ok="updateSmartReloadData"
title="编辑"
v-model="editModal"
>
<Form :label-width="80" :model="formData">
<FormItem label="标签">
<Input disabled="disabled" v-model="formData.tag" />
</FormItem>
<FormItem label="状态标识">
<Input required v-model="formData.identification" />
</FormItem>
<FormItem label="Reload参数(非必填)">
<Input
:autosize="{minRows: 2,maxRows: 5}"
placeholder="传入SmartReload的参数"
type="textarea"
v-model="formData.args"
/>
</FormItem>
</Form>
</Modal>
<Modal
:closable="false"
:mask-closable="false"
title="执行结果"
v-model="showResultModal"
width="1000px"
>
<Table :columns="resultColumns" :data="resultData" height="500"></Table>
<div slot="footer">
<Button @click="showResultModal=false" type="primary">关闭</Button>
</div>
</Modal>
</Card>
</template>
<script>
import { smartReloadApi } from '@/api/smart-reload';
export default {
name: 'SmartReloadList',
components: {},
props: {},
data() {
return {
//表格loading
tableLoading: false,
//reload结果弹出框
showResultModal: false,
// 修改弹窗
editModal: false,
formData: {
tag: '',
identification: 0,
args: ''
},
tableData: [],
columns: [
{
title: '标签',
key: 'tag'
},
{
title: '状态标识',
key: 'identification'
},
{
title: '更新时间',
key: 'updateTime'
},
{
title: '创建时间',
key: 'createTime'
},
{
title: '操作',
width: 200,
align: 'center',
render: (h, param) => {
return this.$tableAction(h, [
{
title: '执行Reload',
directives: [
{
name: 'privilege',
value: 'smart-reload-update'
}
],
action: () => {
this.editModal = true;
this.formData.tag = param.row.tag;
this.formData.identification = param.row.identification;
}
},
{
title: '查看结果',
directives: [
{
name: 'privilege',
value: 'smart-reload-result'
}
],
action: () => {
this.getSmartReloadResult(param.row.tag);
}
}
]);
s;
}
}
],
resultData: [],
resultColumns: [
{
type: 'expand',
width: 50,
render: (h, params) => {
return h('pre', {}, params.row.exception);
}
},
{
title: '标签',
key: 'tag',
width: 150
},
{
title: '状态标识',
key: 'identification'
},
{
title: '参数',
key: 'args',
width: 100
},
{
title: '结果',
key: 'result',
render: (h, params) => {
if (params.row.result) {
return h(
'span',
{
style: {
color: '#19be6b'
}
},
'成功'
);
} else {
return h(
'span',
{
style: {
color: '#ed4014'
}
},
'失败'
);
}
}
},
{
title: '异常信息',
key: 'exception',
width: 200,
render: (h, params) => {
if (params.row.result) {
return h(
'span',
{
style: {
color: '#19be6b'
}
},
'无异常信息'
);
} else {
return h(
'span',
{
style: {
color: '#ed4014'
}
},
'有异常信息请点开Expand'
);
}
}
},
{
title: '创建时间',
key: 'createTime',
width: 180
}
]
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
this.getSmartReloadList();
},
methods: {
// 查询
async getSmartReloadList() {
try {
this.tableLoading = true;
let result = await smartReloadApi.getSmartReloadList();
this.tableData = result.data;
this.tableLoading = false;
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.tableLoading = false;
}
},
// 修改
async updateSmartReloadData() {
if (this.formData.identification) {
this.$Spin.show();
let result = await smartReloadApi.updateSmartReloadData(this.formData);
this.$Spin.hide();
this.$Message.success('更新成功');
this.formData = {};
this.getSmartReloadList();
} else {
this.$Message.error('状态标示不能为空');
this.show = true;
}
},
// 获取执行结果
async getSmartReloadResult(tag) {
this.$Spin.show();
this.showResultModal = true;
let result = await smartReloadApi.getSmartReloadResult(tag);
this.$Spin.hide();
this.resultData = result.data;
}
}
};
</script>

View File

@@ -0,0 +1,397 @@
<template>
<div>
<Card class="warp-card" dis-hover>
<Form class="tools" inline>
<FormItem>
<Input placeholder="请输入" v-model="searchValue" v-privilege="'system-params-search'">
<Select slot="prepend" style="width: 120px" v-model="searchType">
<Option value="BY_KEY">按key</Option>
<Option value="BY_GROUP">按参数类别</Option>
</Select>
</Input>
</FormItem>
<FormItem>
<ButtonGroup>
<Button
@click="searchSystemConfigList"
icon="ios-search"
type="primary"
v-privilege="'system-params-search'"
>查询</Button>
<Button
@click="refresh"
icon="md-refresh"
type="default"
v-privilege="'system-params-search'"
>重置</Button>
</ButtonGroup>
</FormItem>
<FormItem>
<Button
@click="addModal=true"
icon="md-add"
type="primary"
v-privilege="'system-params-add'"
>添加</Button>
</FormItem>
</Form>
<Table
:columns="columns"
:data="data"
:loading="loading"
:pageNumber="pageNum"
:pageShow="pageShow"
border
></Table>
<Page
:current="pageNum"
:page-size="pageSize"
:total="pageTotal"
@on-change="changePage"
@on-page-size-change="changePageSize"
show-elevator
show-total
style="margin:24px 0;text-align:right;"
/>
</Card>
<Modal @on-cancel="cancelUpdate" @on-ok="handleUpdate" title="编辑" v-model="editModal">
<Form :label-width="80" :model="updateItem" :rules="updateValidate" ref="updateRef">
<FormItem label="Key">
<Input disabled="disabled" v-model="updateItem.configKey"></Input>
</FormItem>
<FormItem label="标题">
<Input disabled="disabled" type="textarea" v-model="updateItem.configName" />
</FormItem>
<FormItem label="参数类别" prop="configGroup">
<Input disabled="disabled" placeholder="请输入数据类型(必填)" v-model="updateItem.configGroup" />
</FormItem>
<FormItem label="值" prop="configValue">
<Input
:autosize="{minRows: 5}"
placeholder="请输入值(必填)"
type="textarea"
v-model="updateItem.configValue"
></Input>
</FormItem>
<FormItem label="备注" prop="remark">
<Input
:autosize="{minRows: 4}"
placeholder="请输入备注"
type="textarea"
v-model="updateItem.remark "
></Input>
</FormItem>
</Form>
<div slot="footer">
<Button @click="cancelUpdate" type="text">取消</Button>
<Button @click="handleUpdate" type="primary">确定</Button>
</div>
</Modal>
<Modal title="添加" v-model="addModal">
<Form :label-width="80" :model="saveItem" :rules="saveValidate" ref="saveRef">
<FormItem label="Key" prop="configKey">
<Input placeholder="请输入key(必填)" v-model="saveItem.configKey" />
</FormItem>
<FormItem label="标题" prop="configName">
<Input placeholder="请输入标题(必填)" v-model="saveItem.configName" />
</FormItem>
<FormItem label="参数类别" prop="configGroup">
<Input placeholder="请输入数据类型(必填)" v-model="saveItem.configGroup" />
</FormItem>
<FormItem label="值" prop="configValue">
<Input
:autosize="{minRows: 5}"
placeholder="请输入值(必填)"
type="textarea"
v-model="saveItem.configValue"
></Input>
</FormItem>
<FormItem label="备注" prop="remark">
<Input
:autosize="{minRows: 4}"
placeholder="请输入备注"
type="textarea"
v-model="saveItem.remark "
></Input>
</FormItem>
</Form>
<div slot="footer">
<Button @click="cancelSave" type="text">取消</Button>
<Button @click="handleSave" type="primary">确定</Button>
</div>
</Modal>
<Modal :closable="false" :mask-closable="false" title="值查看" v-model="valueModal">
<json-viewer :expand-depth="10" :value="valueData" boxed copyable sort></json-viewer>
<div slot="footer">
<Button @click="cancelValueModal" type="primary">关闭</Button>
</div>
</Modal>
</div>
</template>
<script>
import { systemConfigApi } from '@/api/system-config';
export default {
name: 'SystemConfig',
components: {},
props: {},
data() {
return {
pageShow: true,
searchValue: '',
searchType: 'BY_KEY',
editModal: false,
addModal: false,
valueModal: false,
// table是否loading
loading: true,
saveLoading: true,
updateLoading: true,
pageNum: 1,
pageSize: 10,
pageTotal: 0,
updateItem: {
id: 0,
configKey: '',
configName: '',
configValue: '',
remark: ''
},
saveItem: {
configKey: '',
configName: '',
configValue: '',
configGroup: '',
remark: ''
},
valueData: '',
// table表头
columns: [
{
title: 'ID',
key: 'id',
width: 60
},
{
title: 'Key',
key: 'configKey'
},
{
title: '标题',
key: 'configName'
},
{
title: '值',
key: 'configValue',
width: 200,
ellipsis: true,
tooltip: true
},
{
title: '参数类别',
key: 'configGroup'
},
{
title: '备注',
key: 'remark'
},
{
title: '操作',
key: 'action',
align: 'center',
width: 200,
className: 'action-hide',
render: (h, params) => {
return this.$tableAction(h, [
{
title: '编辑',
directives: [
{
name: 'privilege',
value: 'system-config-update'
}
],
action: () => {
this.updateItem.id = params.row.id;
this.updateItem.configGroup = params.row.configGroup;
this.updateItem.configKey = params.row.configKey;
this.updateItem.configName = params.row.configName;
this.updateItem.configValue = params.row.configValue;
this.updateItem.remark = params.row.remark;
this.editModal = true;
}
},
{
title: '查看值',
directives: [
{
name: 'privilege',
value: 'system-config-search'
}
],
action: () => {
try {
let valueData = JSON.parse(params.row.configValue);
if (typeof valueData === 'object' && valueData) {
this.valueData = valueData;
} else {
this.valueData = params.row.configValue;
}
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.valueData = params.row.configValue;
}
this.valueModal = true;
}
}
]);
}
}
],
// table数据
data: [],
// 验证规则
updateValidate: {
configValue: [{ required: true, message: '请输入值', trigger: 'blur' }]
},
saveValidate: {
configKey: [{ required: true, message: '请输入Key', trigger: 'blur' }],
configName: [
{ required: true, message: '请输入标题', trigger: 'blur' }
],
configValue: [{ required: true, message: '请输入值', trigger: 'blur' }],
configGroup: [
{ required: true, message: '请输入参数类别', trigger: 'blur' }
]
}
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
this.getSystemConfigList();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 查询系统参数列表
async getSystemConfigList() {
this.loading = true;
this.pageShow = true;
let searchForm = {};
if (this.searchType === 'BY_KEY') {
searchForm['key'] = this.searchValue;
} else {
searchForm['configGroup'] = this.searchValue;
}
let result = await systemConfigApi.getSystemConfigList({
...searchForm,
pageNum: this.pageNum,
pageSize: this.pageSize
});
this.loading = false;
this.data = result.data.list;
this.pageTotal = result.data.total;
this.pageNum = result.data.pageNum;
this.pageSize = result.data.pageSize;
},
// 分页
changePage(pageNum) {
this.pageNum = pageNum;
this.getSystemConfigList();
},
// 更改分页查询条数
changePageSize(pageSize) {
this.pageNum = 1;
this.pageSize = pageSize;
this.getSystemConfigList();
},
// 修改
handleUpdate() {
this.$refs['updateRef'].validate(valid => {
if (valid) {
this.updateSystemConfig();
}
});
},
// 修改系统设置信息
async updateSystemConfig() {
this.$Spin.show();
let result = await systemConfigApi.updateSystemConfig(this.updateItem);
this.$Spin.hide();
this.$Message.success('修改成功');
this.getSystemConfigList();
this.cancelUpdate();
},
// 搜索
searchSystemConfigList() {
this.pageNum = 1;
this.getSystemConfigList();
},
// 保存系统设置信息
handleSave() {
try {
this.$refs['saveRef'].validate(valid => {
if (valid) {
this.addSystemConfig();
}
});
} catch (e) {
//TODO zhuoda sentry
console.log(e);
}
},
// 新增
async addSystemConfig() {
try {
this.saveLoading = true;
let result = await systemConfigApi.addSystemConfig(this.saveItem);
this.saveLoading = false;
this.$Message.success('添加成功');
this.getSystemConfigList();
this.cancelSave();
} catch (e) {
//TODO zhuoda sentry
console.log(e);
this.saveLoading = false;
}
},
// 取消
cancelSave() {
this.saveItem = {};
// 清空form规则检查
this.$refs['saveRef'].resetFields();
this.addModal = false;
},
// 取消模态框
cancelValueModal() {
this.valueData = '';
this.valueModal = false;
},
// 取消
cancelUpdate() {
this.updateItem = {};
// 清空form规则检查
this.$refs['updateRef'].resetFields();
this.editModal = false;
},
// 刷新
refresh() {
this.pageNum = 1;
this.searchType = 'BY_KEY';
this.searchValue = '';
this.getSystemConfigList();
}
}
};
</script>

View File

@@ -0,0 +1,144 @@
<template>
<Modal
:closable="false"
:mask-closable="false"
:title="privilege.functionName"
:width="680"
@on-ok="submitForm()"
v-model="show"
>
<Form :label-width="100" :model="privilege" ref="privilegeFormRef">
<Form-item label="菜单名称:" required>
<Input disabled placeholder="请输入菜单名称" v-model="title"></Input>
</Form-item>
<Form-item label="功能点名称:" prop="functionName" required>
<Input disabled placeholder="请输入功能点名称" v-model="privilege.functionName"></Input>
</Form-item>
<Form-item label="功能Key" prop="functionKey" required>
<Input disabled placeholder="请输入功能Key" v-model="privilege.functionKey"></Input>
</Form-item>
<Form-item label="Url" required>
<Select filterable multiple v-model="urlArray">
<OptionGroup :key="i" :label="items.label" v-for="(items, i) in urlList">
<Option :key="j" :label="item.url" :value="item.name" v-for="(item, j) in items.data">
<span>{{item.url}}</span>
<span style="float:right;color:#ccc">{{item.comment}}</span>
</Option>
</OptionGroup>
</Select>
</Form-item>
</Form>
<div slot="footer">
<Button @click="submitForm" type="primary">保存</Button>
<Button @click="cancel" type="default">取消</Button>
</div>
</Modal>
</template>
<script>
import { privilegeApi } from '@/api/privilege';
export default {
name: 'PrivilegeForm',
components: {},
// 类型禁用
props: {
typeDisabled: {
type: Boolean,
default: true,
require: false
},
// 是否显示
show: {
type: Boolean,
default: false,
require: true
},
// 标题
title: {
type: String,
require: true
},
// 权限
privilege: {
type: Object,
require: true
}
},
data() {
return {
scope: 1, // 权限划分 1管理端权限 2web端权限 ,
urlList: [],
privilegeNameTitle: '菜单名称',
urlArray: []
};
},
computed: {},
watch: {
privilege(val) {
if (val) {
this.urlArray = val.url.split(',');
}
}
},
filters: {},
created() {},
mounted() {
this.getAllUrl();
},
beforeCreate() {},
beforeMount() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {},
activated() {},
methods: {
// 获取所有请求路径
async getAllUrl() {
this.$Spin.show();
let result = await privilegeApi.getAllUrl(this.scope);
this.$Spin.hide();
let key = 1;
let datas = result.data;
let list = [];
let keys = [];
datas.map(item => {
let type = item.name.split('.')[0];
let index = keys.indexOf(type);
if (index < 0) {
keys.push(type);
list.push({
label: type,
data: [item]
});
} else {
list[index].data.push(item);
}
});
this.urlList = list;
},
//保存当前弹窗
cancel() {
this.$emit('closeModal');
},
// 提交数据
submitForm() {
let params = Object.assign({}, this.privilege);
if (this.urlArray.length === 0) {
this.$Message.error('请选择Url!');
return;
}
params.url = this.urlArray.join(',');
this.addOrUpdate(params);
},
// 保存更新功能点
async addOrUpdate(prams) {
this.$Spin.show();
let result = await privilegeApi.addOrUpdate(prams);
this.$Message.success('修改成功');
this.$Spin.hide();
this.$emit('updateMenuSuccess', prams.menuKey);
}
}
};
</script>

View File

@@ -0,0 +1,318 @@
<template>
<Row :gutter="10">
<!--菜单管理-->
<Col :lg="6" :md="8">
<Card class="warp-card" dis-hover>
<div class="card-title" slot="title">
<Icon type="ios-switch"></Icon>菜单管理
</div>
<div slot="extra">
<Button
@click="addBatchSaveMenu"
icon="md-add"
size="small"
type="primary"
v-if="menusChange"
>批量保存</Button>
</div>
<Alert show-icon type="warning" v-if="menusChange"> {{this.menusChangeNum}} 个更新请立即批量保存</Alert>
<Menu
:active-name="activeName"
@on-select="loadPrivilegeTableData"
ref="defineSelect"
width="100%"
>
<!--遍历得到模块-->
<template v-for="(item, i) in menuTree">
<Submenu :key="i" :name="item.menuKey">
<template slot="title">
<span>{{item.menuName}}</span>
</template>
<!--遍历得到子模块-->
<template v-for="(children, j) in item.children">
<Submenu :key="j" :name="children.menuKey" v-if="children.children.length > 0">
<template slot="title">
<span>{{children.menuName}}</span>
</template>
<!--遍历得到子模块页面-->
<template v-for="(childrenPages, k) in children.children">
<MenuItem :key="k" :name="childrenPages.menuKey">{{childrenPages.menuName}}</MenuItem>
</template>
</Submenu>
<MenuItem :key="j" :name="children.menuKey" v-else>{{children.menuName}}</MenuItem>
</template>
</Submenu>
</template>
</Menu>
</Card>
</Col>
<Col :lg="18" :md="16">
<Card class="warp-card" dis-hover style="margin-bottom:100px">
<div class="card-title" slot="title">
<Icon type="ios-cog"></Icon>功能点
</div>
<Row>
<Table :columns="privilegeTableColumn" :data="privilegeTableData" border></Table>
</Row>
</Card>
</Col>
<Col span="24">
<privilege-form
:privilege="formData.privilege"
:show="formData.show"
:title="formData.title"
:typeDisabled="typeDisabled"
@closeModal="closeModal"
@updateMenuSuccess="getPrivilegeList"
></privilege-form>
</Col>
</Row>
</template>
<script>
import { privilegeApi } from '@/api/privilege';
import PrivilegeForm from './components/privilege-form';
import { routers } from '@/router/routers';
import { PRIVILEGE_TYPE_ENUM } from '@/constants/privilege';
export default {
name: 'SystemPrivilege',
components: {
PrivilegeForm
},
data() {
return {
typeDisabled: true,
activeName: 0,
scope: 1,
formData: {
show: false,
title: '功能点菜单',
privilege: {
functionKey: '',
functionName: '',
menuKey: '',
url: 1
}
},
menuTree: [],
menusChange: false,
menusChangeNum: 0,
menuList: [],
routerMap: new Map(),
privilegeTableData: [],
privilegeTableColumn: [
{
title: '名称',
key: 'title'
},
{
title: 'routerKey',
key: 'name'
},
{
title: 'url',
key: 'url'
},
{
title: '操作',
width: 120,
align: 'center',
className: 'action-hide',
render: (h, params) => {
return this.$tableAction(h, [
{
title: '编辑',
directives: [
{
name: 'privilege',
value: 'privilege-main-update'
}
],
action: () => {
this.updatePrivilege(params.row, params.index);
}
}
]);
}
}
]
};
},
mounted() {
this.initRouters();
},
methods: {
// 关闭模态框
closeModal() {
this.formData.show = false;
},
// 初始化菜单
initRouters() {
this.resetMenuChange();
this.buildPrivilegeTree();
},
// 获取全部菜单列表
async buildPrivilegeTree() {
this.$Spin.show();
let getMenuListResult = await privilegeApi.getMenuList();
let serverMenuList = getMenuListResult.data;
let serverMenuMap = new Map();
for (let serverMenu of serverMenuList) {
serverMenuMap.set(serverMenu.menuKey, serverMenu);
}
let privilegeList = [];
let privilegeTree = [];
for (const router of routers) {
//过滤非菜单
if (!router.meta.hideInMenu) {
this.routerMap.set(router.name, router);
let menu = {
type: PRIVILEGE_TYPE_ENUM.MENU.value,
menuName: router.meta.title,
menuKey: router.name,
parentKey: null,
url: router.path,
children: [],
sort: 0
};
privilegeTree.push(menu);
privilegeList.push(menu);
//判断是否有更新菜单
this.hasMenuChange(menu, serverMenuMap);
//存在孩子节点,开始递归
if (router.children && router.children.length > 0) {
this.recursion(router.children, menu, privilegeList, serverMenuMap);
}
}
}
if (privilegeList.length !== serverMenuList.length) {
this.menusChange = true;
this.menusChangeNum =
this.menusChangeNum +
Math.abs(privilegeList.length - serverMenuList.length);
}
this.menuTree = privilegeTree;
this.menuList = privilegeList;
this.$Spin.hide();
},
recursion(children, parentMenu, menuList, serverMenuMap) {
for (const router of children) {
//过滤非菜单
if (!router.meta.hideInMenu) {
this.routerMap.set(router.name, router);
let menu = {
type: PRIVILEGE_TYPE_ENUM.MENU.value,
menuName: router.meta.title,
menuKey: router.name,
parentKey: parentMenu.menuKey,
url: router.path,
children: [],
sort: 0
};
parentMenu.children.push(menu);
menuList.push(menu);
//判断是否有更新菜单
this.hasMenuChange(menu, serverMenuMap);
//存在孩子节点,开始递归
if (router.children && router.children.length > 0) {
this.recursion(router.children, menu, menuList, serverMenuMap);
}
}
}
},
// reset菜单有更新
resetMenuChange() {
this.menusChange = false;
this.menusChangeNum = 0;
},
// 菜单有更新
hasMenuChange(menu, serverMenuMap) {
let isChange = false;
let serverMenu = serverMenuMap.get(menu.menuKey);
if (serverMenu) {
isChange =
serverMenu.menuName !== menu.menuName ||
serverMenu.menuKey !== menu.menuKey ||
serverMenu.parentKey !== menu.parentKey;
} else {
isChange = true;
}
if (isChange) {
this.menusChange = true;
this.menusChangeNum = this.menusChangeNum + 1;
}
},
// 菜单批量保存
async addBatchSaveMenu() {
this.$Spin.show();
let result = await privilegeApi.addBatchSaveMenu(this.menuList);
this.$Message.success('批量保存成功');
this.$Spin.hide();
//重新获取数据
this.initRouters();
},
// 编辑功能点
updatePrivilege(item, sort) {
this.formData.privilege = {
functionKey: item.name,
functionName: item.title,
menuKey: item.parentKey,
url: item.url,
sort
};
this.formData.title = item.parentName;
this.formData.show = true;
},
// 查询菜单对应的页面以及功能点
async getPrivilegeList(menuKey) {
this.$Spin.show();
this.formData.show = false;
let result = await privilegeApi.queryPrivilegeFunctionList(menuKey);
this.$Spin.hide();
let datas = result.data;
let functionKey = new Map();
datas.map(item => {
functionKey.set(item.functionKey, item.url);
});
let privilegeTableData = [];
this.privilegeTableData.map(item => {
let url = functionKey.get(item.name) || '';
item.url = url;
privilegeTableData.push(item);
});
this.privilegeTableData = privilegeTableData;
},
// 点击菜单事件
loadPrivilegeTableData(name) {
let router = this.routerMap.get(name);
if (!_.isUndefined(router) && router.meta && router.meta.childrenPoints) {
this.privilegeTableData = router.meta.childrenPoints.map(e =>
Object.assign({}, e, { parentKey: name })
);
}
this.getPrivilegeList(name);
}
}
};
</script>
<style scoped>
.privilege-badge {
background: rgba(45, 140, 240, 0.8);
border-radius: 3px;
font-size: 8px;
color: #fff;
line-height: 1;
display: inline-block;
padding: 0 3px;
margin-left: 5px;
}
</style>

View File

@@ -0,0 +1,545 @@
<template>
<div>
<Card class="warp-card" dis-hover>
<Form class="tools" inline>
<FormItem>
<Button
@click="refresh"
icon="md-refresh"
type="primary"
v-privilege="'task-refresh'"
>刷新任务</Button>
</FormItem>
<FormItem>
<Button @click="addModal=true" icon="md-add" type="primary" v-privilege="'task-add'">添加任务</Button>
</FormItem>
</Form>
<Tables
:columns="columns"
:current="pageNum"
:loading="loading"
:page-size="pageSize"
:pageShow="true"
:total="pageTotal"
:value="data"
@on-change="changePage"
border
show-elevator
></Tables>
</Card>
<!-- 编辑任务 -->
<Modal
:loading="updateLoading"
@on-cancel="cancelUpdate"
@on-ok="handleUpdate"
title="编辑"
v-model="editModal"
>
<Form :label-width="80" :model="updateItem" :rules="updateValidate" ref="updateRef">
<FormItem label="taskBean" prop="taskBean">
<Input placeholder="请输入taskBean(必填)" v-model="updateItem.taskBean"></Input>
</FormItem>
<FormItem label="taskCron" prop="taskCron">
<Input placeholder="请输入taskCron(必填)" v-model="updateItem.taskCron"></Input>
</FormItem>
<FormItem label="任务标题" prop="taskName">
<Input placeholder="请输入任务标题(必填)" v-model="updateItem.taskName"></Input>
</FormItem>
<FormItem label="任务参数" prop="taskParams">
<Input placeholder="请输入任务参数(必填)" v-model="updateItem.taskParams"></Input>
</FormItem>
</Form>
</Modal>
<!-- 添加任务 -->
<Modal
:loading="saveLoading"
@on-cancel="cancelSave"
@on-ok="handleSave"
title="添加"
v-model="addModal"
>
<Form :label-width="80" :model="saveItem" :rules="saveValidate" ref="saveRef">
<FormItem label="taskBean" prop="taskBean">
<Input placeholder="请输入taskBean(必填)" v-model="saveItem.taskBean"></Input>
</FormItem>
<FormItem label="taskCron" prop="taskCron">
<Input placeholder="请输入taskCron(必填)" v-model="saveItem.taskCron"></Input>
</FormItem>
<FormItem label="任务标题" prop="taskName">
<Input placeholder="请输入任务标题(必填)" v-model="saveItem.taskName"></Input>
</FormItem>
<FormItem label="任务参数" prop="taskParams">
<Input placeholder="请输入任务参数" v-model="saveItem.taskParams"></Input>
</FormItem>
</Form>
</Modal>
<!-- 任务日志 -->
<Modal :closable="false" :mask-closable="false" title="任务运行日志" v-model="logModal" width="850px">
<Row class="log-modal-header" slot="header">
<Col :span="18">
<span>任务调度日志</span>
</Col>
</Row>
<Tables
:columns="logColumns"
:current="logPageNum"
:page-size="changeLogPageSize"
:pageShow="true"
:show-elevator="false"
:show-sizer="false"
:total="logPageTotal"
:value="logData"
@on-change="logChangePage"
style="height: 420px;"
v-if="hasLog"
></Tables>
<div slot="footer">
<Button @click="closeLog" type="primary">关闭</Button>
</div>
</Modal>
</div>
</template>
<script>
import Tables from '@/components/tables';
import { taskApi } from '@/api/task-manage';
export default {
name: 'taskManage',
components: {
Tables
},
props: {},
data() {
return {
hasLog: false,
editModal: false,
addModal: false,
logModal: false,
// 当前所查看的任务Id
taskId: '',
// table是否loading
loading: true,
saveLoading: true,
updateLoading: true,
pageNum: 1,
pageSize: 10,
pageTotal: 0,
logPageNum: 1,
logPageSize: 6,
logPageTotal: 0,
updateItem: {
taskBean: '',
taskCron: '',
taskName: '',
taskParams: '',
taskStatus: ''
},
saveItem: {
taskBean: '',
taskCron: '',
taskName: '',
taskParams: '',
taskStatus: ''
},
// table表头
columns: [
{
title: 'ID',
width: 60,
key: 'id'
},
{
title: 'taskBean',
key: 'taskBean'
},
{
title: 'taskCron',
key: 'taskCron'
},
{
title: '任务标题',
key: 'taskName'
},
{
title: '任务参数',
key: 'taskParams'
},
{
title: '任务状态',
key: 'taskStatus',
render: (h, params) => {
return h('span', params.row.taskStatus === 0 ? '正常' : '暂停');
}
},
{
title: '操作',
key: 'action',
align: 'center',
className: 'action-hide',
width: 180,
render: (h, params) => {
return this.$tableAction(h, [
{
title: '编辑',
directives: [
{
name: 'privilege',
value: 'taskUpdate'
}
],
action: () => {
this.updateItem.taskBean = params.row.taskBean;
this.updateItem.id = params.row.id;
this.updateItem.taskCron = params.row.taskCron;
this.updateItem.taskName = params.row.taskName;
this.updateItem.taskParams = params.row.taskParams;
this.updateItem.taskStatus = params.row.taskStatus;
this.editModal = true;
}
},
{
title: '删除',
directives: [
{
name: 'privilege',
value: 'taskDelete'
}
],
action: () => {
this.$Modal.confirm({
title: '删除',
content: '确认删除该条任务么?',
onOk: () => {
this.deleteTask(params.row.id);
}
});
}
},
{
title: '立即开始任务',
directives: [
{
name: 'privilege',
value: 'taskRun'
}
],
action: () => {
this.$Modal.confirm({
title: '确认',
content: '确认立即开始任务么?',
onOk: () => {
this.controlTask('RUN', params.row.id);
}
});
}
},
{
title: '暂停任务',
directives: [
{
name: 'privilege',
value: 'taskPause'
}
],
action: () => {
this.$Modal.confirm({
title: '确认',
content: '确认暂停任务么?',
onOk: () => {
this.controlTask('PAUSE', params.row.id);
}
});
}
},
{
title: '恢复任务',
directives: [
{
name: 'privilege',
value: 'taskResume'
}
],
action: () => {
this.$Modal.confirm({
title: '确认',
content: '确认恢复任务么?',
onOk: () => {
this.controlTask('RESUME', params.row.id);
}
});
}
},
{
title: '查看日志',
directives: [
{
name: 'privilege',
value: 'taskQueryLog'
}
],
action: () => {
this.taskId = params.row.id;
this.logPageNum = 1;
this.getTaskLog();
}
}
]);
}
}
],
// 日志表格表头
logColumns: [
{
type: 'index',
width: 60,
align: 'center'
},
{
title: '任务名',
key: 'taskName'
},
{
title: '任务参数',
key: 'taskParams'
},
{
title: '主机IP',
key: 'ipAddress'
},
{
title: '处理结果',
key: 'processStatus',
render: (h, params) => {
return h('span', params.row.processStatus === 0 ? '成功' : '失败');
}
},
{
title: '处理时长',
key: 'processDuration',
render: (h, params) => {
return h('span', params.row.processDuration + 'ms');
}
},
{
title: '失败日志',
key: 'processLog',
render: (h, params) => {
return h(
'span',
params.row.processLog ? params.row.processLog : '无'
);
}
},
{
title: '运行时间',
width: 190,
key: 'createTime'
}
],
// table数据
data: [],
// 日志数据
logData: [],
updateValidate: {
taskBean: [{ required: true, message: '请输入值', trigger: 'blur' }],
taskCron: [{ required: true, message: '请输入值', trigger: 'blur' }],
taskName: [
{ required: true, message: '请输入任务标题', trigger: 'blur' }
]
},
saveValidate: {
taskBean: [{ required: true, message: '请输入值', trigger: 'blur' }],
taskCron: [{ required: true, message: '请输入值', trigger: 'blur' }],
taskName: [
{ required: true, message: '请输入任务标题', trigger: 'blur' }
]
}
};
},
mounted() {
this.getTaskList();
},
methods: {
// 查询任务调度列表
async getTaskList() {
try {
this.loading = true;
let result = await taskApi.getTaskList({
pageNum: this.pageNum,
pageSize: this.pageSize
});
this.loading = false;
this.data = result.data.list;
this.pageTotal = result.data.total;
this.pageNum = result.data.pageNum;
this.pageSize = result.data.pageSize;
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.loading = false;
}
},
// 查询任务运行日志
async getTaskLog() {
let result = await taskApi.getTaskLog({
pageNum: this.logPageNum,
pageSize: this.logPageSize,
taskId: this.taskId
});
this.logData = result.data.list;
this.logPageTotal = result.data.total;
this.logPageNum = result.data.pageNum;
this.logPageSize = result.data.pageSize;
this.hasLog = true;
this.logModal = true;
},
// 分页
changePage(pageNum) {
this.pageNum = pageNum;
this.getTaskList();
},
// 分页
changePageSize(pageSize) {
this.pageNum = 1;
this.pageSize = pageSize;
this.getTaskList();
},
// 日志分页
changeLogPageSize(pageNum) {
this.logPageNum = pageNum;
this.getTaskLog();
},
// 删除任务
async deleteTask(id) {
let result = await taskApi.deleteTask(id);
this.$Message.success('删除任务成功!');
this.getTaskList();
},
// 操作任务
async controlTask(type, id) {
this.$Spin.show();
switch (type) {
case 'RUN':
await taskApi.updateTaskRun(id);
break;
case 'PAUSE':
await taskApi.updateTaskPause(id);
break;
case 'RESUME':
await taskApi.updateTaskResume(id);
break;
}
this.$Spin.hide();
this.$Message.success('操作成功');
this.getTaskList();
},
// 触发更新
handleUpdate() {
this.$refs['updateRef'].validate(valid => {
if (valid) {
this.updateTask();
} else {
this.$Message.success('验证信息不通过');
}
});
},
// 修改任务
async updateTask() {
try {
this.updateLoading = true;
let result = await taskApi.addOrUpdateTask(this.updateItem);
this.updateLoading = false;
this.$Message.success('修改成功');
this.getTaskList();
this.cancelUpdate();
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.updateLoading = false;
}
},
// 保存任务
handleSave() {
try {
this.$refs['saveRef'].validate(valid => {
this.saveLoading = true;
if (valid) {
this.saveTask();
} else {
this.saveLoading = false;
this.$nextTick(() => {
this.saveLoading = true;
});
}
});
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.saveLoading = false;
}
},
// 保存任务
async saveTask() {
try {
let result = await taskApi.addOrUpdateTask(this.saveItem);
this.saveLoading = false;
this.$Message.success('添加成功');
this.getTaskList();
this.cancelSave();
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.saveLoading = false;
}
},
// 取消保存
cancelSave() {
this.saveItem = {};
// 清空form规则检查
this.$refs['saveRef'].resetFields();
this.addModal = false;
},
// 取消修改
cancelUpdate() {
this.updateItem = {};
// 清空form规则检查
this.$refs['updateRef'].resetFields();
this.editModal = false;
},
// 关闭日志
closeLog() {
this.logData = [];
this.logPageNum = 1;
this.hasLog = false;
this.logModal = false;
},
// 刷新
refresh() {
this.pageNum = 1;
this.getTaskList();
}
}
};
</script>
<style lang="less" scoped>
.log-modal-header {
height: 25px;
line-height: 25px;
font-size: 14px;
color: #17233d;
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.log-modal-header-btn-group {
text-align: right;
button {
margin: 0 5px;
span {
margin: 0;
}
}
}
</style>

View File

@@ -0,0 +1,185 @@
<template>
<div>
<Card class="warp-card" dis-hover>
<Form
:model="searchform"
class="tools"
inline
ref="searchform"
v-privilege="'user-login-log-search'"
>
<FormItem prop="startDate">
<DatePicker
placeholder="开始日期"
style="width: 200px"
type="date"
v-model="searchform.startDate"
></DatePicker>
</FormItem>
<FormItem prop="endDate">
<DatePicker
placeholder="结束日期"
style="width: 200px"
type="date"
v-model="searchform.endDate"
></DatePicker>
</FormItem>
<FormItem prop="userName">
<Input placeholder="请输入用户名" type="text" v-model="searchform.userName"></Input>
</FormItem>
<FormItem>
<ButtonGroup>
<Button @click="search" icon="ios-search" type="primary">查询</Button>
<Button @click="reset" icon="md-refresh" type="default">重置</Button>
</ButtonGroup>
</FormItem>
</Form>
<Table :columns="columns" :data="data" :loading="loading"></Table>
<Page
:current="searchform.pageNum"
:page-size="searchform.pageSize"
:page-size-opts="[10, 20, 30, 50, 100]"
:total="pageTotal"
@on-change="changePage"
@on-page-size-change="changePageSize"
show-elevator
show-sizer
show-total
style="margin:24px 0;text-align:right;"
></Page>
</Card>
</div>
</template>
<script>
import { userLogApi } from '@/api/user-log';
export default {
name: 'UserLoginLog',
components: {},
props: {},
data() {
return {
loading: false,
searchform: {
startDate: '',
endDate: '',
pageNum: 1,
pageSize: 10,
searchCount: true,
sort: false,
userName: ''
},
pageTotal: 0,
// table表头
columns: [
{
title: '用户名称',
key: 'userName'
},
{
title: '浏览器',
key: 'remoteBrowser'
},
{
title: '操作系统',
key: 'remoteOs'
},
{
title: 'Ip',
key: 'remoteIp'
},
{
title: '用户端口',
key: 'remotePort'
},
{
title: '创建时间',
key: 'createTime'
},
{
title: '操作',
key: 'action',
align: 'center',
className: 'action-hide',
render: (h, param) => {
return this.$tableAction(h, [
{
title: '删除',
directives: [
{
name: 'privilege',
value: 'user-login-log-delete'
}
],
action: () => {
this.$Modal.confirm({
title: '友情提醒',
content: '确定要删除吗?',
onOk: () => {
this.deleteLog(param.row.id);
}
});
}
}
]);
}
}
],
// table数据
data: []
};
},
computed: {},
watch: {},
filters: {},
created() {},
mounted() {
this.getUserLoginLogPage();
},
methods: {
// 查询用户登录日志
async getUserLoginLogPage() {
try {
this.loading = true;
let result = await userLogApi.getUserLoginLogPage(this.searchform);
this.loading = false;
this.data = result.data.list;
this.pageTotal = result.data.total;
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.loading = false;
}
},
// 翻页
changePage(pageNum) {
this.searchform.pageNum = pageNum;
this.getUserLoginLogPage();
},
// 改变一页展示数
changePageSize(pageSize) {
this.searchform.pageNum = 1;
this.searchform.pageSize = pageSize;
this.getUserLoginLogPage();
},
// 搜索
search() {
this.searchform.pageNum = 1;
this.getUserLoginLogPage();
},
// 重置
reset() {
this.$refs.searchform.resetFields();
this.search();
},
// 删除日志
async deleteLog(id) {
this.$Spin.show();
await userLogApi.deleteUserLoginLog(id);
this.$Spin.hide();
this.$Message.success('删除成功');
this.getUserLoginLogPage();
}
}
};
</script>

View File

@@ -0,0 +1,252 @@
<template>
<div>
<Card class="warp-card" dis-hover>
<Form
:model="searchform"
class="tools"
inline
ref="searchform"
v-privilege="'user-operate-log-search'"
>
<FormItem prop="startDate">
<DatePicker
placeholder="开始日期"
style="width: 200px"
type="date"
v-model="searchform.startDate"
></DatePicker>
</FormItem>
<FormItem prop="endDate">
<DatePicker
placeholder="结束日期"
style="width: 200px"
type="date"
v-model="searchform.endDate"
></DatePicker>
</FormItem>
<FormItem prop="userName">
<Input placeholder="请输入用户名" type="text" v-model="searchform.userName"></Input>
</FormItem>
<FormItem>
<ButtonGroup>
<Button @click="search" icon="ios-search" type="primary">查询</Button>
<Button @click="reset" icon="md-refresh" type="default">重置</Button>
</ButtonGroup>
</FormItem>
</Form>
<Table :columns="columns" :data="data" :loading="tableLoading" :total="pageTotal"></Table>
<Page
:current="searchform.pageNum"
:page-size="searchform.pageSize"
:page-size-opts="[10, 20, 30, 50, 100]"
:total="pageTotal"
@on-change="changePage"
@on-page-size-change="changePageSize"
show-elevator
show-sizer
show-total
style="margin:24px 0;text-align:right;"
></Page>
</Card>
<Modal title="日志详情" v-model="detailModal">
<Form :label-width="80" :model="logDetail">
<FormItem label="用户名称:">{{logDetail.userName}}</FormItem>
<FormItem label="用户类型:">{{logDetail.userType?'前台':'后台'}}</FormItem>
<FormItem label="操作模块:">{{logDetail.module}}</FormItem>
<FormItem label="操作内容:">{{logDetail.content}}</FormItem>
<FormItem label="请求方法:">{{logDetail.method}}</FormItem>
<FormItem label="请求路径:">{{logDetail.url}}</FormItem>
<FormItem label="请求参数:">{{logDetail.param}}</FormItem>
<FormItem label="请求结果:">{{logDetail.result?'成功':'失败'}}</FormItem>
<FormItem label="失败原因:">
<div style="max-height:120px;overflow-y:scroll;">{{logDetail.failReason}}</div>
</FormItem>
</Form>
</Modal>
</div>
</template>
<script>
import { userLogApi } from '@/api/user-log';
export default {
name: 'UserOperateLog',
components: {},
props: {},
data() {
return {
tableLoading: false,
searchform: {
startDate: '',
endDate: '',
pageNum: 1,
pageSize: 10,
searchCount: true,
sort: false,
userName: ''
},
pageTotal: 0,
// table表头
columns: [
{
title: '用户名称',
key: 'userName',
width: 150
},
{
title: '操作模块',
key: 'module',
tooltip: true,
width: 180
},
{
title: '操作内容',
key: 'content',
tooltip: true,
width: 260
},
{
title: '请求方法',
key: 'method',
tooltip: true,
width: 200
},
{
title: '请求路径',
key: 'url',
tooltip: true,
width: 200
},
{
title: '请求参数',
key: 'param',
tooltip: true,
width: 200
},
{
title: '请求结果',
key: 'result',
width: 100,
render: (h, param) => {
if (param.row.result) {
return h('div', '成功');
}
return h('div', '失败');
}
},
{
title: '-'
},
{
title: '操作',
key: 'action',
width: 160,
align: 'center',
className: 'action-hide',
fixed: 'right',
render: (h, param) => {
return this.$tableAction(h, [
{
title: '详情',
directives: [
{
name: 'privilege',
value: 'user-operate-log-detail'
}
],
action: () => {
this.showLogDetail(param.row.id);
}
},
{
title: '删除',
directives: [
{
name: 'privilege',
value: 'user-operate-log-delete'
}
],
action: () => {
this.$Modal.confirm({
title: '友情提醒',
content: '确定要删除吗?',
onOk: () => {
this.deleteLog(param.row.id);
}
});
}
}
]);
}
}
],
// table数据
data: [],
detailModal: false,
logDetail: {}
};
},
computed: {},
watch: {},
filters: {},
mounted() {
this.getUserOperateLogPage();
},
methods: {
// 查询用户操作日志
async getUserOperateLogPage() {
this.tableLoading = true;
try {
let result = await userLogApi.getUserOperateLogPage(this.searchform);
this.tableLoading = false;
this.data = result.data.list;
this.pageTotal = result.data.total;
} catch (e) {
//TODO zhuoda sentry
console.error(e);
this.tableLoading = false;
}
},
changePage(pageNum) {
this.searchform.pageNum = pageNum;
this.getUserOperateLogPage();
},
changePageSize(pageSize) {
this.searchform.pageNum = 1;
this.searchform.pageSize = pageSize;
this.getUserOperateLogPage();
},
// 搜索
search() {
this.searchform.pageNum = 1;
this.getUserOperateLogPage();
},
reset() {
this.$refs.searchform.resetFields();
this.search();
},
// 获取并查看详情
async showLogDetail(id) {
this.$Spin.show();
let res = await userLogApi.detailUserOperateLog(id);
this.$Spin.hide();
this.logDetail = res.data;
this.detailModal = true;
},
// 删除日志
async deleteLog(id) {
this.$Spin.show();
await userLogApi.deleteUserOperateLog(id);
this.$Spin.hide();
this.$Message.success('删除成功');
this.getUserOperateLogPage();
}
}
};
</script>
<style scoped>
.ivu-form-item {
word-break: break-word;
}
</style>