init
This commit is contained in:
140
src/views/system/dictionary/index.vue
Normal file
140
src/views/system/dictionary/index.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div class="ele-body ele-body-card">
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="6" :md="10" :sm="24" :xs="24">
|
||||
<a-card
|
||||
:bordered="false"
|
||||
:body-style="{padding: '24px 16px'}">
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="table"
|
||||
row-key="dictId"
|
||||
:datasource="url"
|
||||
:columns="columns"
|
||||
v-model:current="current"
|
||||
:need-page="false"
|
||||
:row-selection="{columnWidth: 38}"
|
||||
:toolkit="[]"
|
||||
@done="done">
|
||||
<template #toolbar>
|
||||
<a-space size="middle">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="openEdit()">新建
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="openEdit(current)"
|
||||
:disabled="!current">修改
|
||||
</a-button>
|
||||
<a-button
|
||||
danger
|
||||
type="primary"
|
||||
@click="remove"
|
||||
:disabled="!current">删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :lg="18" :md="14" :sm="24" :xs="24">
|
||||
<a-card :bordered="false">
|
||||
<sys-dict-data
|
||||
v-if="current"
|
||||
:dict-id="current.dictId"/>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<sys-dict-edit
|
||||
v-model:visible="showEdit"
|
||||
:data="editData"
|
||||
@done="reload"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createVNode} from 'vue';
|
||||
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
|
||||
import SysDictData from './sys-dict-data';
|
||||
import SysDictEdit from './sys-dict-edit';
|
||||
|
||||
export default {
|
||||
name: 'SystemDictionary',
|
||||
components: {SysDictData, SysDictEdit},
|
||||
data() {
|
||||
return {
|
||||
// 表格数据接口
|
||||
url: '/sys/dict',
|
||||
// 表格列配置
|
||||
columns: [
|
||||
{
|
||||
key: 'index',
|
||||
width: 38,
|
||||
customRender: ({index}) => index + 1
|
||||
},
|
||||
{
|
||||
title: '字典名称',
|
||||
dataIndex: 'dictName'
|
||||
}
|
||||
],
|
||||
// 表格选中数据
|
||||
current: null,
|
||||
// 是否显示编辑弹窗
|
||||
showEdit: false,
|
||||
// 编辑回显数据
|
||||
editData: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/* 表格渲染完成回调 */
|
||||
done(res) {
|
||||
if (res.data.length > 0) {
|
||||
this.current = res.data[0];
|
||||
}
|
||||
},
|
||||
/* 刷新表格 */
|
||||
reload() {
|
||||
this.$refs.table.reload();
|
||||
},
|
||||
/* 打开编辑弹窗 */
|
||||
openEdit(row) {
|
||||
this.editData = row;
|
||||
this.showEdit = true;
|
||||
},
|
||||
/* 删除 */
|
||||
remove() {
|
||||
this.$confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的字典吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.delete('/sys/dict/' + this.current.dictId).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.reload();
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@media screen and (min-width: 768px) {
|
||||
.ant-card {
|
||||
min-height: calc(100vh - 122px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
135
src/views/system/dictionary/sys-dict-data-edit.vue
Normal file
135
src/views/system/dictionary/sys-dict-data-edit.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<!-- 字典项编辑弹窗 -->
|
||||
<template>
|
||||
<a-modal
|
||||
:width="460"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:body-style="{paddingBottom: '8px'}"
|
||||
:title="isUpdate?'修改字典项':'添加字典项'"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save">
|
||||
<a-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="{md: {span: 6}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 18}, sm: {span: 24}}">
|
||||
<a-form-item label="字典项名称:" name="dictDataName">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入字典项名称"
|
||||
v-model:value="form.dictDataName"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="字典项值:" name="dictDataCode">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入字典项值"
|
||||
v-model:value="form.dictDataCode"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号:" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注:">
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入备注"
|
||||
v-model:value="form.comments"/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SysDictDataEdit',
|
||||
emits: ['done', 'update:visible'],
|
||||
props: {
|
||||
// 弹窗是否打开
|
||||
visible: Boolean,
|
||||
// 修改回显的数据
|
||||
data: Object,
|
||||
// 字典id
|
||||
dictId: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表单数据
|
||||
form: Object.assign({}, this.data),
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
dictDataName: [
|
||||
{required: true, message: '请输入字典项名称', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
dictDataCode: [
|
||||
{required: true, message: '请输入字典项值', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
sortNumber: [
|
||||
{required: true, message: '请输入排序号', type: 'number', trigger: 'blur'}
|
||||
]
|
||||
},
|
||||
// 提交状态
|
||||
loading: false,
|
||||
// 是否是修改
|
||||
isUpdate: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
if (this.data) {
|
||||
this.form = Object.assign({}, this.data);
|
||||
this.isUpdate = true;
|
||||
} else {
|
||||
this.form = {};
|
||||
this.isUpdate = false;
|
||||
}
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/* 保存编辑 */
|
||||
save() {
|
||||
this.$refs.form.validate().then(() => {
|
||||
this.loading = true;
|
||||
this.$http[this.isUpdate ? 'put' : 'post']('/sys/dictdata',
|
||||
Object.assign({}, this.form, {
|
||||
dictId: this.dictId
|
||||
})
|
||||
).then(res => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
if (!this.isUpdate) {
|
||||
this.form = {};
|
||||
}
|
||||
this.updateVisible(false);
|
||||
this.$emit('done');
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/* 更新visible */
|
||||
updateVisible(value) {
|
||||
this.$emit('update:visible', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
178
src/views/system/dictionary/sys-dict-data.vue
Normal file
178
src/views/system/dictionary/sys-dict-data.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="table"
|
||||
row-key="dictDataId"
|
||||
:datasource="url"
|
||||
:columns="columns"
|
||||
:where="where"
|
||||
tool-class="ele-toolbar-form"
|
||||
v-model:selection="selection"
|
||||
:scroll="{x: 'max-content'}">
|
||||
<template #toolbar>
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="6" :md="8" :sm="24" :xs="24">
|
||||
<a-input
|
||||
v-model:value.trim="where.keywords"
|
||||
placeholder="输入关键字搜索"
|
||||
allow-clear/>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="8" :sm="24" :xs="24">
|
||||
<a-space size="middle">
|
||||
<a-button type="primary" @click="reload">查询</a-button>
|
||||
<a-button type="primary" @click="openEdit()">新建</a-button>
|
||||
<a-button type="primary" danger @click="removeBatch">删除</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical"/>
|
||||
<a-popconfirm
|
||||
title="确定要删除此字典项吗?"
|
||||
@confirm="remove(record)">
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
<!-- 编辑弹窗 -->
|
||||
<sys-dict-data-edit
|
||||
v-model:visible="showEdit"
|
||||
:data="current"
|
||||
:dict-id="dictId"
|
||||
@done="reload"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createVNode} from 'vue';
|
||||
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
|
||||
import SysDictDataEdit from './sys-dict-data-edit';
|
||||
|
||||
export default {
|
||||
name: 'SysDictData',
|
||||
components: {SysDictDataEdit},
|
||||
props: {
|
||||
// 字典id
|
||||
dictId: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表格数据接口
|
||||
url: '/sys/dictdata/page',
|
||||
// 表格列配置
|
||||
columns: [
|
||||
{
|
||||
title: '字典项名称',
|
||||
dataIndex: 'dictDataName',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '字典项值',
|
||||
dataIndex: 'dictDataCode',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '排序号',
|
||||
dataIndex: 'sortNumber',
|
||||
sorter: true,
|
||||
width: 120,
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
sorter: true,
|
||||
customRender: ({text}) => this.$util.toDateString(text)
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 130,
|
||||
align: 'center',
|
||||
slots: {customRender: 'action'}
|
||||
}
|
||||
],
|
||||
// 表格搜索条件
|
||||
where: {
|
||||
dictId: this.dictId
|
||||
},
|
||||
// 表格选中数据
|
||||
selection: [],
|
||||
// 当前编辑数据
|
||||
current: null,
|
||||
// 是否显示编辑弹窗
|
||||
showEdit: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/* 刷新表格 */
|
||||
reload() {
|
||||
this.$refs.table.reload({page: 1, where: this.where});
|
||||
},
|
||||
/* 打开编辑弹窗 */
|
||||
openEdit(row) {
|
||||
this.current = row;
|
||||
this.showEdit = true;
|
||||
},
|
||||
/* 删除单个 */
|
||||
remove(row) {
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.delete('/sys/dictdata/' + row.dictDataId).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.reload();
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
},
|
||||
/* 批量删除 */
|
||||
removeBatch() {
|
||||
if (!this.selection.length) {
|
||||
this.$message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的字典项吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.delete('/sys/dictdata/batch', {
|
||||
data: this.selection.map(d => d.dictDataId)
|
||||
}).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.reload();
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听字典id变化
|
||||
dictId() {
|
||||
this.where.dictId = this.dictId;
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
129
src/views/system/dictionary/sys-dict-edit.vue
Normal file
129
src/views/system/dictionary/sys-dict-edit.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<!-- 字典编辑弹窗 -->
|
||||
<template>
|
||||
<a-modal
|
||||
:width="460"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate?'修改字典':'添加字典'"
|
||||
:body-style="{paddingBottom: '8px'}"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save">
|
||||
<a-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="{md: {span: 5}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 19}, sm: {span: 24}}">
|
||||
<a-form-item label="字典名称:" name="dictName">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入字典名称"
|
||||
v-model:value="form.dictName"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="字典值:" name="dictCode">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入字典值"
|
||||
v-model:value="form.dictCode"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号:" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注:">
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入备注"
|
||||
v-model:value="form.comments"/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SysDictEdit',
|
||||
emits: ['done', 'update:visible'],
|
||||
props: {
|
||||
// 弹窗是否打开
|
||||
visible: Boolean,
|
||||
// 修改回显的数据
|
||||
data: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表单数据
|
||||
form: Object.assign({}, this.data),
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
dictName: [
|
||||
{required: true, message: '请输入字典名称', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
dictCode: [
|
||||
{required: true, message: '请输入字典值', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
sortNumber: [
|
||||
{required: true, message: '请输入排序号', type: 'number', trigger: 'blur'}
|
||||
]
|
||||
},
|
||||
// 提交状态
|
||||
loading: false,
|
||||
// 是否是修改
|
||||
isUpdate: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
if (this.data) {
|
||||
this.form = Object.assign({}, this.data);
|
||||
this.isUpdate = true;
|
||||
} else {
|
||||
this.form = {};
|
||||
this.isUpdate = false;
|
||||
}
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/* 保存编辑 */
|
||||
save() {
|
||||
this.$refs.form.validate().then(() => {
|
||||
this.loading = true;
|
||||
this.$http[this.isUpdate ? 'put' : 'post']('/sys/dict', this.form).then(res => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
if (!this.isUpdate) {
|
||||
this.form = {};
|
||||
}
|
||||
this.updateVisible(false);
|
||||
this.$emit('done');
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/* 更新visible */
|
||||
updateVisible(value) {
|
||||
this.$emit('update:visible', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
214
src/views/system/login-record/index.vue
Normal file
214
src/views/system/login-record/index.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<div class="ele-body">
|
||||
<a-card :bordered="false">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form
|
||||
:model="where"
|
||||
:label-col="{md: {span: 6}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 18}, sm: {span: 24}}">
|
||||
<a-row>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="用户账号:">
|
||||
<a-input
|
||||
v-model:value.trim="where.username"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="用户名:">
|
||||
<a-input
|
||||
v-model:value.trim="where.nickname"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="登录时间:">
|
||||
<a-range-picker
|
||||
v-model:value="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
class="ele-fluid">
|
||||
<template #suffixIcon>
|
||||
<calendar-outlined/>
|
||||
</template>
|
||||
</a-range-picker>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item class="ele-text-right" :wrapper-col="{span: 24}">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="reload">查询</a-button>
|
||||
<a-button @click="reset">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="table"
|
||||
row-key="id"
|
||||
:datasource="url"
|
||||
:columns="columns"
|
||||
:where="where"
|
||||
:scroll="{x: 'max-content'}">
|
||||
<template #toolbar>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="exportData">
|
||||
<template #icon>
|
||||
<download-outlined/>
|
||||
</template>
|
||||
<span>导出</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #operType="{ record }">
|
||||
<a-tag :color="['green', 'red', '', 'orange'][record.operType]">
|
||||
{{ ['登录成功', '登录失败', '退出登录', '刷新TOKEN'][record.operType] }}
|
||||
</a-tag>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import XLSX from 'xlsx';
|
||||
import {
|
||||
DownloadOutlined,
|
||||
CalendarOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
|
||||
export default {
|
||||
name: 'SystemLoginRecord',
|
||||
components: {
|
||||
DownloadOutlined,
|
||||
CalendarOutlined
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表格数据接口
|
||||
url: '/sys/loginRecord/page',
|
||||
// 表格列配置
|
||||
columns: [
|
||||
{
|
||||
key: 'index',
|
||||
width: 48,
|
||||
align: 'center',
|
||||
customRender: ({index}) => this.$refs.table.tableIndex + index
|
||||
},
|
||||
{
|
||||
title: '账号',
|
||||
dataIndex: 'username',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'nickname',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: 'IP地址',
|
||||
dataIndex: 'ip',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '设备型号',
|
||||
dataIndex: 'device',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '操作系统',
|
||||
dataIndex: 'os',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '浏览器',
|
||||
dataIndex: 'browser',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '操作类型',
|
||||
dataIndex: 'operType',
|
||||
sorter: true,
|
||||
width: 120,
|
||||
slots: {customRender: 'operType'}
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'comments',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '登录时间',
|
||||
dataIndex: 'createTime',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
customRender: ({text}) => this.$util.toDateString(text)
|
||||
}
|
||||
],
|
||||
// 表格搜索条件
|
||||
where: {},
|
||||
// 日期范围选择
|
||||
daterange: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/* 刷新表格 */
|
||||
reload() {
|
||||
this.$refs.table.reload({page: 1, where: this.where});
|
||||
},
|
||||
/* 重置搜索 */
|
||||
reset() {
|
||||
this.where = {};
|
||||
this.daterange = [];
|
||||
this.reload();
|
||||
},
|
||||
/* 导出数据 */
|
||||
exportData() {
|
||||
let array = [['账号', '用户名', 'IP地址', '设备型号', '操作系统', '浏览器', '操作类型', '备注', '登录时间']];
|
||||
// 请求查询全部(不分页)的接口
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.get('/sys/loginRecord/page?page=1&limit=2000').then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
res.data.data.forEach(d => {
|
||||
array.push([
|
||||
d.username,
|
||||
d.nickname,
|
||||
d.ip,
|
||||
d.device,
|
||||
d.os,
|
||||
d.browser,
|
||||
['登录成功', '登录失败', '退出登录', '刷新TOKEN'][d.operType],
|
||||
d.comments,
|
||||
this.$util.toDateString(d.createTime)
|
||||
]);
|
||||
});
|
||||
this.$util.exportSheet(XLSX, array, '登录日志');
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
daterange() {
|
||||
if (this.daterange && this.daterange.length === 2) {
|
||||
this.where.createTimeStart = this.daterange[0] + ' 00:00:00';
|
||||
this.where.createTimeEnd = this.daterange[1] + ' 23:59:59';
|
||||
} else {
|
||||
this.where.createTimeStart = null;
|
||||
this.where.createTimeEnd = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
275
src/views/system/menu/index.vue
Normal file
275
src/views/system/menu/index.vue
Normal file
@@ -0,0 +1,275 @@
|
||||
<template>
|
||||
<div class="ele-body">
|
||||
<a-card :bordered="false">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form
|
||||
:model="where"
|
||||
:label-col="{md: {span: 6}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 18}, sm: {span: 24}}">
|
||||
<a-row>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="菜单名称:">
|
||||
<a-input
|
||||
v-model:value.trim="where.title"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="菜单地址:">
|
||||
<a-input
|
||||
v-model:value.trim="where.path"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="权限标识:">
|
||||
<a-input
|
||||
v-model:value.trim="where.authority"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item class="ele-text-right" :wrapper-col="{span: 24}">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="reload">查询</a-button>
|
||||
<a-button @click="reset">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="table"
|
||||
row-key="menuId"
|
||||
:datasource="url"
|
||||
:columns="columns"
|
||||
:where="where"
|
||||
:need-page="false"
|
||||
:parse-data="parseData"
|
||||
:expand-icon-column-index="1"
|
||||
:expanded-row-keys="expandedRowKeys"
|
||||
:scroll="{x: 'max-content'}"
|
||||
@expandedRowsChange="onExpandedRowsChange">
|
||||
<template #toolbar>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="openEdit()">
|
||||
<template #icon>
|
||||
<plus-outlined/>
|
||||
</template>
|
||||
<span>新建</span>
|
||||
</a-button>
|
||||
<a-button @click="expandAll">展开全部</a-button>
|
||||
<a-button @click="foldAll">折叠全部</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #menuType="{ record }">
|
||||
<a-tag
|
||||
v-if="isUrl(record.path)"
|
||||
color="orange">外链
|
||||
</a-tag>
|
||||
<a-tag
|
||||
v-else-if="isUrl(record.component)"
|
||||
color="green">内链
|
||||
</a-tag>
|
||||
<a-tag
|
||||
v-else
|
||||
:color="['blue', ''][record.menuType]">
|
||||
{{ ['菜单', '按钮'][record.menuType] }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a @click="openEdit(null, record.menuId)">添加</a>
|
||||
<a-divider type="vertical"/>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical"/>
|
||||
<a-popconfirm @confirm="remove(record)" title="确定要删除此菜单吗?">
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<menu-edit
|
||||
v-model:visible="showEdit"
|
||||
:data="current"
|
||||
:menu-list="menuList"
|
||||
@done="reload"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {PlusOutlined} from '@ant-design/icons-vue';
|
||||
import MenuEdit from './menu-edit';
|
||||
|
||||
export default {
|
||||
name: 'SystemMenu',
|
||||
components: {PlusOutlined, MenuEdit},
|
||||
data() {
|
||||
return {
|
||||
// 表格数据接口
|
||||
url: '/sys/menu',
|
||||
// 表格列配置
|
||||
columns: [
|
||||
{
|
||||
key: 'index',
|
||||
dataIndex: 'index',
|
||||
width: 48,
|
||||
align: 'center',
|
||||
customRender: ({index}) => index + 1
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'title',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '路由地址',
|
||||
dataIndex: 'path',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '组件路径',
|
||||
dataIndex: 'component',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '权限标识',
|
||||
dataIndex: 'authority',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sortNumber',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '可见',
|
||||
dataIndex: 'hide',
|
||||
sorter: true,
|
||||
customRender: ({text}) => ['是', '否'][text]
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'menuType',
|
||||
sorter: true,
|
||||
slots: {customRender: 'menuType'}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
sorter: true,
|
||||
customRender: ({text}) => this.$util.toDateString(text)
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
slots: {customRender: 'action'}
|
||||
}
|
||||
],
|
||||
// 表格搜索条件
|
||||
where: {},
|
||||
// 表格选中数据
|
||||
selection: [],
|
||||
// 当前编辑数据
|
||||
current: null,
|
||||
// 是否显示编辑弹窗
|
||||
showEdit: false,
|
||||
// 表格展开的行
|
||||
expandedRowKeys: [],
|
||||
// 全部菜单数据
|
||||
menuList: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/* 解析接口返回数据 */
|
||||
parseData(res) {
|
||||
res.data = this.$util.toTreeData(res.data.map(d => {
|
||||
d.key = d.menuId;
|
||||
d.value = d.menuId;
|
||||
return d;
|
||||
}), 'menuId', 'parentId');
|
||||
if (!Object.keys(this.where).length) {
|
||||
this.menuList = res.data;
|
||||
}
|
||||
if (!this.expandedRowKeys.length) {
|
||||
this.expandAll();
|
||||
}
|
||||
return res;
|
||||
},
|
||||
/* 刷新表格 */
|
||||
reload() {
|
||||
this.$refs.table.reload({where: this.where});
|
||||
},
|
||||
/* 重置搜索 */
|
||||
reset() {
|
||||
this.where = {};
|
||||
this.reload();
|
||||
},
|
||||
/* 打开编辑弹窗 */
|
||||
openEdit(row, parentId) {
|
||||
this.current = Object.assign({
|
||||
menuType: 0,
|
||||
hide: 0,
|
||||
parentId: parentId
|
||||
}, row);
|
||||
this.showEdit = true;
|
||||
},
|
||||
/* 删除单个 */
|
||||
remove(row) {
|
||||
if (row.children && row.children.length > 0) {
|
||||
this.$message.error('请先删除子节点');
|
||||
return;
|
||||
}
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.delete('/sys/menu/' + row.menuId).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.reload();
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
},
|
||||
/* 展开全部 */
|
||||
expandAll() {
|
||||
let keys = [];
|
||||
this.$util.eachTreeData(this.menuList, (d) => {
|
||||
if (d.children && d.children.length) {
|
||||
keys.push(d.menuId);
|
||||
}
|
||||
});
|
||||
this.expandedRowKeys = keys;
|
||||
},
|
||||
/* 折叠全部 */
|
||||
foldAll() {
|
||||
this.expandedRowKeys = [];
|
||||
},
|
||||
/* 展开的行变化 */
|
||||
onExpandedRowsChange(expandedRows) {
|
||||
this.expandedRowKeys = expandedRows;
|
||||
},
|
||||
/* 判断是否是网址 */
|
||||
isUrl(url) {
|
||||
return url && (
|
||||
url.startsWith('http://') ||
|
||||
url.startsWith('https://') ||
|
||||
url.startsWith('://'));
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
263
src/views/system/menu/menu-edit.vue
Normal file
263
src/views/system/menu/menu-edit.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<a-modal
|
||||
:width="680"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate?'修改菜单':'新建菜单'"
|
||||
:body-style="{paddingBottom: '8px'}"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save">
|
||||
<a-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="{md: {span: 7}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 17}, sm: {span: 24}}">
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="上级菜单" name="parentId">
|
||||
<a-tree-select
|
||||
allow-clear
|
||||
:tree-data="menuList"
|
||||
tree-default-expand-all
|
||||
placeholder="请选择上级菜单"
|
||||
v-model:value="form.parentId"
|
||||
:dropdown-style="{maxHeight: '360px', overflow: 'auto'}"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入菜单名称"
|
||||
v-model:value="form.title"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="菜单类型" name="menuType">
|
||||
<a-radio-group
|
||||
v-model:value="form.menuType"
|
||||
@change="onMenuTypeChange">
|
||||
<a-radio :value="0">菜单</a-radio>
|
||||
<a-radio :value="1">按钮</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="打开方式">
|
||||
<a-radio-group
|
||||
v-model:value="form.openType"
|
||||
:disabled="form.menuType === 1"
|
||||
@change="onOpenTypeChange">
|
||||
<a-radio :value="0">组件</a-radio>
|
||||
<a-radio :value="1">内链</a-radio>
|
||||
<a-radio :value="2">外链</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div style="margin-bottom: 22px;">
|
||||
<a-divider/>
|
||||
</div>
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<ele-icon-picker
|
||||
v-model:value="form.icon"
|
||||
:disabled="form.menuType===1"
|
||||
placeholder="请选择菜单图标"/>
|
||||
</a-form-item>
|
||||
<a-form-item name="path">
|
||||
<template #label>
|
||||
<a-tooltip
|
||||
v-if="form.openType === 2"
|
||||
title="需要以`http://`、`https://`、`//`开头">
|
||||
<question-circle-outlined
|
||||
style="vertical-align: -2px;margin-right: 4px;"/>
|
||||
</a-tooltip>
|
||||
<span>{{ form.openType === 2 ? '外链地址' : '路由地址' }}</span>
|
||||
</template>
|
||||
<a-input
|
||||
allow-clear
|
||||
v-model:value="form.path"
|
||||
:disabled="form.menuType===1"
|
||||
:placeholder="form.openType === 2 ? '请输入外链地址' : '请输入路由地址'"/>
|
||||
</a-form-item>
|
||||
<a-form-item name="component">
|
||||
<template #label>
|
||||
<a-tooltip
|
||||
v-if="form.openType === 1"
|
||||
title="需要以`http://`、`https://`、`//`开头">
|
||||
<question-circle-outlined
|
||||
style="vertical-align: -2px;margin-right: 4px;"/>
|
||||
</a-tooltip>
|
||||
<span>{{ form.openType === 1 ? '内链地址' : '组件路径' }}</span>
|
||||
</template>
|
||||
<a-input
|
||||
allow-clear
|
||||
v-model:value="form.component"
|
||||
:disabled="form.menuType === 1 || form.openType === 2"
|
||||
:placeholder="form.openType === 1 ? '请输入内链地址' : '请输入组件路径'"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="权限标识" name="authority">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入权限标识"
|
||||
v-model:value="form.authority"
|
||||
:disabled="form.menuType === 0"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号:" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否可见">
|
||||
<a-switch
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
v-model:checked="form.isShow"
|
||||
:disabled="form.menuType === 1"/>
|
||||
<a-tooltip title="选择不可见只注册路由不显示在侧边栏,比如添加页面应该选择不可见">
|
||||
<question-circle-outlined
|
||||
style="vertical-align: -3px;margin-left: 16px;"/>
|
||||
</a-tooltip>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EleIconPicker from 'ele-admin-pro/packages/ele-icon-picker';
|
||||
import {QuestionCircleOutlined} from '@ant-design/icons-vue';
|
||||
|
||||
export default {
|
||||
name: 'MenuEdit',
|
||||
components: {EleIconPicker, QuestionCircleOutlined},
|
||||
emits: [
|
||||
'done',
|
||||
'update:visible'
|
||||
],
|
||||
props: {
|
||||
// 弹窗是否打开
|
||||
visible: Boolean,
|
||||
// 修改回显的数据
|
||||
data: Object,
|
||||
// 全部菜单数据
|
||||
menuList: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表单数据
|
||||
form: this.initFormData(this.data),
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
title: [
|
||||
{required: true, type: 'string', message: '请输入菜单名称', trigger: 'blur'}
|
||||
],
|
||||
sortNumber: [
|
||||
{required: true, type: 'number', message: '请输入排序号', trigger: 'blur'}
|
||||
]
|
||||
},
|
||||
// 提交状态
|
||||
loading: false,
|
||||
// 是否是修改
|
||||
isUpdate: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
this.isUpdate = !!(this.data && this.data.menuId);
|
||||
this.form = this.initFormData(this.data);
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/* 保存编辑 */
|
||||
save() {
|
||||
this.$refs.form.validate().then(() => {
|
||||
this.loading = true;
|
||||
this.$http[this.isUpdate ? 'put' : 'post']('/sys/menu',
|
||||
Object.assign({}, this.form, {
|
||||
parentId: this.form.parentId || 0,
|
||||
hide: this.form.isShow ? 0 : 1
|
||||
})
|
||||
).then(res => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.updateVisible(false);
|
||||
this.$emit('done');
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/* 更新visible */
|
||||
updateVisible(value) {
|
||||
this.$emit('update:visible', value);
|
||||
},
|
||||
/* menuType选择改变 */
|
||||
onMenuTypeChange() {
|
||||
if (this.form.menuType === 0) {
|
||||
this.form.authority = '';
|
||||
} else {
|
||||
this.form.openType = 0;
|
||||
this.form.icon = '';
|
||||
this.form.path = '';
|
||||
this.form.component = '';
|
||||
this.form.hide = 0;
|
||||
this.form.isShow = true;
|
||||
}
|
||||
},
|
||||
/* openType选择改变 */
|
||||
onOpenTypeChange() {
|
||||
if (this.form.openType === 2) {
|
||||
this.form.component = '';
|
||||
}
|
||||
},
|
||||
/* 初始化form数据 */
|
||||
initFormData(data) {
|
||||
let form = {
|
||||
menuType: 0,
|
||||
openType: 0,
|
||||
hide: 0,
|
||||
isShow: true
|
||||
};
|
||||
if (data) {
|
||||
let openType = 0;
|
||||
if (this.isUrl(data.path)) {
|
||||
openType = 2;
|
||||
} else if (this.isUrl(data.component)) {
|
||||
openType = 1;
|
||||
}
|
||||
Object.assign(form, data, {
|
||||
parentId: data.parentId === 0 ? null : data.parentId,
|
||||
isShow: data.hide === 0,
|
||||
openType: openType
|
||||
});
|
||||
}
|
||||
return form;
|
||||
},
|
||||
/* 判断是否是网址 */
|
||||
isUrl(url) {
|
||||
return url && (
|
||||
url.startsWith('http://') ||
|
||||
url.startsWith('https://') ||
|
||||
url.startsWith('://'));
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
243
src/views/system/oper-record/index.vue
Normal file
243
src/views/system/oper-record/index.vue
Normal file
@@ -0,0 +1,243 @@
|
||||
<template>
|
||||
<div class="ele-body">
|
||||
<a-card :bordered="false">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form
|
||||
:model="where"
|
||||
:label-col="{md: {span: 6}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 18}, sm: {span: 24}}">
|
||||
<a-row>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="用户账号:">
|
||||
<a-input
|
||||
v-model:value.trim="where.username"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="操作模块:">
|
||||
<a-input
|
||||
v-model:value.trim="where.model"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="操作时间:">
|
||||
<a-range-picker
|
||||
v-model:value="daterange"
|
||||
:show-time="true"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
class="ele-fluid">
|
||||
<template #suffixIcon>
|
||||
<calendar-outlined/>
|
||||
</template>
|
||||
</a-range-picker>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item class="ele-text-right" :wrapper-col="{span: 24}">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="reload">查询</a-button>
|
||||
<a-button @click="reset">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="table"
|
||||
row-key="id"
|
||||
:datasource="url"
|
||||
:columns="columns"
|
||||
:where="where"
|
||||
:scroll="{x: 'max-content'}">
|
||||
<template #toolbar>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="exportData">
|
||||
<template #icon>
|
||||
<download-outlined/>
|
||||
</template>
|
||||
<span>导出</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #state="{ record }">
|
||||
<a-tag :color="['green', 'red'][record.state]">
|
||||
{{ ['正常', '异常'][record.state] }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a @click="openDetail(record)">详情</a>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
<!-- 详情弹窗 -->
|
||||
<oper-record-detail
|
||||
v-model:visible="showInfo"
|
||||
:data="current||{}"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import XLSX from 'xlsx';
|
||||
import {
|
||||
DownloadOutlined,
|
||||
CalendarOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import OperRecordDetail from './oper-record-detail';
|
||||
|
||||
export default {
|
||||
name: 'SystemOperRecord',
|
||||
components: {
|
||||
DownloadOutlined,
|
||||
CalendarOutlined,
|
||||
OperRecordDetail
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表格数据接口
|
||||
url: '/sys/operRecord/page',
|
||||
// 表格列配置
|
||||
columns: [
|
||||
{
|
||||
key: 'index',
|
||||
width: 48,
|
||||
align: 'center',
|
||||
customRender: ({index}) => this.$refs.table.tableIndex + index
|
||||
},
|
||||
{
|
||||
title: '账号',
|
||||
dataIndex: 'username',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'nickname',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '操作模块',
|
||||
dataIndex: 'model',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '操作功能',
|
||||
dataIndex: 'description',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '请求地址',
|
||||
dataIndex: 'url',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '方式',
|
||||
dataIndex: 'requestMethod',
|
||||
sorter: true,
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
sorter: true,
|
||||
width: 90,
|
||||
slots: {customRender: 'state'}
|
||||
},
|
||||
{
|
||||
title: '耗时',
|
||||
dataIndex: 'spendTime',
|
||||
sorter: true,
|
||||
width: 100,
|
||||
customRender: ({text}) => text / 1000 + 's'
|
||||
},
|
||||
{
|
||||
title: '操作时间',
|
||||
dataIndex: 'createTime',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
customRender: ({text}) => this.$util.toDateString(text)
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 90,
|
||||
align: 'center',
|
||||
slots: {customRender: 'action'}
|
||||
}
|
||||
],
|
||||
// 表格搜索条件
|
||||
where: {},
|
||||
// 当前选中数据
|
||||
current: null,
|
||||
// 是否显示查看弹窗
|
||||
showInfo: false,
|
||||
// 日期范围选择
|
||||
daterange: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/* 刷新表格 */
|
||||
reload() {
|
||||
this.$refs.table.reload({page: 1, where: this.where});
|
||||
},
|
||||
/* 重置搜索 */
|
||||
reset() {
|
||||
this.where = {};
|
||||
this.daterange = [];
|
||||
this.reload();
|
||||
},
|
||||
/* 详情 */
|
||||
openDetail(row) {
|
||||
this.current = row;
|
||||
this.showInfo = true;
|
||||
},
|
||||
/* 导出数据 */
|
||||
exportData() {
|
||||
let array = [['账号', '用户名', '操作模块', '操作功能', '请求地址', '请求方式', '状态', '耗时', '操作时间']];
|
||||
// 请求查询全部(不分页)的接口
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.get('/sys/loginRecord/page?page=1&limit=2000').then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
res.data.data.forEach(d => {
|
||||
array.push([
|
||||
d.username,
|
||||
d.nickname,
|
||||
d.model,
|
||||
d.description,
|
||||
d.url,
|
||||
d.requestMethod,
|
||||
['正常', '异常'][d.state],
|
||||
d.spendTime,
|
||||
this.$util.toDateString(d.createTime)
|
||||
]);
|
||||
});
|
||||
this.$util.exportSheet(XLSX, array, '操作日志');
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
daterange() {
|
||||
if (this.daterange && this.daterange.length === 2) {
|
||||
this.where.createTimeStart = this.daterange[0];
|
||||
this.where.createTimeEnd = this.daterange[1];
|
||||
} else {
|
||||
this.where.createTimeStart = null;
|
||||
this.where.createTimeEnd = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
126
src/views/system/oper-record/oper-record-detail.vue
Normal file
126
src/views/system/oper-record/oper-record-detail.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<!-- 详情弹窗 -->
|
||||
<template>
|
||||
<a-modal
|
||||
title="详情"
|
||||
:width="640"
|
||||
:footer="null"
|
||||
:visible="visible"
|
||||
@update:visible="updateVisible">
|
||||
<a-form
|
||||
class="ele-form-detail"
|
||||
:label-col="{sm: {span: 8}, xs: {span: 6}}"
|
||||
:wrapper-col="{sm: {span: 16}, xs: {span: 18}}">
|
||||
<a-row :gutter="16">
|
||||
<a-col :sm="12" :xs="24">
|
||||
<a-form-item label="操作人:">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.nickname }}({{ data.username }})
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="操作模块:">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.model }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="操作时间:">
|
||||
<div class="ele-text-secondary">
|
||||
{{ $util.toDateString(data.createTime) }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="请求方式:">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.requestMethod }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :sm="12" :xs="24">
|
||||
<a-form-item label="IP地址:">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.ip }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="操作功能:">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.description }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="请求耗时:">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.spendTime / 1000 }}s
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="请求状态:">
|
||||
<a-tag :color="['green', 'red'][data.state]">
|
||||
{{ ['正常', '异常'][data.state] }}
|
||||
</a-tag>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div style="margin: 12px 0;">
|
||||
<a-divider/>
|
||||
</div>
|
||||
<a-form-item
|
||||
label="请求地址:"
|
||||
:label-col="{sm: {span: 4}, xs: {span: 6}}"
|
||||
:wrapper-col="{sm: {span: 20}, xs: {span: 18}}">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.url }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="调用方法:"
|
||||
:label-col="{sm: {span: 4}, xs: {span: 6}}"
|
||||
:wrapper-col="{sm: {span: 20}, xs: {span: 18}}">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.operMethod }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="请求参数:"
|
||||
:label-col="{sm: {span: 4}, xs: {span: 6}}"
|
||||
:wrapper-col="{sm: {span: 20}, xs: {span: 18}}">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.param }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="返回结果:"
|
||||
:label-col="{sm: {span: 4}, xs: {span: 6}}"
|
||||
:wrapper-col="{sm: {span: 20}, xs: {span: 18}}">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.result }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="备注:"
|
||||
:label-col="{sm: {span: 4}, xs: {span: 6}}"
|
||||
:wrapper-col="{sm: {span: 20}, xs: {span: 18}}">
|
||||
<div class="ele-text-secondary">
|
||||
{{ data.comments }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'OperRecordDetail',
|
||||
emits: ['update:visible'],
|
||||
props: {
|
||||
// 弹窗是否打开
|
||||
visible: Boolean,
|
||||
// 数据
|
||||
data: Object
|
||||
},
|
||||
methods: {
|
||||
/* 更新visible */
|
||||
updateVisible(value) {
|
||||
this.$emit('update:visible', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
166
src/views/system/organization/index.vue
Normal file
166
src/views/system/organization/index.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="ele-body ele-body-card">
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="6" :md="24" :sm="24" :xs="24">
|
||||
<a-card
|
||||
:bordered="false"
|
||||
:body-style="{padding: '24px 16px'}">
|
||||
<div class="ele-table-tool">
|
||||
<a-space size="middle">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="openEdit()">新建
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="openEdit(current)"
|
||||
:disabled="!current">修改
|
||||
</a-button>
|
||||
<a-button
|
||||
danger
|
||||
type="primary"
|
||||
@click="remove"
|
||||
:disabled="!current">删除
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<a-tree
|
||||
:tree-data="data"
|
||||
v-model:expanded-keys="expandedRowKeys"
|
||||
v-model:selected-keys="selectedRowKeys"
|
||||
@select="onTreeSelect"/>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :lg="18" :md="24" :sm="24" :xs="24">
|
||||
<a-card :bordered="false">
|
||||
<org-user-list
|
||||
v-if="current"
|
||||
:organization-id="current.organizationId"
|
||||
:organization-list="data"/>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<org-edit
|
||||
v-model:visible="showEdit"
|
||||
:data="editData"
|
||||
:organization-list="data"
|
||||
@done="query"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createVNode} from 'vue';
|
||||
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
|
||||
import OrgUserList from './org-user-list';
|
||||
import OrgEdit from './org-edit';
|
||||
|
||||
export default {
|
||||
name: 'SystemOrganization',
|
||||
components: {
|
||||
OrgUserList,
|
||||
OrgEdit
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
// 树形数据
|
||||
data: [],
|
||||
// 树展开的key
|
||||
expandedRowKeys: [],
|
||||
// 树选中的key
|
||||
selectedRowKeys: [],
|
||||
// 选中数据
|
||||
current: null,
|
||||
// 是否显示表单弹窗
|
||||
showEdit: false,
|
||||
// 编辑回显数据
|
||||
editData: null
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.query();
|
||||
},
|
||||
methods: {
|
||||
/* 查询 */
|
||||
query() {
|
||||
this.loading = true;
|
||||
this.$http.get('/sys/organization').then(res => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
let eks = [];
|
||||
res.data.data.forEach(d => {
|
||||
d.key = d.organizationId;
|
||||
d.value = d.organizationId;
|
||||
d.title = d.organizationName;
|
||||
eks.push(d.key);
|
||||
});
|
||||
this.expandedRowKeys = eks;
|
||||
this.data = this.$util.toTreeData(res.data.data, 'organizationId', 'parentId');
|
||||
if (this.data.length) {
|
||||
this.selectedRowKeys = [this.data[0].key];
|
||||
this.onTreeSelect();
|
||||
} else {
|
||||
this.selectedRowKeys = [];
|
||||
this.current = null;
|
||||
}
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
},
|
||||
/* 选择数据 */
|
||||
onTreeSelect() {
|
||||
this.$util.eachTreeData(this.data, (d) => {
|
||||
if (this.selectedRowKeys.indexOf(d.key) !== -1) {
|
||||
this.current = d;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
/* 打开编辑弹窗 */
|
||||
openEdit(item) {
|
||||
this.editData = Object.assign({}, {
|
||||
parentId: this.current.parentId
|
||||
}, item);
|
||||
this.showEdit = true;
|
||||
},
|
||||
/* 删除 */
|
||||
remove() {
|
||||
this.$confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的机构吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.delete('/sys/organization/' + this.current.organizationId).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.query();
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@media screen and (min-width: 768px) {
|
||||
.ant-card {
|
||||
min-height: calc(100vh - 122px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
201
src/views/system/organization/org-edit.vue
Normal file
201
src/views/system/organization/org-edit.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<!-- 机构编辑弹窗 -->
|
||||
<template>
|
||||
<a-modal
|
||||
:width="680"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate?'修改机构':'添加机构'"
|
||||
:body-style="{paddingBottom: '8px'}"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save">
|
||||
<a-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="{md: {span: 7}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 17}, sm: {span: 24}}">
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="上级机构:" name="parentId">
|
||||
<a-tree-select
|
||||
allow-clear
|
||||
tree-default-expand-all
|
||||
placeholder="请选择上级机构"
|
||||
v-model:value="form.parentId"
|
||||
:tree-data="organizationList"
|
||||
:dropdown-style="{maxHeight: '360px', overflow: 'auto'}"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="机构名称:" name="organizationName">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入机构名称"
|
||||
v-model:value="form.organizationName"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="机构全称:" name="organizationFullName">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="100"
|
||||
placeholder="请输入机构全称"
|
||||
v-model:value="form.organizationFullName"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="机构代码:" name="organizationCode">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入机构代码"
|
||||
v-model:value="form.organizationCode"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="机构类型:" name="organizationType">
|
||||
<a-select
|
||||
allow-clear
|
||||
placeholder="请选择机构类型"
|
||||
v-model:value="form.organizationType">
|
||||
<a-select-option
|
||||
v-for="item in organizationTypeList"
|
||||
:key="item.dictDataId"
|
||||
:value="item.dictDataId">
|
||||
{{ item.dictDataName }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号:" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="99999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注:">
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入备注"
|
||||
v-model:value="form.comments"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'OrgEdit',
|
||||
emits: ['done', 'update:visible'],
|
||||
props: {
|
||||
// 弹窗是否打开
|
||||
visible: Boolean,
|
||||
// 修改回显的数据
|
||||
data: Object,
|
||||
// 全部机构
|
||||
organizationList: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表单数据
|
||||
form: Object.assign({}, this.data, {
|
||||
parentId: this.data ? (this.data.parentId === 0 ? null : this.data.parentId) : null
|
||||
}),
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
organizationName: [
|
||||
{required: true, message: '请输入机构名称', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
organizationFullName: [
|
||||
{required: true, message: '请输入机构全称', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
organizationCode: [
|
||||
{required: true, message: '请输入机构代码', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
organizationType: [
|
||||
{required: true, message: '请选择机构类型', type: 'number', trigger: 'blur'}
|
||||
],
|
||||
sortNumber: [
|
||||
{required: true, message: '请输入排序号', type: 'number', trigger: 'blur'}
|
||||
]
|
||||
},
|
||||
// 提交状态
|
||||
loading: false,
|
||||
// 是否是修改
|
||||
isUpdate: false,
|
||||
// 机构类型列表
|
||||
organizationTypeList: []
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
if (this.data) {
|
||||
this.form = Object.assign({}, this.data, {
|
||||
parentId: this.data.parentId === 0 ? null : this.data.parentId
|
||||
});
|
||||
this.isUpdate = !!this.data.organizationId;
|
||||
} else {
|
||||
this.form = {};
|
||||
this.isUpdate = false;
|
||||
}
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate();
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.queryOrganizationType(); // 获取机构类型
|
||||
},
|
||||
methods: {
|
||||
/* 保存编辑 */
|
||||
save() {
|
||||
this.$refs.form.validate().then(() => {
|
||||
this.loading = true;
|
||||
this.$http[this.isUpdate ? 'put' : 'post']('/sys/organization',
|
||||
Object.assign({}, this.form, {
|
||||
parentId: this.form.parentId || 0
|
||||
})
|
||||
).then(res => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
if (!this.isUpdate) {
|
||||
this.form = {};
|
||||
}
|
||||
this.updateVisible(false);
|
||||
this.$emit('done');
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/* 更新visible */
|
||||
updateVisible(value) {
|
||||
this.$emit('update:visible', value);
|
||||
},
|
||||
/* 查询机构类型 */
|
||||
queryOrganizationType() {
|
||||
this.$http.get('/sys/dictdata', {
|
||||
params: {
|
||||
dictCode: 'organization_type'
|
||||
}
|
||||
}).then(res => {
|
||||
if (res.data.code === 0) {
|
||||
this.organizationTypeList = res.data.data;
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
248
src/views/system/organization/org-user-edit.vue
Normal file
248
src/views/system/organization/org-user-edit.vue
Normal file
@@ -0,0 +1,248 @@
|
||||
<!-- 用户编辑弹窗 -->
|
||||
<template>
|
||||
<a-modal
|
||||
:width="680"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate?'修改用户':'新建用户'"
|
||||
:body-style="{paddingBottom: '8px'}"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save">
|
||||
<a-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="{md: {span: 7}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 17}, sm: {span: 24}}">
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="所属机构:">
|
||||
<a-tree-select
|
||||
allow-clear
|
||||
tree-default-expand-all
|
||||
placeholder="请选择所属机构"
|
||||
v-model:value="form.organizationId"
|
||||
:tree-data="organizationList"
|
||||
:dropdown-style="{maxHeight: '360px', overflow: 'auto'}"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="用户账号:" name="username">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入用户账号"
|
||||
v-model:value="form.username"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="用户名:" name="nickname">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入用户名"
|
||||
v-model:value="form.nickname"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="性别:" name="sex">
|
||||
<a-select
|
||||
allow-clear
|
||||
placeholder="请选择性别"
|
||||
v-model:value="form.sex">
|
||||
<a-select-option :value="1">男</a-select-option>
|
||||
<a-select-option :value="2">女</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="角色:" name="roleIds">
|
||||
<a-select
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
placeholder="请选择角色"
|
||||
v-model:value="form.roleIds">
|
||||
<a-select-option
|
||||
v-for="item in roleList"
|
||||
:key="item.roleId"
|
||||
:value="item.roleId">
|
||||
{{ item.roleName }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="手机号:" name="phone">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="11"
|
||||
placeholder="请输入手机号"
|
||||
v-model:value="form.phone"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="邮箱:" name="email">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="100"
|
||||
placeholder="请输入邮箱"
|
||||
v-model:value="form.email"/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="!isUpdate"
|
||||
label="登录密码:"
|
||||
name="password">
|
||||
<a-input-password
|
||||
:maxlength="20"
|
||||
v-model:value="form.password"
|
||||
placeholder="请输入登录密码"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="个人简介:">
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入个人简介"
|
||||
v-model:value="form.introduction"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import validate from 'ele-admin-pro/packages/validate';
|
||||
|
||||
export default {
|
||||
name: 'OrgUserEdit',
|
||||
emits: ['done', 'update:visible'],
|
||||
props: {
|
||||
// 弹窗是否打开
|
||||
visible: Boolean,
|
||||
// 修改回显的数据
|
||||
data: Object,
|
||||
// 全部机构
|
||||
organizationList: Array,
|
||||
// 机构id
|
||||
organizationId: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表单数据
|
||||
form: Object.assign({}, this.data, {
|
||||
organizationId: this.organizationId
|
||||
}),
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
trigger: 'blur',
|
||||
asyncValidator: (rule, value) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!value) {
|
||||
return reject('请输入用户账号');
|
||||
}
|
||||
this.$http.get('/sys/user?username=' + value).then(res => {
|
||||
if (res.data.code !== 0 || !res.data.data.length) {
|
||||
return resolve();
|
||||
}
|
||||
if (this.isUpdate && res.data.data[0].username === this.data.username) {
|
||||
return resolve();
|
||||
}
|
||||
reject('账号已经存在');
|
||||
}).catch(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
nickname: [
|
||||
{required: true, message: '请输入用户名', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
sex: [
|
||||
{required: true, message: '请选择性别', type: 'number', trigger: 'blur'}
|
||||
],
|
||||
roleIds: [
|
||||
{required: true, message: '请选择角色', type: 'array', trigger: 'blur'}
|
||||
],
|
||||
email: [
|
||||
{pattern: validate.email, message: '邮箱格式不正确', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
password: [
|
||||
{required: true, pattern: /^[\S]{5,18}$/, message: '密码必须为5-18位非空白字符', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
phone: [
|
||||
{pattern: validate.phone, message: '手机号格式不正确', type: 'string', trigger: 'blur'}
|
||||
]
|
||||
},
|
||||
// 提交状态
|
||||
loading: false,
|
||||
// 是否是修改
|
||||
isUpdate: false,
|
||||
// 角色列表
|
||||
roleList: []
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
if (this.data) {
|
||||
this.form = Object.assign({}, this.data, {
|
||||
roleIds: this.data.roles.map(d => d.roleId)
|
||||
});
|
||||
this.isUpdate = true;
|
||||
} else {
|
||||
this.form = {organizationId: this.organizationId};
|
||||
this.isUpdate = false;
|
||||
}
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate();
|
||||
}
|
||||
},
|
||||
organizationId() {
|
||||
if (!this.isUpdate) {
|
||||
this.form = {organizationId: this.organizationId};
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.queryRoles(); // 查询角色列表
|
||||
},
|
||||
methods: {
|
||||
/* 保存编辑 */
|
||||
save() {
|
||||
this.$refs.form.validate().then(() => {
|
||||
this.loading = true;
|
||||
this.$http[this.isUpdate ? 'put' : 'post']('/sys/user', this.form).then(res => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
if (!this.isUpdate) {
|
||||
this.form = {};
|
||||
}
|
||||
this.updateVisible(false);
|
||||
this.$emit('done');
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/* 更新visible */
|
||||
updateVisible(value) {
|
||||
this.$emit('update:visible', value);
|
||||
},
|
||||
/* 查询角色列表 */
|
||||
queryRoles() {
|
||||
this.$http.get('/sys/role').then(res => {
|
||||
if (res.data.code === 0) {
|
||||
this.roleList = res.data.data;
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
207
src/views/system/organization/org-user-list.vue
Normal file
207
src/views/system/organization/org-user-list.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="table"
|
||||
row-key="userId"
|
||||
:datasource="url"
|
||||
:columns="columns"
|
||||
:where="where"
|
||||
tool-class="ele-toolbar-form"
|
||||
:scroll="{x: 'max-content'}">
|
||||
<template #toolbar>
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="6" :md="8" :sm="24" :xs="24">
|
||||
<a-input
|
||||
v-model:value.trim="where.username"
|
||||
placeholder="请输入用户账号"
|
||||
allow-clear/>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="8" :sm="24" :xs="24">
|
||||
<a-input
|
||||
v-model:value.trim="where.nickname"
|
||||
placeholder="请输入用户名"
|
||||
allow-clear/>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="8" :sm="24" :xs="24">
|
||||
<a-space size="middle">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="reload">查询
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="openEdit()">新建
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<template #roles="{ record }">
|
||||
<a-tag
|
||||
v-for="(item, index) in record.roles"
|
||||
:key="index"
|
||||
color="blue">{{ item.roleName }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template #state="{ text,record }">
|
||||
<a-switch
|
||||
:checked="text===0"
|
||||
@change="(checked) => changeState(checked, record)"/>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical"/>
|
||||
<a-popconfirm
|
||||
title="确定要删除此用户吗?"
|
||||
@confirm="remove(record)">
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
<!-- 编辑弹窗 -->
|
||||
<org-user-edit
|
||||
v-model:visible="showEdit"
|
||||
:data="current"
|
||||
:organization-list="organizationList"
|
||||
:organization-id="organizationId"
|
||||
@done="reload"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OrgUserEdit from './org-user-edit';
|
||||
|
||||
export default {
|
||||
name: 'SysOrgUserList',
|
||||
components: {OrgUserEdit},
|
||||
props: {
|
||||
// 机构id
|
||||
organizationId: Number,
|
||||
// 全部机构
|
||||
organizationList: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表格数据接口
|
||||
url: '/sys/user/page',
|
||||
// 表格列配置
|
||||
columns: [
|
||||
{
|
||||
key: 'index',
|
||||
width: 48,
|
||||
align: 'center',
|
||||
customRender: ({index}) => this.$refs.table.tableIndex + index
|
||||
},
|
||||
{
|
||||
title: '用户账号',
|
||||
dataIndex: 'username',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'nickname',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
dataIndex: 'sexName',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
dataIndex: 'phone',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
key: 'roles',
|
||||
slots: {customRender: 'roles'}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
customRender: ({text}) => this.$util.toDateString(text)
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
sorter: true,
|
||||
width: 90,
|
||||
align: 'center',
|
||||
slots: {customRender: 'state'}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
slots: {customRender: 'action'}
|
||||
}
|
||||
],
|
||||
// 表格搜索条件
|
||||
where: {
|
||||
organizationId: this.organizationId
|
||||
},
|
||||
// 当前编辑数据
|
||||
current: null,
|
||||
// 是否显示编辑弹窗
|
||||
showEdit: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/* 刷新表格 */
|
||||
reload() {
|
||||
this.$refs.table.reload({page: 1, where: this.where});
|
||||
},
|
||||
/* 打开编辑弹窗 */
|
||||
openEdit(row) {
|
||||
this.current = row;
|
||||
this.showEdit = true;
|
||||
},
|
||||
/* 删除单个 */
|
||||
remove(row) {
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.delete('/sys/user/' + row.userId).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.reload();
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
},
|
||||
/* 修改用户状态 */
|
||||
changeState(checked, row) {
|
||||
let params = new FormData();
|
||||
params.append('state', checked ? 0 : 1);
|
||||
this.$http.put('/sys/user/state/' + row.userId, params).then(res => {
|
||||
if (res.data.code === 0) {
|
||||
row.state = checked ? 0 : 1;
|
||||
this.$message.success(res.data.msg);
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
/* 监听机构id变化 */
|
||||
organizationId() {
|
||||
this.where.organizationId = this.organizationId;
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
238
src/views/system/role/index.vue
Normal file
238
src/views/system/role/index.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<div class="ele-body">
|
||||
<a-card :bordered="false">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form
|
||||
:model="where"
|
||||
:label-col="{md: {span: 6}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 18}, sm: {span: 24}}">
|
||||
<a-row>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="角色名称:">
|
||||
<a-input
|
||||
v-model:value.trim="where.roleName"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="角色标识:">
|
||||
<a-input
|
||||
v-model:value.trim="where.roleCode"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="备注:">
|
||||
<a-input
|
||||
v-model:value.trim="where.comments"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item class="ele-text-right" :wrapper-col="{span: 24}">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="reload">查询</a-button>
|
||||
<a-button @click="reset">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="table"
|
||||
row-key="roleId"
|
||||
:datasource="url"
|
||||
:columns="columns"
|
||||
:where="where"
|
||||
v-model:selection="selection"
|
||||
:scroll="{x: 'max-content'}">
|
||||
<template #toolbar>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="openEdit()">
|
||||
<template #icon>
|
||||
<plus-outlined/>
|
||||
</template>
|
||||
<span>新建</span>
|
||||
</a-button>
|
||||
<a-button type="primary" danger @click="removeBatch">
|
||||
<template #icon>
|
||||
<delete-outlined/>
|
||||
</template>
|
||||
<span>删除</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical"/>
|
||||
<a @click="openAuth(record)">分配权限</a>
|
||||
<a-divider type="vertical"/>
|
||||
<a-popconfirm
|
||||
title="确定要删除此角色吗?"
|
||||
@confirm="remove(record)">
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<role-edit
|
||||
v-model:visible="showEdit"
|
||||
:data="current"
|
||||
@done="reload"/>
|
||||
<!-- 权限分配弹窗 -->
|
||||
<role-auth
|
||||
v-model:visible="showAuth"
|
||||
:data="current"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createVNode} from 'vue'
|
||||
import {
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
ExclamationCircleOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import RoleEdit from './role-edit';
|
||||
import RoleAuth from './role-auth';
|
||||
|
||||
export default {
|
||||
name: 'SystemRole',
|
||||
components: {
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
RoleEdit,
|
||||
RoleAuth
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表格数据接口
|
||||
url: '/sys/role/page',
|
||||
// 表格列配置
|
||||
columns: [
|
||||
{
|
||||
key: 'index',
|
||||
width: 48,
|
||||
customRender: ({index}) => this.$refs.table.tableIndex + index
|
||||
},
|
||||
{
|
||||
title: '角色名称',
|
||||
dataIndex: 'roleName',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '角色标识',
|
||||
dataIndex: 'roleCode',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'comments',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
sorter: true,
|
||||
customRender: ({text}) => this.$util.toDateString(text)
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
slots: {customRender: 'action'}
|
||||
}
|
||||
],
|
||||
// 表格搜索条件
|
||||
where: {},
|
||||
// 表格选中数据
|
||||
selection: [],
|
||||
// 当前编辑数据
|
||||
current: null,
|
||||
// 是否显示编辑弹窗
|
||||
showEdit: false,
|
||||
// 是否显示权限分配弹窗
|
||||
showAuth: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/* 搜索 */
|
||||
reload() {
|
||||
this.selection = [];
|
||||
this.$refs.table.reload({page: 1, where: this.where});
|
||||
},
|
||||
/* 重置搜索 */
|
||||
reset() {
|
||||
this.where = {};
|
||||
this.reload();
|
||||
},
|
||||
/* 删除单个 */
|
||||
remove(row) {
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.delete('/sys/role/' + row.roleId).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.reload();
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
},
|
||||
/* 批量删除 */
|
||||
removeBatch() {
|
||||
if (!this.selection.length) {
|
||||
this.$message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的角色吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.delete('/sys/role/batch', {
|
||||
data: this.selection.map(d => d.roleId)
|
||||
}).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.reload();
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
/* 打开编辑弹窗 */
|
||||
openEdit(row) {
|
||||
this.current = row;
|
||||
this.showEdit = true;
|
||||
},
|
||||
/* 打开权限分配弹窗 */
|
||||
openAuth(row) {
|
||||
this.current = row;
|
||||
this.showAuth = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
134
src/views/system/role/role-auth.vue
Normal file
134
src/views/system/role/role-auth.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<!-- 角色权限分配弹窗 -->
|
||||
<template>
|
||||
<a-modal
|
||||
:width="460"
|
||||
title="分配权限"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save">
|
||||
<a-spin :spinning="authLoading">
|
||||
<div style="height: 60vh;" class="ele-scrollbar-hover">
|
||||
<a-tree
|
||||
checkable
|
||||
:tree-data="authData"
|
||||
v-model:expandedKeys="expandKeys"
|
||||
v-model:checkedKeys="checkedKeys"/>
|
||||
</div>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RoleAuth',
|
||||
emits: ['update:visible'],
|
||||
props: {
|
||||
// 弹窗是否打开
|
||||
visible: Boolean,
|
||||
// 当前角色数据
|
||||
data: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 权限数据
|
||||
authData: [],
|
||||
// 权限数据请求状态
|
||||
authLoading: false,
|
||||
// 提交状态
|
||||
loading: false,
|
||||
// 角色权限展开的keys
|
||||
expandKeys: [],
|
||||
// 角色权限选中的keys
|
||||
checkedKeys: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible() {
|
||||
if (this.visible) {
|
||||
this.query();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/* 查询权限数据 */
|
||||
query() {
|
||||
this.authData = [];
|
||||
this.expandKeys = [];
|
||||
this.checkedKeys = [];
|
||||
if (!this.data) {
|
||||
return;
|
||||
}
|
||||
this.authLoading = true;
|
||||
this.$http.get('/sys/role/menu', {
|
||||
params: {
|
||||
roleId: this.data.roleId
|
||||
}
|
||||
}).then(res => {
|
||||
this.authLoading = false;
|
||||
if (res.data.code === 0) {
|
||||
let eks = [], cks = [];
|
||||
// 转成树形结构的数据
|
||||
this.authData = this.$util.toTreeData({
|
||||
data: res.data.data,
|
||||
idKey: 'menuId',
|
||||
pidKey: 'parentId',
|
||||
addPIds: true,
|
||||
parentIds: []
|
||||
});
|
||||
// 全部默认展开以及回显选中的数据
|
||||
this.$util.eachTreeData(this.authData, (d) => {
|
||||
d.key = d.menuId;
|
||||
eks.push(d.key);
|
||||
if (d.checked && (!d.children || !d.children.length)) {
|
||||
cks.push(d.key);
|
||||
}
|
||||
});
|
||||
this.expandKeys = eks;
|
||||
this.checkedKeys = cks;
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.authLoading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
},
|
||||
/* 保存权限分配 */
|
||||
save() {
|
||||
this.loading = true;
|
||||
// 获取选中的id,包含所有半选的父级的id
|
||||
const ids = new Set();
|
||||
this.$util.eachTreeData(this.authData, (d) => {
|
||||
if (this.checkedKeys.some(c => c === d.key)) {
|
||||
ids.add(d.key);
|
||||
if (d.parentIds) {
|
||||
d.parentIds.forEach((id) => {
|
||||
ids.add(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
this.$http.put('/sys/role/menu/' + this.data.roleId, Array.from(ids)).then(res => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.updateVisible(false);
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
},
|
||||
/* 更新visible */
|
||||
updateVisible(value) {
|
||||
this.$emit('update:visible', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
118
src/views/system/role/role-edit.vue
Normal file
118
src/views/system/role/role-edit.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<!-- 角色编辑弹窗 -->
|
||||
<template>
|
||||
<a-modal
|
||||
:width="460"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate?'修改角色':'添加角色'"
|
||||
:body-style="{paddingBottom: '8px'}"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save">
|
||||
<a-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="{md: {span: 5}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 19}, sm: {span: 24}}">
|
||||
<a-form-item label="角色名称:" name="roleName">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入角色名称"
|
||||
v-model:value="form.roleName"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="角色标识:" name="roleCode">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入角色标识"
|
||||
v-model:value="form.roleCode"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注:">
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入备注"
|
||||
v-model:value="form.comments"/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RoleEdit',
|
||||
emits: ['done', 'update:visible'],
|
||||
props: {
|
||||
// 弹窗是否打开
|
||||
visible: Boolean,
|
||||
// 修改回显的数据
|
||||
data: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表单数据
|
||||
form: Object.assign({}, this.data),
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
roleName: [
|
||||
{required: true, message: '请输入角色名称', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
roleCode: [
|
||||
{required: true, message: '请输入角色标识', type: 'string', trigger: 'blur'}
|
||||
]
|
||||
},
|
||||
// 提交状态
|
||||
loading: false,
|
||||
// 是否是修改
|
||||
isUpdate: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
if (this.data) {
|
||||
this.form = Object.assign({}, this.data);
|
||||
this.isUpdate = true;
|
||||
} else {
|
||||
this.form = {};
|
||||
this.isUpdate = false;
|
||||
}
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate();
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/* 保存编辑 */
|
||||
save() {
|
||||
this.$refs.form.validate().then(() => {
|
||||
this.loading = true;
|
||||
this.$http[this.isUpdate ? 'put' : 'post']('/sys/role', this.form).then(res => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
if (!this.isUpdate) {
|
||||
this.form = {};
|
||||
}
|
||||
this.updateVisible(false);
|
||||
this.$emit('done');
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/* 更新visible */
|
||||
updateVisible(value) {
|
||||
this.$emit('update:visible', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
321
src/views/system/user/index.vue
Normal file
321
src/views/system/user/index.vue
Normal file
@@ -0,0 +1,321 @@
|
||||
<template>
|
||||
<div class="ele-body">
|
||||
<a-card :bordered="false">
|
||||
<!-- 搜索表单 -->
|
||||
<a-form
|
||||
:model="where"
|
||||
:label-col="{md: {span: 6}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 18}, sm: {span: 24}}">
|
||||
<a-row>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="用户账号:">
|
||||
<a-input
|
||||
v-model:value.trim="where.username"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="用户名:">
|
||||
<a-input
|
||||
v-model:value.trim="where.nickname"
|
||||
placeholder="请输入"
|
||||
allow-clear/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="性别:">
|
||||
<a-select
|
||||
v-model:value="where.sex"
|
||||
placeholder="请选择"
|
||||
allow-clear>
|
||||
<a-select-option :value="1">男</a-select-option>
|
||||
<a-select-option :value="2">女</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :sm="24" :xs="24">
|
||||
<a-form-item class="ele-text-right" :wrapper-col="{span: 24}">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="reload">查询</a-button>
|
||||
<a-button @click="reset">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="table"
|
||||
row-key="userId"
|
||||
:datasource="url"
|
||||
:columns="columns"
|
||||
:where="where"
|
||||
v-model:selection="selection"
|
||||
:scroll="{x: 'max-content'}">
|
||||
<template #toolbar>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="openEdit()">
|
||||
<template #icon>
|
||||
<plus-outlined/>
|
||||
</template>
|
||||
<span>新建</span>
|
||||
</a-button>
|
||||
<a-button type="primary" danger @click="removeBatch">
|
||||
<template #icon>
|
||||
<delete-outlined/>
|
||||
</template>
|
||||
<span>删除</span>
|
||||
</a-button>
|
||||
<a-button @click="showImport=true">
|
||||
<template #icon>
|
||||
<upload-outlined/>
|
||||
</template>
|
||||
<span>导入</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #nickname="{ record }">
|
||||
<router-link :to="'/system/user/info?id=' + record.userId">
|
||||
{{ record.nickname }}
|
||||
</router-link>
|
||||
</template>
|
||||
<template #roles="{ record }">
|
||||
<a-tag
|
||||
v-for="(item, index) in record.roles"
|
||||
:key="index"
|
||||
color="blue">
|
||||
{{ item.roleName }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template #state="{ text, record }">
|
||||
<a-switch
|
||||
:checked="text===0"
|
||||
@change="(checked) => editState(checked, record)"/>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical"/>
|
||||
<a @click="resetPsw(record)">重置密码</a>
|
||||
<a-divider type="vertical"/>
|
||||
<a-popconfirm
|
||||
title="确定要删除此用户吗?"
|
||||
@confirm="remove(record)">
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<user-edit
|
||||
v-model:visible="showEdit"
|
||||
:data="current"
|
||||
@done="reload"/>
|
||||
<!-- 导入弹窗 -->
|
||||
<user-import
|
||||
v-model:visible="showImport"
|
||||
@done="reload"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createVNode} from 'vue';
|
||||
import {
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
UploadOutlined,
|
||||
ExclamationCircleOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import UserEdit from './user-edit';
|
||||
import UserImport from './user-import';
|
||||
|
||||
export default {
|
||||
name: 'SystemUser',
|
||||
components: {
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
UploadOutlined,
|
||||
UserImport,
|
||||
UserEdit
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表格数据接口
|
||||
url: '/sys/user/page',
|
||||
// 表格列配置
|
||||
columns: [
|
||||
{
|
||||
key: 'index',
|
||||
width: 48,
|
||||
customRender: ({index}) => this.$refs.table.tableIndex + index
|
||||
},
|
||||
{
|
||||
title: '用户账号',
|
||||
dataIndex: 'username',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'nickname',
|
||||
sorter: true,
|
||||
slots: {customRender: 'nickname'}
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
dataIndex: 'sexName',
|
||||
sorter: true
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
dataIndex: 'phone',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
key: 'roles',
|
||||
slots: {customRender: 'roles'}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
sorter: true,
|
||||
customRender: ({text}) => this.$util.toDateString(text)
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
sorter: true,
|
||||
width: 90,
|
||||
align: 'center',
|
||||
slots: {customRender: 'state'}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
slots: {customRender: 'action'}
|
||||
}
|
||||
],
|
||||
// 表格搜索条件
|
||||
where: {},
|
||||
// 表格选中数据
|
||||
selection: [],
|
||||
// 当前编辑数据
|
||||
current: null,
|
||||
// 是否显示编辑弹窗
|
||||
showEdit: false,
|
||||
// 是否显示用户导入弹窗
|
||||
showImport: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/* 搜索 */
|
||||
reload() {
|
||||
this.selection = [];
|
||||
this.$refs.table.reload({page: 1, where: this.where});
|
||||
},
|
||||
/* 重置搜索 */
|
||||
reset() {
|
||||
this.where = {};
|
||||
this.reload();
|
||||
},
|
||||
/* 删除单个 */
|
||||
remove(row) {
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.delete('/sys/user/' + row.userId).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.reload();
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
},
|
||||
/* 批量删除 */
|
||||
removeBatch() {
|
||||
if (!this.selection.length) {
|
||||
this.$message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的用户吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
this.$http.delete('/sys/user/batch', {
|
||||
data: this.selection.map(d => d.userId)
|
||||
}).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.reload();
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
/* 打开编辑弹窗 */
|
||||
openEdit(row) {
|
||||
this.current = row;
|
||||
this.showEdit = true;
|
||||
},
|
||||
/* 重置用户密码 */
|
||||
resetPsw(row) {
|
||||
this.$confirm({
|
||||
title: '提示',
|
||||
content: '确定要重置此用户的密码为"123456"吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = this.$message.loading('请求中..', 0);
|
||||
const formData = new FormData();
|
||||
formData.append('password', '123456');
|
||||
this.$http.put('/sys/user/psw/' + row.userId, formData).then(res => {
|
||||
hide();
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
hide();
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
/* 修改用户状态 */
|
||||
editState(checked, row) {
|
||||
let params = new FormData();
|
||||
params.append('state', checked ? 0 : 1);
|
||||
this.$http.put('/sys/user/state/' + row.userId, params).then(res => {
|
||||
if (res.data.code === 0) {
|
||||
row.state = checked ? 0 : 1;
|
||||
this.$message.success(res.data.msg);
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
86
src/views/system/user/info/index.vue
Normal file
86
src/views/system/user/info/index.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="ele-body">
|
||||
<a-card title="基本信息" :bordered="false">
|
||||
<a-form
|
||||
class="ele-form-detail"
|
||||
:label-col="{md: {span: 2}, sm: {span: 4}, xs: {span: 6}}"
|
||||
:wrapper-col="{md: {span: 22}, sm: {span: 20}, xs: {span: 18}}">
|
||||
<a-form-item label="账号">
|
||||
<div class="ele-text-secondary">{{ user.username }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="用户名">
|
||||
<div class="ele-text-secondary">{{ user.nickname }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="性别">
|
||||
<div class="ele-text-secondary">{{ user.sexName }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="手机号">
|
||||
<div class="ele-text-secondary">{{ user.phone }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="角色">
|
||||
<a-tag v-for="item in user.roles" :key="item.roleId" color="blue">
|
||||
{{ item.roleName }}
|
||||
</a-tag>
|
||||
</a-form-item>
|
||||
<a-form-item label="创建时间">
|
||||
<div class="ele-text-secondary">{{ $util.toDateString(user.createTime) }}</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="状态">
|
||||
<a-badge
|
||||
:status="['processing', 'error'][user.state]"
|
||||
:text="['正常', '冻结'][user.state]"/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {setPageTab} from '@/utils/page-tab-util';
|
||||
|
||||
export default {
|
||||
name: 'SystemUserInfo',
|
||||
data() {
|
||||
return {
|
||||
user: {},
|
||||
loading: true
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.query(this.$route.query.id);
|
||||
},
|
||||
methods: {
|
||||
/* 查询用户信息 */
|
||||
query(id) {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
this.$http.get('/sys/user/' + id).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
this.user = res.data.data;
|
||||
// 修改页签标题
|
||||
setPageTab({
|
||||
fullPath: this.$route.fullPath,
|
||||
title: this.user.nickname + '的信息'
|
||||
});
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch((e) => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.query(this.$route.query.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
235
src/views/system/user/user-edit.vue
Normal file
235
src/views/system/user/user-edit.vue
Normal file
@@ -0,0 +1,235 @@
|
||||
<!-- 用户编辑弹窗 -->
|
||||
<template>
|
||||
<a-modal
|
||||
:width="680"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate?'修改用户':'新建用户'"
|
||||
:body-style="{paddingBottom: '8px'}"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save">
|
||||
<a-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="{md: {span: 7}, sm: {span: 24}}"
|
||||
:wrapper-col="{md: {span: 17}, sm: {span: 24}}">
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="用户账号:" name="username">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入用户账号"
|
||||
v-model:value="form.username"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="用户名:" name="nickname">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入用户名"
|
||||
v-model:value="form.nickname"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="性别:" name="sex">
|
||||
<a-select
|
||||
allow-clear
|
||||
placeholder="请选择性别"
|
||||
v-model:value="form.sex">
|
||||
<a-select-option :value="1">男</a-select-option>
|
||||
<a-select-option :value="2">女</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="角色:" name="roleIds">
|
||||
<a-select
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
placeholder="请选择角色"
|
||||
v-model:value="form.roleIds">
|
||||
<a-select-option
|
||||
v-for="item in roleList"
|
||||
:key="item.roleId"
|
||||
:value="item.roleId">
|
||||
{{ item.roleName }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="邮箱:" name="email">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="100"
|
||||
placeholder="请输入邮箱"
|
||||
v-model:value="form.email"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="手机号:" name="phone">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="11"
|
||||
placeholder="请输入手机号"
|
||||
v-model:value="form.phone"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="出生日期:" name="birthday">
|
||||
<a-date-picker
|
||||
class="ele-fluid"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择出生日期"
|
||||
v-model:value="form.birthday"/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="!isUpdate"
|
||||
label="登录密码:"
|
||||
name="password">
|
||||
<a-input-password
|
||||
:maxlength="20"
|
||||
v-model:value="form.password"
|
||||
placeholder="请输入登录密码"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="个人简介:">
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入个人简介"
|
||||
v-model:value="form.introduction"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import validate from 'ele-admin-pro/packages/validate';
|
||||
|
||||
export default {
|
||||
name: 'UserEdit',
|
||||
emits: ['done', 'update:visible'],
|
||||
props: {
|
||||
// 弹窗是否打开
|
||||
visible: Boolean,
|
||||
// 修改回显的数据
|
||||
data: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 表单数据
|
||||
form: Object.assign({}, this.data),
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
username: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
trigger: 'blur',
|
||||
asyncValidator: (rule, value) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!value) {
|
||||
return reject('请输入用户账号');
|
||||
}
|
||||
this.$http.get('/sys/user?username=' + value).then(res => {
|
||||
if (res.data.code !== 0 || !res.data.data.length) {
|
||||
return resolve();
|
||||
}
|
||||
if (this.isUpdate && res.data.data[0].username === this.data.username) {
|
||||
return resolve();
|
||||
}
|
||||
reject('账号已经存在');
|
||||
}).catch(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
nickname: [
|
||||
{required: true, message: '请输入用户名', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
sex: [
|
||||
{required: true, message: '请选择性别', type: 'number', trigger: 'blur'}
|
||||
],
|
||||
roleIds: [
|
||||
{required: true, message: '请选择角色', type: 'array', trigger: 'blur'}
|
||||
],
|
||||
email: [
|
||||
{pattern: validate.email, message: '邮箱格式不正确', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
password: [
|
||||
{required: true, pattern: /^[\S]{5,18}$/, message: '密码必须为5-18位非空白字符', type: 'string', trigger: 'blur'}
|
||||
],
|
||||
phone: [
|
||||
{pattern: validate.phone, message: '手机号格式不正确', type: 'string', trigger: 'blur'}
|
||||
]
|
||||
},
|
||||
// 提交状态
|
||||
loading: false,
|
||||
// 是否是修改
|
||||
isUpdate: false,
|
||||
// 角色列表
|
||||
roleList: []
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
if (this.data) {
|
||||
this.form = Object.assign({}, this.data, {
|
||||
roleIds: this.data.roles.map(d => d.roleId)
|
||||
});
|
||||
this.isUpdate = true;
|
||||
} else {
|
||||
this.form = {};
|
||||
this.isUpdate = false;
|
||||
}
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.clearValidate();
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.queryRoles(); // 查询角色列表
|
||||
},
|
||||
methods: {
|
||||
/* 保存编辑 */
|
||||
save() {
|
||||
this.$refs.form.validate().then(() => {
|
||||
this.loading = true;
|
||||
this.$http[this.isUpdate ? 'put' : 'post']('/sys/user', this.form).then(res => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
if (!this.isUpdate) {
|
||||
this.form = {};
|
||||
}
|
||||
this.updateVisible(false);
|
||||
this.$emit('done');
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}).catch(() => {
|
||||
});
|
||||
},
|
||||
/* 更新visible */
|
||||
updateVisible(value) {
|
||||
this.$emit('update:visible', value);
|
||||
},
|
||||
/* 查询角色列表 */
|
||||
queryRoles() {
|
||||
this.$http.get('/sys/role').then(res => {
|
||||
if (res.data.code === 0) {
|
||||
this.roleList = res.data.data;
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
77
src/views/system/user/user-import.vue
Normal file
77
src/views/system/user/user-import.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<!-- 用户导入弹窗 -->
|
||||
<template>
|
||||
<a-modal
|
||||
:width="520"
|
||||
:footer="null"
|
||||
title="导入用户"
|
||||
:visible="visible"
|
||||
@update:visible="updateVisible">
|
||||
<a-spin :spinning="loading">
|
||||
<a-upload-dragger
|
||||
accept=".xls,.xlsx"
|
||||
:show-upload-list="false"
|
||||
:customRequest="doUpload"
|
||||
style="padding: 24px 0;margin-bottom: 16px;">
|
||||
<p class="ant-upload-drag-icon">
|
||||
<cloud-upload-outlined/>
|
||||
</p>
|
||||
<p class="ant-upload-hint">将文件拖到此处,或点击上传</p>
|
||||
</a-upload-dragger>
|
||||
</a-spin>
|
||||
<div class="ele-text-center">
|
||||
<span>只能上传xls、xlsx文件,</span>
|
||||
<a href="https://cdn.eleadmin.com/20200610/用户导入模板.xlsx"
|
||||
download="用户导入模板.xlsx">下载模板
|
||||
</a>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {CloudUploadOutlined} from '@ant-design/icons-vue';
|
||||
|
||||
export default {
|
||||
name: 'UserImport',
|
||||
components: {CloudUploadOutlined},
|
||||
emits: ['done', 'update:visible'],
|
||||
props: {
|
||||
// 是否打开弹窗
|
||||
visible: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 导入请求状态
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/* 上传 */
|
||||
doUpload({file}) {
|
||||
this.loading = true;
|
||||
let formData = new FormData();
|
||||
formData.append('file', file);
|
||||
this.$http.post('/sys/user/import', formData).then(res => {
|
||||
this.loading = false;
|
||||
if (res.data.code === 0) {
|
||||
this.$message.success(res.data.msg);
|
||||
this.updateVisible(false);
|
||||
this.$emit('done');
|
||||
} else {
|
||||
this.$message.error(res.data.msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.loading = false;
|
||||
this.$message.error(e.message);
|
||||
});
|
||||
return false;
|
||||
},
|
||||
/* 更新visible */
|
||||
updateVisible(value) {
|
||||
this.$emit('update:visible', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
Reference in New Issue
Block a user