admin 初始化
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
@@ -0,0 +1,30 @@
|
||||
import request from '../../utils/request';
|
||||
export function login(data) {
|
||||
return request({
|
||||
url:'/api/v1/login',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getCaptcha() {
|
||||
return request({
|
||||
url:'/api/v1/captcha',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getAppConfig() {
|
||||
return request({
|
||||
url:'/api/v1/app-config',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据角色获取菜单
|
||||
export function getUserMenuRole() {
|
||||
return request({
|
||||
url:'/api/v1/menurole',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import request from '../../utils/request'
|
||||
|
||||
const url = '/api/v1/menu';
|
||||
|
||||
export function getMenu(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getMenuDetails(menuId) {
|
||||
return request({
|
||||
url: `${url}/${menuId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addMenu(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeMenu(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改菜单
|
||||
* @param {Object} data
|
||||
* @param {Number} id
|
||||
* @returns
|
||||
*/
|
||||
export function updateMenu(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import request from '../../utils/request'
|
||||
|
||||
const url = '/api/v1/post';
|
||||
|
||||
export function getPost(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addPost(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removePost(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updatePost(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/role';
|
||||
export function getRole(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addRole(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeRole(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRole(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRoleScoped(data) {
|
||||
return request({
|
||||
url:'/api/v1/roledatascope',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getRoleMenuTree(params, roleId) {
|
||||
return request({
|
||||
url:`/api/v1/roleMenuTreeselect/${roleId}`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/sys-api';
|
||||
|
||||
export function getSysApi(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addSysApi(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeSysApi(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateSysApi(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/config';
|
||||
|
||||
export function getSysConfig(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addSysConfig(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeSysConfig(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateSysConfig(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/dept';
|
||||
|
||||
export function getDept(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addDept(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeDept(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateDept(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
const url = '/api/v1/dict/data';
|
||||
|
||||
export function getDictData(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addDictData(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateDictData(data, dictCode) {
|
||||
return request({
|
||||
url: `${url}/${dictCode}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteDictData(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/dict/type';
|
||||
|
||||
export function getDictType(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function addDictType(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeDictType(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateDictType(data, id) {
|
||||
return request({
|
||||
url: `${url}/${id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取字典选择框列表
|
||||
export function optionselect() {
|
||||
return request({
|
||||
url: '/api/v1/dict/type-option-select',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/sys-login-log';
|
||||
|
||||
export function getSysLoginLog(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'GET',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function removeSysLoginLog(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'DELETE',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/sys-opera-log';
|
||||
|
||||
export function getSysOperaLog(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function removeSysOperaLog(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
const url = '/api/v1/sys-user';
|
||||
|
||||
|
||||
export function getUser(params) {
|
||||
return request({
|
||||
url,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getInfo() {
|
||||
return request({
|
||||
url:'/api/v1/getinfo',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addUser(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function removeUser(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateUser(data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function updateUserStatus(data) {
|
||||
return request({
|
||||
url: '/api/v1/user/status',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function resetUserPwd(data) {
|
||||
return request({
|
||||
url:'/api/v1/user/pwd/reset',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取当前登录用户信息
|
||||
export function getCurrentUser(uid) {
|
||||
return request({
|
||||
url: `${url}/${uid}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import request from '../../utils/request';
|
||||
|
||||
// 获取用户信息
|
||||
export function getUserProfile() {
|
||||
return request({
|
||||
url: '/api/v1/user/profile',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户密码
|
||||
export function putUserPwd(data) {
|
||||
return request({
|
||||
url: '/api/v1/user/pwd/set',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询SysJob列表
|
||||
export function listSysJob(query) {
|
||||
return request({
|
||||
url: '/api/v1/sysjob',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询SysJob详细
|
||||
export function getSysJob(jobId) {
|
||||
return request({
|
||||
url: '/api/v1/sysjob/' + jobId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增SysJob
|
||||
export function addSysJob(data) {
|
||||
return request({
|
||||
url: '/api/v1/sysjob',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改SysJob
|
||||
export function updateSysJob(data) {
|
||||
return request({
|
||||
url: '/api/v1/sysjob',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除SysJob
|
||||
export function delSysJob(data) {
|
||||
return request({
|
||||
url: '/api/v1/sysjob',
|
||||
method: 'delete',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 移除SysJob
|
||||
export function removeJob(jobId) {
|
||||
return request({
|
||||
url: '/api/v1/job/remove/' + jobId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 启动SysJob
|
||||
export function startJob(jobId) {
|
||||
return request({
|
||||
url: '/api/v1/job/start/' + jobId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import request from '../../utils/request'
|
||||
|
||||
const url = '/api/v1/server-monitor';
|
||||
|
||||
export function getServerMonitor() {
|
||||
return request({
|
||||
url,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询生成表数据
|
||||
export function listTable(query) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
// 查询db数据库列表
|
||||
export function listDbTable(query) {
|
||||
return request({
|
||||
url: '/api/v1/db/tables/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询表详细信息
|
||||
export function getGenTable(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables/info/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getGenTableInfo(tablename) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables?tableName=' + tablename,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改代码生成信息
|
||||
export function updateGenTable(data) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables/info',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 导入表
|
||||
export function importTable(data) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables/info',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
// 预览生成代码
|
||||
export function previewTable(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/gen/preview/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
// 删除表数据
|
||||
export function delTable(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/sys/tables/info/' + tableId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 生成代码到项目
|
||||
export function toProjectTable(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/gen/toproject/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 生成接口数据到迁移脚本
|
||||
export function apiToFile(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/gen/apitofile/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function toProjectTableCheckRole(tableId, ischeckrole) {
|
||||
return request({
|
||||
url: '/api/v1/gen/toproject/' + tableId + '?ischeckrole=' + ischeckrole,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 生成菜单到数据库
|
||||
export function toDBTable(tableId) {
|
||||
return request({
|
||||
url: '/api/v1/gen/todb/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getTableTree() {
|
||||
return request({
|
||||
url: '/api/v1/gen/tabletree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="deleteVisible" :title="data.length > 1 ? '批量删除' : '删除'" title-align="start" @close="handleClose" width="400px">
|
||||
<a-form :model="data" :rules="rules" ref="modalFormRef" size="medium" label-align="left" auto-label-width>
|
||||
<a-alert type="warning" style="margin-bottom: 16px;" v-if="data.length > 1">您正在批量删除,请确认删除数据是否正确</a-alert>
|
||||
<a-form-item label="摘要" label-col-flex="100px">
|
||||
<a-tag color="gray" style="padding: 0 0px; margin-right: 5px">
|
||||
<template #icon>
|
||||
<a-tag color="arcoblue">数量</a-tag>
|
||||
</template>
|
||||
<a-tag style="margin-left: -4px">{{ data.length }}</a-tag>
|
||||
</a-tag>
|
||||
</a-form-item>
|
||||
<a-form-item field="deleteConfirmation" label="删除确认" label-col-flex="100px">
|
||||
<a-input v-model.trim="data.deleteConfirmation" placeholder="请输入 DELETE 以确认删除"></a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button @click="handleClose"><template #icon><icon-close /></template>取消</a-button>
|
||||
<a-button type="primary" @click="handleConfirm"><template #icon><icon-check /></template>确认</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, toRefs, watch, unref, getCurrentInstance } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const props = defineProps({
|
||||
// 数组名称
|
||||
data: {
|
||||
type: Array,
|
||||
},
|
||||
// 是否显示
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 更新接口
|
||||
apiDelete: {
|
||||
type: Function,
|
||||
},
|
||||
});
|
||||
|
||||
const { data, visible, apiDelete } = toRefs(props);
|
||||
|
||||
const emits = defineEmits(['deleteVisibleChange']);
|
||||
|
||||
// Akiraka 20230210 对话框弹出
|
||||
const deleteVisible = ref(false);
|
||||
|
||||
// Akiraka 20230210 删除数据校验
|
||||
const rules = reactive({
|
||||
deleteConfirmation: [
|
||||
{
|
||||
required: true,
|
||||
validator: (value, cb) => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
if ( "DELETE" !== value) {
|
||||
cb('请输入 DELETE')
|
||||
}
|
||||
resolve()
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
// Akiraka 20230210 关闭弹窗
|
||||
const handleClose = () => {
|
||||
emits('deleteVisibleChange', deleteVisible.value = false);
|
||||
// Akiraka 20230210 关闭弹窗
|
||||
deleteVisible.value = false;
|
||||
}
|
||||
|
||||
// Akiraka 20230210 监听事件
|
||||
watch(() => props.visible,(value) => {
|
||||
if (value) {
|
||||
// Akiraka 20230210 打开或关闭对话框
|
||||
deleteVisible.value = value;
|
||||
}
|
||||
})
|
||||
|
||||
// Akiraka 20230210 确认按钮 => 开始数据检查
|
||||
const handleConfirm = () => {
|
||||
proxy.$refs.modalFormRef.validate((valid) => {
|
||||
if (!valid) {
|
||||
// Akiraka 20230210 请求接口
|
||||
props.apiDelete({ ids: data.value }).then(response => {
|
||||
// Akiraka 20230210 关闭弹窗
|
||||
deleteVisible.value = false;
|
||||
Message.success("数据删除成功");
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
import { useUserStore } from '@/store/userInfo';
|
||||
|
||||
export default {
|
||||
checkPermission(el, binding) {
|
||||
const store = useUserStore();
|
||||
const { value } = binding;
|
||||
const all_permission = '*:*:*'
|
||||
const permissions = store.userInfo && store.userInfo.permissions;
|
||||
|
||||
if (typeof value === 'string') {
|
||||
const hasPermission = permissions.some((permission) => {
|
||||
return all_permission === permission || value === permission;
|
||||
})
|
||||
|
||||
if (!hasPermission) {
|
||||
el.parentNode && el.parentNode.removeChild(el);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`请设置操作权限标签值`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" viewBox="0 0 48 48" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M4 15a1 1 0 001 1h2a1 1 0 001-1V8h7a1 1 0 001-1V5a1 1 0 00-1-1H6a2 2 0 00-2 2v9zm4 18a1 1 0 00-1-1H5a1 1 0 00-1 1v9a2 2 0 002 2h9a1 1 0 001-1v-2a1 1 0 00-1-1H8v-7zm35-17a1 1 0 001-1V6a2 2 0 00-2-2h-9a1 1 0 00-1 1v2a1 1 0 001 1h7v7a1 1 0 001 1h2zm1 17a1 1 0 00-1-1h-2a1 1 0 00-1 1v7h-7a1 1 0 00-1 1v2a1 1 0 001 1h9a2 2 0 002-2v-9zM32.835 11h-6.108c-6.512 0-11.882 4.804-12.636 11h-1.992c-.382 0-.52.046-.66.134a.855.855 0 00-.325.378c-.074.162-.114.324-.114.77v1.436c0 .446.04.608.114.77.075.163.185.291.324.378.14.088.279.134.66.134h2.157c1.179 5.706 6.315 10 12.472 10h6.108c.405 0 .552-.041.7-.12a.819.819 0 00.344-.337c.079-.145.121-.29.121-.688V31h.901c.382 0 .52-.046.66-.134a.855.855 0 00.325-.378c.074-.162.114-.324.114-.77v-1.436c0-.446-.04-.608-.114-.77a.855.855 0 00-.325-.378c-.14-.088-.278-.134-.66-.134H34v-7h.901c.382 0 .52-.046.66-.134a.855.855 0 00.325-.378c.074-.162.114-.324.114-.77v-1.436c0-.446-.04-.608-.114-.77a.855.855 0 00-.325-.378c-.14-.088-.278-.134-.66-.134H34v-3.855c0-.398-.042-.543-.121-.688a.819.819 0 00-.344-.338c-.148-.078-.295-.119-.7-.119zm-2.744 3.571h-3.637c-5.02 0-9.09 3.998-9.09 8.929s4.07 8.929 9.09 8.929h3.637V14.57z" fill="currentColor"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div class="app-main">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="fade-transform" mode="out-in">
|
||||
<component :is="Component" :key="route" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<a-dropdown position="bl" :style="{ top: '52px' }">
|
||||
<div class="avatar-container">
|
||||
<a-avatar :size="32" :style="{ backgroundColor: '#3370ff' }">
|
||||
<img alt="avatar" :src="userInfo.avatar" />
|
||||
</a-avatar>
|
||||
<div class="user-info">
|
||||
<div class="user-info-name">{{ userInfo.name }}</div>
|
||||
<!-- <div class="user-info-desc">{{ userInfo.introduction }}</div> -->
|
||||
</div>
|
||||
</div>
|
||||
<template #content>
|
||||
<a-doption @click="$router.push('/profile')">
|
||||
<template #icon>
|
||||
<icon-settings />
|
||||
</template>
|
||||
<template #default>用户设置</template>
|
||||
</a-doption>
|
||||
<a-doption @click="handleLogout()">
|
||||
<template #icon>
|
||||
<icon-export />
|
||||
</template>
|
||||
<template #default>退出登陆</template>
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import {
|
||||
IconSettings,
|
||||
IconExport,
|
||||
} from '@arco-design/web-vue/es/icon';
|
||||
import { useUserStore } from '@/store/userInfo';
|
||||
import { clearLocalStorage } from '@/utils/storage';
|
||||
|
||||
const store = useUserStore();
|
||||
const { userInfo } = storeToRefs(store);
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const handleLogout = () => {
|
||||
proxy.$modal.warning({
|
||||
title: '提示',
|
||||
content: '确定注销并退出登陆系统吗?',
|
||||
hideCancel: false,
|
||||
onOk: () => {
|
||||
window.sessionStorage.removeItem('token');
|
||||
clearLocalStorage();
|
||||
proxy.$router.push('/login');
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.avatar-container {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
.user-info {
|
||||
margin-left: 10px;
|
||||
&-name {
|
||||
color: var(--color-text-1);
|
||||
font-weight: 700;
|
||||
padding-top: 8px;
|
||||
}
|
||||
&-desc {
|
||||
color: var(--color-text-1);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div class="sider-logo">
|
||||
<img :src="store.sysConfig.sys_app_logo" />
|
||||
<span class="sider-title" v-if="!props.collapsed">{{store.sysConfig.sys_app_name}}</span>
|
||||
</div>
|
||||
<a-menu
|
||||
class="menu"
|
||||
@menu-item-click="handleMenuClick"
|
||||
:default-open-keys="['/admin']"
|
||||
:default-selected-keys="defaultSelectKeys"
|
||||
:auto-open-selected="true"
|
||||
>
|
||||
<sub-menu :menu-list="permissionStore.menuList" />
|
||||
</a-menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onBeforeMount } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useUserStore } from '@/store/userInfo';
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
|
||||
import SubMenu from './SubMenu.vue';
|
||||
|
||||
const store = useUserStore();
|
||||
const permissionStore = usePermissionStore();
|
||||
|
||||
const props = defineProps({
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// 默认菜单选中
|
||||
const defaultSelectKeys = ref([]);
|
||||
|
||||
// 刷新保持菜单选中
|
||||
const keepDefaultSelect = () => {
|
||||
defaultSelectKeys.value = [];
|
||||
defaultSelectKeys.value.push(route.fullPath);
|
||||
};
|
||||
|
||||
const handleMenuClick = (key) => {
|
||||
router.push(key);
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
keepDefaultSelect();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sider-logo {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--color-text-4);
|
||||
& img {
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.sider-title {
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
|
||||
.left-side {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
font-size: 18px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease-in-out;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.arco-menu-indent {
|
||||
width: 30px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<template v-for="menu in props.menuList" :key="menu.menuId">
|
||||
<a-sub-menu
|
||||
v-if="menu.children && menu.menuType == 'M' && menu.visible == 0"
|
||||
:key="menu.path"
|
||||
>
|
||||
<template #icon>
|
||||
<component :is="menu.icon" />
|
||||
</template>
|
||||
<template #title>{{ menu.title }}</template>
|
||||
<sub-menu :menuList="menu.children" />
|
||||
</a-sub-menu>
|
||||
<a-menu-item
|
||||
v-if="menu.menuType == 'C' && menu.visible && isRouteParams(menu.path)"
|
||||
:key="menu.path"
|
||||
>{{ menu.title }}</a-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
menuList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
// 判断是否路由需要带参匹配
|
||||
const isRouteParams = computed(() => {
|
||||
return (path) => {
|
||||
if (path.indexOf(':') == -1) return true;
|
||||
return false;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="navbar">
|
||||
<div class="left-side" @click="onCollapse">
|
||||
<icon-menu-fold v-if="!props.collapsed" />
|
||||
<icon-menu-unfold v-else />
|
||||
</div>
|
||||
<ul class="right-side">
|
||||
<li>
|
||||
<a-button shape="circle" @click="handleDarkTheme">
|
||||
<template #icon>
|
||||
<component
|
||||
:is="!darkTheme ? IconSunFill : IconMoonFill"
|
||||
></component>
|
||||
</template>
|
||||
</a-button>
|
||||
</li>
|
||||
<li>
|
||||
<Avatar />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import Avatar from './Avatar/index.vue';
|
||||
import {
|
||||
IconMenuFold,
|
||||
IconMenuUnfold,
|
||||
IconSunFill,
|
||||
IconMoonFill,
|
||||
} from '@arco-design/web-vue/es/icon';
|
||||
|
||||
const darkTheme = ref(false);
|
||||
|
||||
const props = defineProps({
|
||||
collapsed: Boolean,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onCollapse']);
|
||||
|
||||
const onCollapse = () => {
|
||||
emit('onCollapse');
|
||||
};
|
||||
|
||||
// 切换亮色和暗色
|
||||
const handleDarkTheme = () => {
|
||||
const theme = document.body.getAttribute('arco-theme');
|
||||
if (!theme) {
|
||||
document.body.setAttribute('arco-theme', 'dark');
|
||||
darkTheme.value = true;
|
||||
} else {
|
||||
document.body.removeAttribute('arco-theme');
|
||||
darkTheme.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 50px;
|
||||
border-bottom: 1px solid #e5e6eb;
|
||||
box-shadow: 0 2px 5px 0 rgba(0,0,0, .1);
|
||||
|
||||
.left-side {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
font-size: 18px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease-in-out;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
}
|
||||
|
||||
.right-side {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
padding-right: 30px;
|
||||
|
||||
& > li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as AppMain } from './AppMain.vue';
|
||||
export { default as Navbar } from './Navbar.vue';
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<a-layout :style="{ height: '100vh' }">
|
||||
<a-layout-sider theme="dark" breakpoint="lg" :width="220" collapsible :collapsed="collapsed" @collapse="onCollapse">
|
||||
<Menu :collapsed="collapsed" />
|
||||
</a-layout-sider>
|
||||
<a-layout>
|
||||
<a-layout-header>
|
||||
<Navbar :collapsed="collapsed" @on-collapse="onCollapse" />
|
||||
</a-layout-header>
|
||||
<a-layout-content class="layout-content">
|
||||
<AppMain />
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { AppMain, Navbar } from './components';
|
||||
import Menu from './components/Menu/Menu.vue';
|
||||
|
||||
const collapsed = ref(false);
|
||||
const onCollapse = () => {
|
||||
collapsed.value = !collapsed.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../style/index.scss';
|
||||
@import '../style/transition.scss';
|
||||
@import '../style/dark-theme.scss';
|
||||
|
||||
.layout-content {
|
||||
padding: 16px;
|
||||
background-color: #f2f3f5;
|
||||
}
|
||||
</style>
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import App from './App.vue';
|
||||
import ArcoVue from '@arco-design/web-vue';
|
||||
import { Message, Modal, Notification } from '@arco-design/web-vue';
|
||||
import '@arco-design/web-vue/dist/arco.css';
|
||||
import router from './router/';
|
||||
import { parseTime } from '@/utils/parseTime';
|
||||
|
||||
// Directive
|
||||
import permission from '@/directive/permission/permission';
|
||||
|
||||
// 引入 Arco 图标库
|
||||
import * as ArcoIconModules from '@arco-design/web-vue/es/icon';
|
||||
|
||||
console.log(import.meta.env);
|
||||
|
||||
// Initialize the Pinia instance
|
||||
const pinia = createPinia();
|
||||
const app = createApp(App);
|
||||
|
||||
app.directive('has', permission.checkPermission);
|
||||
|
||||
// 挂载全局变量
|
||||
app.config.globalProperties.message = Message;
|
||||
app.config.globalProperties.modal = Modal;
|
||||
app.config.globalProperties.notification = Notification;
|
||||
app.config.globalProperties.parseTime = parseTime;
|
||||
|
||||
// 挂载全局图标
|
||||
for(const name in ArcoIconModules){
|
||||
app.component(name,ArcoIconModules[name])
|
||||
}
|
||||
|
||||
app.use(ArcoVue);
|
||||
app.use(router);
|
||||
app.use(pinia);
|
||||
app.mount('#app');
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import { createWebHashHistory, createRouter, createWebHistory } from 'vue-router';
|
||||
import Layout from '../layout/index.vue';
|
||||
import { useUserStore } from '../store/userInfo';
|
||||
import { usePermissionStore } from '../store/permission';
|
||||
import Watermark from '@/utils/watermark.js';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: '/',
|
||||
redirect: 'admin',
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: '/profile',
|
||||
name: 'profile',
|
||||
component: () => import('../views/profile/index.vue'),
|
||||
meta: {
|
||||
title: '个人设置',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/403',
|
||||
name: '403',
|
||||
component: () => import('../views/error-page/403.vue'),
|
||||
meta: {
|
||||
title: '找不到页面',
|
||||
},
|
||||
},
|
||||
{
|
||||
hide: true,
|
||||
path: '/:catchAll(.*)',
|
||||
component: () => import('../views/error-page/404.vue'),
|
||||
meta: {
|
||||
title: '找不到页面',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/500',
|
||||
name: '500',
|
||||
component: () => import('../views/error-page/500.vue'),
|
||||
meta: {
|
||||
title: '找不到页面',
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: () => import('../views/login/index.vue'),
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
// createWebHashHistory URL 带井号
|
||||
// createWebHistory URL 去井号
|
||||
history: createWebHistory(),
|
||||
routes: routes,
|
||||
});
|
||||
|
||||
// beforeEach router
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const store = useUserStore();
|
||||
|
||||
const permissionStore = usePermissionStore();
|
||||
|
||||
// 获取系统配置信息
|
||||
await store.getSysConfig();
|
||||
|
||||
// 判断用户Token是否获取
|
||||
if (to.name !== 'login' && !store.token) {
|
||||
next({ name: 'login' });
|
||||
} else {
|
||||
// 判断判断权限有无获取
|
||||
if (store.token && store.roles.length === 0) {
|
||||
store.getUserInfo();
|
||||
await permissionStore.getMenuRole();
|
||||
|
||||
permissionStore.addRouters.forEach((route) => {
|
||||
router.addRoute('/', route);
|
||||
});
|
||||
// next(to.fullPath);
|
||||
// 如果 addRoute 并未完成,路由守卫会一层一层的执行执行,直到 addRoute 完成,找到对应的路由
|
||||
next({ ...to, replace: true })
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// afterEach Router
|
||||
router.afterEach((to) => {
|
||||
const store = useUserStore();
|
||||
|
||||
// 修改网页标题
|
||||
if (to.name !== 'login') {
|
||||
document.title = `${to.meta.title} - ${store.sysConfig.sys_app_name}`;
|
||||
} else {
|
||||
document.title = store.sysConfig.sys_app_name;
|
||||
}
|
||||
|
||||
// Vincent 2023004 修复加载水印的bug
|
||||
if (store.userInfo != undefined){
|
||||
if ( store.userInfo.name != undefined ) {
|
||||
Watermark.set(store.userInfo.name)
|
||||
} else {
|
||||
Watermark.out() // 清除水印
|
||||
}
|
||||
} else{
|
||||
Watermark.out() // 清除水印
|
||||
}
|
||||
});
|
||||
export default router;
|
||||
@@ -0,0 +1,60 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { getUserMenuRole } from '@/api/admin/login';
|
||||
|
||||
|
||||
const modules = import.meta.glob('../views/**/*.vue');
|
||||
|
||||
export const usePermissionStore = defineStore('permisson', {
|
||||
state: () => {
|
||||
return {
|
||||
addRouters: [],
|
||||
menuList: [],
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
getRoutes: (state) => state.addRouters,
|
||||
},
|
||||
actions: {
|
||||
setMenuList(menus) {
|
||||
this.menuList = menus;
|
||||
},
|
||||
GenerateRoutes(routeList) {
|
||||
const routes = [];
|
||||
|
||||
routeList.forEach((item) => {
|
||||
const route = {};
|
||||
// if (item.visible == 0) {
|
||||
if (item.menuType === 'M' || item.menuType === 'C') {
|
||||
route.path = item.path;
|
||||
route.name = item.menuName;
|
||||
if (item.menuType === 'M') {
|
||||
route.component = modules[`../views/index.vue`];
|
||||
} else if (item.menuType === 'C') {
|
||||
route.component = modules[`../views${item.component}.vue`] || modules['../views/error-page/888.vue'];
|
||||
}
|
||||
route.meta = {
|
||||
title: item.title,
|
||||
permission: item.permission,
|
||||
};
|
||||
}
|
||||
|
||||
if (item.children) {
|
||||
route.children = this.GenerateRoutes(item.children);
|
||||
}
|
||||
routes.push(route);
|
||||
// }
|
||||
});
|
||||
|
||||
return routes;
|
||||
},
|
||||
async getMenuRole() {
|
||||
const res = await getUserMenuRole();
|
||||
console.log(res.data);
|
||||
this.setMenuList(res.data);
|
||||
this.addRouters = await this.GenerateRoutes(res.data);
|
||||
},
|
||||
ClearMenuList() {
|
||||
this.menuList = [];
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { setLocalStorage, getLocalStorage } from '@/utils/storage';
|
||||
import { getInfo } from '@/api/admin/sys-user';
|
||||
import { getAppConfig } from '@/api/admin/login';
|
||||
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: () => {
|
||||
return {
|
||||
token: window.sessionStorage.getItem('token') || '',
|
||||
uid: window.sessionStorage.getItem('uid') || '',
|
||||
sysConfig: getLocalStorage('sysConfig'),
|
||||
userInfo: '',
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
roles: (state) => state.userInfo.roles || [],
|
||||
},
|
||||
actions: {
|
||||
setToken(token) {
|
||||
this.token = token;
|
||||
|
||||
window.sessionStorage.setItem('token', token);
|
||||
},
|
||||
async getUserInfo() {
|
||||
try {
|
||||
const res = await getInfo();
|
||||
// window.sessionStorage.setItem('uid', res.data.userId);
|
||||
window.localStorage.setItem('uid', res.data.userId);
|
||||
this.userInfo = res.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
},
|
||||
async getSysConfig() {
|
||||
try {
|
||||
const res = await getAppConfig();
|
||||
setLocalStorage('sysConfig', res.data);
|
||||
this.sysConfig = res.data;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
},
|
||||
userLogout() {
|
||||
this.token = null;
|
||||
this.userInfo = null;
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
// Dark Theme
|
||||
|
||||
$dark-bg-1: #17171A;
|
||||
$color-bg-2: #393A3C;
|
||||
$color-bg-3: #2a2a2b;
|
||||
$color-bg-4: #313132;
|
||||
$color-bg-5: #373739;
|
||||
|
||||
$--color-text-1: rgba(255, 255, 255, 0.9);
|
||||
$--color-text-2: rgba(255, 255, 255, 0.7);
|
||||
$--color-text-3: rgba(255, 255, 255, 0.5);
|
||||
$--color-text-4: rgba(255, 255, 255, 0.3);
|
||||
|
||||
body[arco-theme='dark'] {
|
||||
.navbar {
|
||||
background-color: $dark-bg-1;
|
||||
}
|
||||
.arco-layout-content {
|
||||
background-color: $color-bg-2
|
||||
}
|
||||
.app-container {
|
||||
background-color: $color-bg-3;
|
||||
}
|
||||
.navbar {
|
||||
border-bottom: 1px solid $--color-text-4;
|
||||
.left-side {
|
||||
// background-color: $color-bg-2;
|
||||
color: $--color-text-1;
|
||||
&:hover {
|
||||
background-color: $color-bg-5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,28 @@
|
||||
// global transition css
|
||||
|
||||
/* fade */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.28s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* fade-transform */
|
||||
.fade-transform-leave-active,
|
||||
.fade-transform-enter-active {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.fade-transform-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// base color
|
||||
$primary-font-color: #1d2129;
|
||||
$secondary-font-color: #86909c;
|
||||
@@ -0,0 +1,41 @@
|
||||
// 日期格式化
|
||||
export function parseTime(time, pattern) {
|
||||
if (arguments.length === 0 || !time) {
|
||||
return null
|
||||
}
|
||||
if (time.indexOf('01-01-01') > -1) {
|
||||
return '-'
|
||||
}
|
||||
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||
let date
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
} else {
|
||||
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
|
||||
time = parseInt(time)
|
||||
}
|
||||
if ((typeof time === 'number') && (time.toString().length === 10)) {
|
||||
time = time * 1000
|
||||
}
|
||||
date = new Date(time)
|
||||
}
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay()
|
||||
}
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||
let value = formatObj[key]
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
|
||||
if (result.length > 0 && value < 10) {
|
||||
value = '0' + value
|
||||
}
|
||||
return value || 0
|
||||
})
|
||||
return time_str
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import axios from 'axios';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useUserStore } from '../store/userInfo'
|
||||
|
||||
// create an axios instance
|
||||
console.log("________"+import.meta.env.VITE_BASE_URL)
|
||||
const service = axios.create({
|
||||
baseURL:import.meta.env.VITE_BASE_URL,
|
||||
timeout: 8000,
|
||||
});
|
||||
|
||||
// request interceptor
|
||||
service.interceptors.request.use(
|
||||
(config) => {
|
||||
// Store 必须在拦截器内部导入,在外部导入会显示 Pinia 未初始化
|
||||
const store = useUserStore();
|
||||
// 设置请求头部 Authorization
|
||||
if (store.token) {
|
||||
config.headers['Authorization'] = 'Bearer ' + store.token;
|
||||
config.headers['Content-Type'] = 'application/json'
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
console.error(error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// response interceptor
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
return response.data;
|
||||
},
|
||||
(error) => {
|
||||
const store = useUserStore();
|
||||
const { code, msg } = error.response.data;
|
||||
// 如果过期则退出登录
|
||||
if (code === 401) {
|
||||
Message.error({
|
||||
content: 'Token 已过期, 请重新登陆',
|
||||
duration: 3000
|
||||
});
|
||||
// 重定向路由到登陆页面
|
||||
store.userLogout();
|
||||
// Akiraka 20230410 重定向到登录页面
|
||||
return router.push('/login');
|
||||
} else {
|
||||
Message.error({
|
||||
content: error.message,
|
||||
duration: 3000
|
||||
})
|
||||
return Promise.reject(msg);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default service;
|
||||
@@ -0,0 +1,41 @@
|
||||
// LocalStorage
|
||||
|
||||
/**
|
||||
* 存储LocalStorage
|
||||
* @param {string, Object} name
|
||||
* @param {*} value
|
||||
*/
|
||||
export const setLocalStorage = (name, value) => {
|
||||
if (!name) throw new Error('name must be specified');
|
||||
if (typeof value !== 'string') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
window.localStorage.setItem(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取LocalStorage
|
||||
* @param {string, Object} name
|
||||
* @returns
|
||||
*/
|
||||
export const getLocalStorage = (name) => {
|
||||
if(!name) throw new Error('name must be specified');
|
||||
const value = window.localStorage.getItem(name);
|
||||
return JSON.parse(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定name
|
||||
* @param {*} name
|
||||
*/
|
||||
export const removeLocalStorage = (name) => {
|
||||
if (!name) throw new Error('name must be specified');
|
||||
window.localStorage.removeItem(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有LocalStorage
|
||||
*/
|
||||
export const clearLocalStorage = () => {
|
||||
window.localStorage.clear();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
let watermark = {}
|
||||
|
||||
let setWatermark = (str) => {
|
||||
let id = '1.23452384164.123412415'
|
||||
|
||||
if (document.getElementById(id) !== null) {
|
||||
document.body.removeChild(document.getElementById(id))
|
||||
}
|
||||
|
||||
let can = document.createElement('canvas')
|
||||
can.width = 200
|
||||
can.height = 120
|
||||
|
||||
let cans = can.getContext('2d')
|
||||
cans.rotate(-15 * Math.PI / 150)
|
||||
cans.font = '18px Vedana'
|
||||
cans.fillStyle = 'rgba(200, 200, 200, 0.20)'
|
||||
cans.textAlign = 'left'
|
||||
cans.textBaseline = 'Middle'
|
||||
cans.fillText(str, can.width / 8, can.height / 2)
|
||||
|
||||
let div = document.createElement('div')
|
||||
div.id = id
|
||||
div.style.pointerEvents = 'none'
|
||||
div.style.top = '35px'
|
||||
div.style.left = '200px'
|
||||
div.style.position = 'fixed'
|
||||
div.style.zIndex = '100000'
|
||||
div.style.width = document.documentElement.clientWidth + 'px'
|
||||
div.style.height = document.documentElement.clientHeight + 'px'
|
||||
div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat'
|
||||
document.body.appendChild(div)
|
||||
return id
|
||||
}
|
||||
|
||||
// 该方法只允许调用一次
|
||||
watermark.set = (str) => {
|
||||
let id = setWatermark(str)
|
||||
setInterval(() => {
|
||||
if (document.getElementById(id) === null) {
|
||||
id = setWatermark(str)
|
||||
}
|
||||
}, 500)
|
||||
window.onresize = () => {
|
||||
setWatermark(str)
|
||||
}
|
||||
}
|
||||
|
||||
const outWatermark = (id) => {
|
||||
if (document.getElementById(id) !== null) {
|
||||
const div = document.getElementById(id)
|
||||
div.style.display = 'none'
|
||||
}
|
||||
}
|
||||
watermark.out = () => {
|
||||
const str = '1.23452384164.123412415'
|
||||
outWatermark(str)
|
||||
}
|
||||
|
||||
export default watermark
|
||||
@@ -0,0 +1,253 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" layout="inline">
|
||||
<a-form-item field="dictType" label="字典名称">
|
||||
<a-select v-model="queryForm.dictType" :options="dictTypeOptions" :field-names="{ value: 'dictType', label: 'dictName' }" />
|
||||
</a-form-item>
|
||||
<a-form-item field="dictTag" label="字典标签">
|
||||
<a-input v-model="queryForm.dictTag" placeholder="请输入字典标签" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="字典状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择字典状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">关闭</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysDictData:query'" type="primary">搜索</a-button>
|
||||
<a-button>重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="table-action">
|
||||
<a-button v-has="'admin:sysDictData:add'" type="primary" @click="handleAdd">新增</a-button>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
@page-change="handlePageChange"
|
||||
>
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysDictData:edit'" type="text" @click="handleEdit(record)">修改</a-button>
|
||||
<a-button v-has="'admin:sysDictData:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.dictCode]; }">删除</a-button>
|
||||
</template>
|
||||
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-else-if="record.status == 1" color="red">停用</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
title-align="start"
|
||||
@close="$refs.modalFormRef.resetFields()"
|
||||
@before-ok="handleBeforeOk"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef">
|
||||
<a-form-item field="dictType" label="字典类型">
|
||||
<a-input v-model="modalForm.dictType" disabled></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="dictLabel" label="数据标签">
|
||||
<a-input
|
||||
v-model="modalForm.dictLabel"
|
||||
placeholder="请输入数据标签"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="dictValue" label="数据键值">
|
||||
<a-input
|
||||
v-model="modalForm.dictValue"
|
||||
placeholder="请输入数据键值"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="dictSort" label="显示排序">
|
||||
<a-input-number
|
||||
v-model="modalForm.dictSort"
|
||||
:default-value="0"
|
||||
mode="button"
|
||||
></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio :value="2">正常</a-radio>
|
||||
<a-radio :value="1">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea v-model="modalForm.remark"></a-textarea>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="deleteDictData"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onBeforeMount, watch } from 'vue';
|
||||
import { getDictData, addDictData, updateDictData, deleteDictData } from '@/api/admin/sys-dict-data';
|
||||
import { getDictType } from '@/api/admin/sys-dict';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getDictDataInfo({...proxy.$route.params, ...pager});
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 当前页码
|
||||
const currentPage = ref(1);
|
||||
|
||||
// 分页
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// Rules
|
||||
const rules = {
|
||||
dictLabel: [{ required: true, message: '请输入数据标签' }],
|
||||
dictValue: [{ required: true, message: '请输入数据键值' }],
|
||||
dictSort: [{ required: true, message: '请选择排序' }],
|
||||
};
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
// DictType Options
|
||||
const dictTypeOptions = ref([]);
|
||||
|
||||
// 搜索表单
|
||||
const queryForm = reactive({});
|
||||
|
||||
// Modal表单
|
||||
const modalForm = reactive({
|
||||
status: 2
|
||||
});
|
||||
|
||||
// 表格
|
||||
const tableData = ref([]);
|
||||
const columns = [
|
||||
{ title: '字典编码', dataIndex: 'dictCode' },
|
||||
{ title: '字典标签', dataIndex: 'dictLabel' },
|
||||
{ title: '字典键值', dataIndex: 'dictValue' },
|
||||
{ title: '字典排序', dataIndex: 'dictSort' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '备注', dataIndex: 'remark' },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', dataIndex: 'action', slotName: 'action' },
|
||||
];
|
||||
|
||||
// 新增字典数据
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增字典数据';
|
||||
|
||||
Object.assign(modalForm, proxy.$route.params);
|
||||
};
|
||||
|
||||
// 修改字典数据
|
||||
const handleEdit = (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改字典数据';
|
||||
|
||||
Object.assign(modalForm, proxy.$route.params, record);
|
||||
console.log(modalForm)
|
||||
};
|
||||
|
||||
// 提交表单前检查
|
||||
const handleBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate((err) => {
|
||||
if (err) {
|
||||
proxy.$message.error('表单校验失败');
|
||||
done(false);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (modalForm.dictCode) {
|
||||
const { code, msg } = await updateDictData(modalForm, modalForm.dictCode);
|
||||
if ( code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await addDictData(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
getDictDataInfo({...proxy.$route.params, ...pager});
|
||||
}
|
||||
|
||||
// 页码改变
|
||||
const handlePageChange = (pagerIndex) => {
|
||||
currentPage.value = pagerIndex;
|
||||
pager.pageIndex = pagerIndex;
|
||||
getDictDataInfo({...proxy.$route.params, ...pager});
|
||||
}
|
||||
|
||||
// 获取字典数据
|
||||
const getDictDataInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getDictData(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取字典类型
|
||||
const getDictTypeInfo = async () => {
|
||||
const res = await getDictType({ pageSize: 1000 });
|
||||
|
||||
dictTypeOptions.value = res.data.list;
|
||||
Object.assign(queryForm, proxy.$route.params);
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
getDictDataInfo(proxy.$route.params);
|
||||
getDictTypeInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="dictName" label="字典名称">
|
||||
<a-input v-model="queryForm.dictName" placeholder="请输入字典名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="dictType" label="字典类型">
|
||||
<a-input v-model="queryForm.dictType" placeholder="请输入字典类型" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择字典状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysDictType:query'" type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button v-has="'admin:sysDictType:query'" @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysDictType:add'" type="primary" @click="handleAdd"><icon-plus /> 新增</a-button>
|
||||
<a-button v-has="'system:sysdicttype:remove'" type="primary" status="danger" @click="() => { deleteVisible = true; }"><icon-delete /> 批量删除</a-button>
|
||||
<a-button type="primary" status="warning" disabled><icon-download /> 导出</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- table -->
|
||||
<a-table
|
||||
:columns="tableColumns"
|
||||
:data="tableData"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
row-key="id"
|
||||
@selection-change="(selection) => {deleteData = selection;}"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #dictType="{ record }">
|
||||
<router-link :to="`/admin/dict/data/${record.dictType}`">{{ record.dictType }}</router-link>
|
||||
</template>
|
||||
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-else color="red">停用</a-tag>
|
||||
</template>
|
||||
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysDictType:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysDictType:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.id]; }"><icon-edit /> 删除</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- Modal弹框 -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
title-align="start"
|
||||
:title="modalTitle"
|
||||
@before-ok="handleSubmit"
|
||||
@close="$refs.modalFormRef.resetFields()"
|
||||
>
|
||||
<a-form :model="modalForm" ref="modalFormRef">
|
||||
<a-form-item field="dictName" label="字典名称">
|
||||
<a-input
|
||||
v-model="modalForm.dictName"
|
||||
placeholder="请输入字典名称"
|
||||
:disabled="modalForm.id ? true : false"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="dictType" label="字典类型">
|
||||
<a-input
|
||||
v-model="modalForm.dictType"
|
||||
placeholder="请输入字典类型"
|
||||
:disabled="modalForm.id ? true : false"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio :value="2">正常</a-radio>
|
||||
<a-radio :value="1">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea
|
||||
v-model="modalForm.remark"
|
||||
placeholder="请输入内容备注"
|
||||
></a-textarea>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeDictType"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, nextTick, onMounted, watch } from 'vue';
|
||||
import { getDictType, addDictType, removeDictType, updateDictType } from '@/api/admin/sys-dict';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysDictTypeInfo({ ...pager, ...queryForm });
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
|
||||
// pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// form
|
||||
const modalForm = reactive({
|
||||
status: 2,
|
||||
});
|
||||
|
||||
// Table
|
||||
const tableData = ref([]);
|
||||
const tableColumns = [
|
||||
{ title: '编号', dataIndex: 'id' },
|
||||
{ title: '字典名称', dataIndex: 'dictName' },
|
||||
{ title: '字典类型', dataIndex: 'dictType', slotName: 'dictType' },
|
||||
{ title: '字典状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '备注', dataIndex: 'remark' },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', slotName: 'action' },
|
||||
];
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
const { queryForm, handleQuery, handleResetQuery } = useQueryDict();
|
||||
// 搜索
|
||||
function useQueryDict() {
|
||||
const queryForm = reactive({});
|
||||
|
||||
const handleQuery = () => {
|
||||
getSysDictTypeInfo(queryForm);
|
||||
};
|
||||
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
currentPage.value = 1;
|
||||
getSysDictTypeInfo(queryForm);
|
||||
};
|
||||
|
||||
return {
|
||||
queryForm,
|
||||
handleQuery,
|
||||
handleResetQuery,
|
||||
};
|
||||
}
|
||||
|
||||
// 页码改变
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
currentPage.value = page;
|
||||
|
||||
getSysDictTypeInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量改变
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
|
||||
getSysDictTypeInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增字典';
|
||||
};
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改字典';
|
||||
|
||||
await nextTick();
|
||||
Object.assign(modalForm, record);
|
||||
};
|
||||
|
||||
// Submit
|
||||
const handleSubmit = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (err) => {
|
||||
if (!err) {
|
||||
try {
|
||||
if (Reflect.has(modalForm, 'id')) {
|
||||
const { code, msg } = await updateDictType(modalForm, modalForm.id);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('更新成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await addDictType(modalForm);
|
||||
currentPage.value = Math.ceil(++pager.count / pager.pageSize);
|
||||
pager.pageIndex = currentPage.value;
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
modalVisible.value = false;
|
||||
getSysDictTypeInfo(pager);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(false);
|
||||
}
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败!');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 获取字典数据
|
||||
const getSysDictTypeInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getDictType(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getSysDictTypeInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="title" label="标题">
|
||||
<a-input v-model="queryForm.title" placeholder="请输入标题" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="path" label="地址">
|
||||
<a-input v-model="queryForm.path" placeholder="请输入地址" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="action" label="类型">
|
||||
<a-select v-model="queryForm.action" placeholder="请选择类型">
|
||||
<a-option>GET</a-option>
|
||||
<a-option>POST</a-option>
|
||||
<a-option>PUT</a-option>
|
||||
<a-option>DELETE</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space size="medium">
|
||||
<a-button v-has="'admin:sysApi:query'" type="primary" @click="handleQuery">
|
||||
<template #icon>
|
||||
<icon-search />
|
||||
</template>
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button @click="handlResetQuery">
|
||||
<template #icon>
|
||||
<icon-loop />
|
||||
</template>
|
||||
重置
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<!-- Table -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
'show-total': true,
|
||||
'show-jumper': true,
|
||||
'show-page-size': true,
|
||||
total: pager.count,
|
||||
current: currentPage,
|
||||
}"
|
||||
row-key="id"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #reqType="{ record }">
|
||||
<a-tag v-if="record.action.toLowerCase() === 'get'" color="cyan">{{record.action}}</a-tag>
|
||||
<a-tag v-else-if="record.action.toLowerCase() === 'post'" color="gold" >{{ record.action }}</a-tag>
|
||||
<a-tag v-else-if="record.action.toLowerCase() === 'put'" color="green" >{{ record.action }}</a-tag>
|
||||
<a-tag v-else-if="record.action.toLowerCase() === 'delete'" color="pinkpurple" >{{ record.action }}</a-tag>
|
||||
</template>
|
||||
<template #createdAt="{ record }"> {{ parseTime(record.createdAt) }}</template>
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysApi:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改 </a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- Drawer -->
|
||||
<a-drawer
|
||||
:visible="drawerVisible"
|
||||
:width="450"
|
||||
@before-ok="handleDrawerSubmit"
|
||||
@cancel="handleDrawerCancel"
|
||||
>
|
||||
<template #title> 修改接口管理 </template>
|
||||
<a-form :model="drawerForm" ref="drawerFormRef" :rules="rules">
|
||||
<a-form-item field="handle" label="Handle">
|
||||
<a-input v-model="drawerForm.handle" placeholder="请输入Handle" />
|
||||
</a-form-item>
|
||||
<a-form-item field="title" label="标题">
|
||||
<a-input v-model="drawerForm.title" placeholder="请输入标题" />
|
||||
</a-form-item>
|
||||
<a-form-item field="type" label="类型">
|
||||
<a-select
|
||||
v-model="drawerForm.type"
|
||||
placeholder="请选择类型"
|
||||
allow-clear
|
||||
>
|
||||
<a-option>SYS</a-option>
|
||||
<a-option>BUS</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="action" label="方式">
|
||||
<a-select
|
||||
v-model="drawerForm.action"
|
||||
placeholder="请选择请求方式"
|
||||
allow-clear
|
||||
>
|
||||
<a-option>GET</a-option>
|
||||
<a-option>POST</a-option>
|
||||
<a-option>PUT</a-option>
|
||||
<a-option>DELETE</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="path" label="路径">
|
||||
<a-input v-model="drawerForm.path" disabled />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onMounted, nextTick } from 'vue';
|
||||
import { IconSearch, IconLoop } from '@arco-design/web-vue/es/icon';
|
||||
import { getSysApi,addSysApi,updateSysApi } from '@/api/admin/sys-api';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 分页当前页
|
||||
const currentPage = ref(1);
|
||||
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// From 定义
|
||||
const queryForm = reactive({});
|
||||
const drawerForm = reactive({});
|
||||
|
||||
// From 校验规则
|
||||
const rules = {
|
||||
title: [{ required: true, message: '请输入标题' }],
|
||||
method: [{ required: true, message: '请选择方式' }],
|
||||
};
|
||||
|
||||
// 抽屉显示
|
||||
const drawerVisible = ref(false);
|
||||
|
||||
// Table Columns
|
||||
const columns = [
|
||||
{ title: '标题', dataIndex: 'title' },
|
||||
{ title: '请求类型', dataIndex: 'action', slotName: 'reqType' },
|
||||
{ title: '请求信息', dataIndex: 'path' },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', slotName: 'action' },
|
||||
];
|
||||
|
||||
// Table Data
|
||||
const tableData = ref([]);
|
||||
|
||||
// Drawer 确定事件
|
||||
// 异步关闭Modal需要调用 done()
|
||||
const handleDrawerSubmit = (done) => {
|
||||
proxy.$refs.drawerFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
let res;
|
||||
if (!drawerForm.id) {
|
||||
const { code, msg } = await addSysApi(drawerForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await updateSysApi(drawerForm, drawerForm.id);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
drawerVisible.value = false;
|
||||
done();
|
||||
getSysApiInfo(pager);
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Drawer 关闭事件
|
||||
const handleDrawerCancel = () => {
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
|
||||
// 查询
|
||||
const handleQuery = async () => {
|
||||
getSysApiInfo(queryForm);
|
||||
currentPage.value = 1;
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handlResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (record) => {
|
||||
drawerVisible.value = true;
|
||||
// updateSysApi(record);
|
||||
await nextTick();
|
||||
Object.assign(drawerForm, record);
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getSysApiInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getSysApiInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 获取接口信息
|
||||
const getSysApiInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getSysApi(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getSysApiInfo(pager);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.pagination {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
// Table 操作列样式
|
||||
.arco-table-th:last-child > span {
|
||||
margin-left: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,328 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="configName" label="参数名称">
|
||||
<a-input
|
||||
v-model="queryForm.configName"
|
||||
placeholder="请输入参数名称"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="configKey" label="参数键名">
|
||||
<a-input
|
||||
v-model="queryForm.configKey"
|
||||
placeholder="请输入参数键名"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="configType" label="系统内置">
|
||||
<a-select v-model="queryForm.configType" placeholder="选择系统内置">
|
||||
<a-option value="Y">是</a-option>
|
||||
<a-option value="N">否</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery()"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery()"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysConfig:add'" type="primary" @click="handleAdd"><icon-plus /> 新增</a-button>
|
||||
<a-button v-has="'admin:sysConfig:remove'" type="primary" status="danger" disabled><icon-delete /> 删除</a-button>
|
||||
<a-button type="primary" status="warning" disabled><icon-download /> 导出</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #configType="{ record }">
|
||||
<a-tag v-if="record.configType === 'Y'" color="green">是</a-tag>
|
||||
<a-tag v-else-if="record.configType === 'N'" color="red">否</a-tag>
|
||||
</template>
|
||||
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysConfig:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysConfig:edit'" type="text" @click="() => { deleteVisible = true; deleteData = [record.id]; }"><icon-delete /> 删除</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
:title="title"
|
||||
title-align="start"
|
||||
@before-ok="handleBeforeOk"
|
||||
@close="handleResetModalForm"
|
||||
>
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef">
|
||||
<a-form-item field="configName" label="参数名称">
|
||||
<a-input
|
||||
v-model="modalForm.configName"
|
||||
placeholder="请输入参数名称"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="configKey" label="参数键名">
|
||||
<a-input v-model="modalForm.configKey" placeholder="请输入参数键名" />
|
||||
</a-form-item>
|
||||
<a-form-item field="configValue" label="参数键值">
|
||||
<a-input
|
||||
v-model="modalForm.configValue"
|
||||
placeholder="请输入参数键值"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="configType" label="系统内置">
|
||||
<a-radio-group v-model="modalForm.configType">
|
||||
<a-radio value="Y">是</a-radio>
|
||||
<a-radio value="N">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="isFrontend" label="前台显示">
|
||||
<a-radio-group v-model="modalForm.isFrontend">
|
||||
<a-radio :value="1">是</a-radio>
|
||||
<a-radio :value="0">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea
|
||||
v-model="modalForm.remark"
|
||||
placeholder="请输入备注内容"
|
||||
></a-textarea>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeSysConfig"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, getCurrentInstance, watch } from 'vue';
|
||||
import { getSysConfig, addSysConfig, removeSysConfig, updateSysConfig } from '@/api/admin/sys-config';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysConfigInfo(pager);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
const { queryForm, handleQuery, handleResetQuery } = useQueryForm();
|
||||
const { tableData, columns, handlePageChange, handlePageSizeChange } =
|
||||
useTableData();
|
||||
const {
|
||||
rules,
|
||||
title,
|
||||
visible,
|
||||
modalForm,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
handleBeforeOk,
|
||||
} = useModal();
|
||||
|
||||
function useQueryForm() {
|
||||
const queryForm = reactive({});
|
||||
|
||||
const handleQuery = () => {
|
||||
getSysConfigInfo(queryForm);
|
||||
};
|
||||
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
return {
|
||||
queryForm,
|
||||
handleQuery,
|
||||
handleResetQuery,
|
||||
};
|
||||
}
|
||||
|
||||
function useTableData() {
|
||||
const tableData = ref([]);
|
||||
const columns = [
|
||||
{ title: '编码', dataIndex: 'id', width: 70 },
|
||||
{ title: '名称', dataIndex: 'configName' },
|
||||
{ title: '键名', dataIndex: 'configKey' },
|
||||
{
|
||||
title: '内置',
|
||||
dataIndex: 'configType',
|
||||
slotName: 'configType',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
width: 350,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', dataIndex: 'action', slotName: 'action' },
|
||||
];
|
||||
|
||||
// 分页改变
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
getSysConfigInfo(pager);
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getSysConfigInfo(pager);
|
||||
};
|
||||
|
||||
return {
|
||||
columns,
|
||||
tableData,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
};
|
||||
}
|
||||
|
||||
function useModal() {
|
||||
const visible = ref(false);
|
||||
const title = ref('默认标题');
|
||||
const modalForm = reactive({
|
||||
configType: 'Y',
|
||||
isFrontend: 1,
|
||||
});
|
||||
|
||||
const rules = {
|
||||
configName: [{ required: true, message: '请输入参数名称' }],
|
||||
configKey: [{ required: true, message: '请输入参数键名' }],
|
||||
configValue: [{ required: true, message: '请输入参数键值' }],
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
visible.value = true;
|
||||
title.value = '新增参数';
|
||||
};
|
||||
|
||||
const handleUpdate = async (record) => {
|
||||
visible.value = true;
|
||||
title.value = '修改参数';
|
||||
|
||||
Object.assign(modalForm, record);
|
||||
};
|
||||
|
||||
const handleBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (err) => {
|
||||
if (!err) {
|
||||
try {
|
||||
const msg = await handleSubmit(modalForm);
|
||||
proxy.$message.success(msg);
|
||||
done();
|
||||
getSysConfigInfo();
|
||||
} catch (e) {
|
||||
proxy.$message.error(e);
|
||||
done(false);
|
||||
}
|
||||
} else {
|
||||
proxy.message.error('表单校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
rules,
|
||||
title,
|
||||
visible,
|
||||
modalForm,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
handleBeforeOk,
|
||||
};
|
||||
}
|
||||
|
||||
// 获取系统配置
|
||||
const getSysConfigInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getSysConfig(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const handleSubmit = (data) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
let res;
|
||||
if (!data.id) {
|
||||
const { code, msg } = await addSysConfig(data);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await updateSysConfig(data, data.id);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const handleResetModalForm = () => {
|
||||
proxy.$refs.modalFormRef.resetFields();
|
||||
|
||||
modalForm.id = null;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSysConfigInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,266 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="deptName" label="部门名称">
|
||||
<a-input v-model="queryForm.deptName" placeholder="请输入部门名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="部门状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择部门状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysDept:query'" type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-button v-has="'admin:sysDept:add'" type="primary" @click="handleAdd()"><icon-plus /> 新增</a-button>
|
||||
</div>
|
||||
|
||||
<!-- 异步数据需要defualt-expanded-keys 传入所有行Key才能默认展开 -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="false"
|
||||
:default-expanded-keys="[1]"
|
||||
row-key="deptId"
|
||||
>
|
||||
<template #status="{ record }">
|
||||
<a-tag color="green" v-if="record.status === 2">正常</a-tag>
|
||||
<a-tag color="red" v-else> 停用 </a-tag>
|
||||
</template>
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysDept:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysDept:add'" type="text" @click="handleAdd(record)"><icon-plus /> 新增</a-button>
|
||||
<a-button v-has="'admin:sysDept:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.deptId]; }"><icon-delete /> 删除</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- Modal -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
title-align="start"
|
||||
@before-ok="handleBeforeOk"
|
||||
@close="() => $refs.modalFormRef.resetFields()"
|
||||
>
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef">
|
||||
<a-form-item field="parentId" label="上级部门">
|
||||
<a-tree-select
|
||||
v-model="modalForm.parentId"
|
||||
:data="tableData"
|
||||
:field-names="{ key: 'deptId', title: 'deptName' }"
|
||||
placeholder="请选择上级部门"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="deptName" label="部门名称">
|
||||
<a-input v-model="modalForm.deptName" placeholder="请输入部门名称" />
|
||||
</a-form-item>
|
||||
<a-form-item field="leader" label="负责人">
|
||||
<a-input v-model="modalForm.leader" placeholder="请输入负责人" />
|
||||
</a-form-item>
|
||||
<a-form-item field="phone" label="联系电话">
|
||||
<a-input v-model="modalForm.phone" placeholder="请输入联系电话" />
|
||||
</a-form-item>
|
||||
<a-form-item field="email" label="邮箱">
|
||||
<a-input v-model="modalForm.email" placeholder="请输入邮箱" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="部门状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio :value="2">正常</a-radio>
|
||||
<a-radio :value="1">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeDept"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted, getCurrentInstance, nextTick, watch } from 'vue';
|
||||
import { getDept, addDept, removeDept, updateDept } from '@/api/admin/sys-dept';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getDeptInfo(queryForm);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
// Form
|
||||
const queryForm = reactive({});
|
||||
const modalForm = reactive({
|
||||
status: 2,
|
||||
});
|
||||
|
||||
// rules
|
||||
const rules = {
|
||||
parentId: [{ required: true, message: '请选择上级部门' }],
|
||||
deptName: [{ required: true, message: '请输入部门名称' }],
|
||||
leader: [{ required: true, message: '请输入负责人' }],
|
||||
};
|
||||
|
||||
// Columns
|
||||
const columns = [
|
||||
{
|
||||
title: '部门名称',
|
||||
dataIndex: 'deptName',
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
slotName: 'createdAt',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slotName: 'action',
|
||||
},
|
||||
];
|
||||
|
||||
// Table Data
|
||||
const tableData = ref([]);
|
||||
|
||||
// 查询
|
||||
const handleQuery = async () => {
|
||||
const res = await getDept(queryForm);
|
||||
tableData.value = deepDelChildren(res.data);
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
getDeptInfo(queryForm);
|
||||
}
|
||||
|
||||
// 新增
|
||||
const handleAdd = ({ deptId, status = 2 } = {}) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增部门';
|
||||
|
||||
if (deptId) Object.assign(modalForm, {parentId: deptId, status});
|
||||
};
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改部门信息';
|
||||
|
||||
await nextTick();
|
||||
const { parentId, deptName, leader, phone, email, status, deptId } = record;
|
||||
Object.assign(modalForm, {
|
||||
parentId,
|
||||
deptName,
|
||||
leader,
|
||||
phone,
|
||||
email,
|
||||
status,
|
||||
deptId,
|
||||
});
|
||||
};
|
||||
|
||||
// 递归删除空Children
|
||||
function deepDelChildren(data) {
|
||||
const depts = data;
|
||||
let len = depts?.length;
|
||||
// let len = depts && depts.length;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (depts[i].children.length > 0) {
|
||||
deepDelChildren(depts[i].children);
|
||||
} else {
|
||||
delete depts[i].children;
|
||||
}
|
||||
}
|
||||
|
||||
return depts;
|
||||
}
|
||||
|
||||
// Modal 表单提交前检查
|
||||
const handleBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (err) => {
|
||||
if (!err) {
|
||||
let res;
|
||||
if (Reflect.has(modalForm, 'deptId')) {
|
||||
const { code, msg } = await updateDept(modalForm, modalForm.deptId);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await addDept(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
done();
|
||||
getDeptInfo();
|
||||
} else {
|
||||
proxy.$message.error('数据校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 获取部门信息
|
||||
const getDeptInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getDept(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = deepDelChildren(data);
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDeptInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="username" label="用户名">
|
||||
<a-input v-model="queryForm.username" placeholder="请输入用户名" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择系统登录日志状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">关闭</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="ipaddr" label="IP地址">
|
||||
<a-input v-model="queryForm.ipaddr" placeholder="请输入IP地址" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
status="danger"
|
||||
:disabled="selectedKeys.length === 0"
|
||||
@click="handleBatchDelete"
|
||||
><icon-delete /> 批量删除</a-button
|
||||
>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
row-key="id"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
:pagination="{
|
||||
'show-total': true,
|
||||
'show-jumper': true,
|
||||
'show-page-size': true,
|
||||
current: currentPage,
|
||||
total: pager.count,
|
||||
}"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-if="record.status == 1" color="red">失败</a-tag>
|
||||
</template>
|
||||
<template #loginTime="{ record }">
|
||||
{{ parseTime(record.loginTime) }}
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-popconfirm
|
||||
content="是否删除当前数据?"
|
||||
@ok="removeSysLoginLogInfo([record.id])"
|
||||
>
|
||||
<a-button type="text" status="danger">删除</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { getSysLoginLog, removeSysLoginLog } from '@/api/admin/sys-login-log';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 当前页
|
||||
const currentPage = ref(1);
|
||||
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// 表格多选
|
||||
const selectedKeys = ref([]);
|
||||
|
||||
const queryForm = reactive({});
|
||||
|
||||
const tableData = ref([]);
|
||||
const columns = [
|
||||
{ title: '用户名', dataIndex: 'username' },
|
||||
{ title: '类型', dataIndex: 'msg' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: 'IP地址', dataIndex: 'ipaddr' },
|
||||
{ title: '登陆时间', dataIndex: 'loginTime', slotName: 'loginTime' },
|
||||
{ title: '操作', slotName: 'action', slotName: 'action' },
|
||||
];
|
||||
|
||||
// 查询
|
||||
const handleQuery = () => {
|
||||
getSysLoginLogInfo({ ...pager, ...queryForm });
|
||||
}
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
// 页码改变
|
||||
const handlePageChange = (page) => {
|
||||
currentPage.value = page;
|
||||
|
||||
pager.pageIndex = page;
|
||||
getSysLoginLogInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getSysLoginLogInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const handleBatchDelete = () => {
|
||||
proxy.$modal.confirm({
|
||||
title: '注意',
|
||||
content: '是否删除当前所有勾选数据?',
|
||||
onOk: () => {
|
||||
removeSysLoginLogInfo(selectedKeys.value);
|
||||
},
|
||||
onCancel: () => {
|
||||
proxy.$message.info('已取消操作');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取登陆日志信息
|
||||
const getSysLoginLogInfo = async (params = {}) => {
|
||||
const res = await getSysLoginLog(params);
|
||||
const { count, list, pageIndex, pageSize } = res.data;
|
||||
|
||||
tableData.value = list;
|
||||
Object.assign(pager, { total: count, pageIndex, pageSize });
|
||||
};
|
||||
|
||||
// 删除登陆日志信息
|
||||
const removeSysLoginLogInfo = async (ids) => {
|
||||
const res = await removeSysLoginLog({ ids });
|
||||
proxy.$message.success(res.msg);
|
||||
|
||||
getSysLoginLogInfo();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getSysLoginLogInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,350 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="title" label="菜单名称">
|
||||
<a-input v-model="queryForm.title" placeholder="请输入菜单名称" @press-enter="handleQuery"/>
|
||||
</a-form-item>
|
||||
<a-form-item field="visible" label="状态">
|
||||
<a-select v-model="queryForm.visible" placeholder="请选择菜单状态">
|
||||
<a-option value="1">显示</a-option>
|
||||
<a-option value="0">隐藏</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery">搜索</a-button>
|
||||
<a-button @click="$refs.queryFormRef.resetFields()">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<!-- 动作 -->
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleAddMenu()">新增菜单</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- 菜单管理列表 -->
|
||||
<a-table :columns="columns" :data="tableData" row-key="menuId" :pagination="false">
|
||||
<template #icon="{ record }">
|
||||
<component :is="record.icon" :style="{ fontSize: '18px' }"></component>
|
||||
</template>
|
||||
<template #menutype="{ record }">
|
||||
<a-tag color="purple" v-if="record.menuType === 'M'">目录</a-tag>
|
||||
<a-tag color="orange" v-else-if="record.menuType === 'C'">菜单</a-tag>
|
||||
<a-tag color="blue" v-else-if="record.menuType === 'F'">按钮</a-tag>
|
||||
</template>
|
||||
<template #isFrame="{ record }">
|
||||
<a-tag v-if="record.isFrame == '1'" color="green">内部</a-tag>
|
||||
<a-tag v-else color="red">外部</a-tag>
|
||||
</template>
|
||||
<template #visible="{ record }">
|
||||
<a-tag v-if="record.visible == '0'" color="green">显示</a-tag>
|
||||
<a-tag v-else color="red">隐藏</a-tag>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysMenu:add'" type="text" @click="handleAddMenu(record.menuId)">新增</a-button>
|
||||
<a-button v-has="'admin:sysMenu:edit'" type="text" @click="handleUpdate(record)">修改</a-button>
|
||||
<a-button v-has="'admin:sysMenu:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.menuId]; }">删除</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- 菜单管理新增与提交弹窗 -->
|
||||
<a-modal v-model:visible="modalVisible" :title="modalTitle" title-align="start" :width="800" modal-class="menu-modal" @before-ok="handleSubmit" @close="() => {$refs.modalFormRef.resetFields(); modalForm.menuId = null;}">
|
||||
<a-form :model="modalForm" :rules="modalRules" ref="modalFormRef" auto-label-width label-align="left">
|
||||
<a-form-item field="menuType" label="菜单类型">
|
||||
<a-radio-group v-model="modalForm.menuType">
|
||||
<a-radio value="M">目录</a-radio>
|
||||
<a-radio value="C">菜单</a-radio>
|
||||
<a-radio value="F">按钮</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="parentId" label="上级菜单">
|
||||
<a-tree-select v-model="modalForm.parentId" :data="tableData" :field-names="{ key: 'menuId', icon: '_' }" :allow-search="true" :filter-tree-node="filterTreeNode" placeholder="请选择上级菜单"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="title" label="菜单标题">
|
||||
<a-input v-model="modalForm.title" placeholder="请输入菜单标题" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="icon" label="菜单图标">
|
||||
<a-select v-model="modalForm.icon" :options="parseIconName" :field-names="{ name: 'value' }" :trigger-props="{ contentClass: 'iconselect-trigger' }" allow-search placeholder="请选择菜单图标">
|
||||
<template #option="{ data }">
|
||||
<component :is="data.value" />
|
||||
</template>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="sort" label="显示排序">
|
||||
<a-input-number v-model="modalForm.sort" mode="button" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12" v-if="modalForm.menuType !== 'F'">
|
||||
<a-form-item field="menuName" label="路由名称">
|
||||
<a-input v-model="modalForm.menuName" placeholder="请输入路由名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12" v-if="modalForm.menuType !== 'F'">
|
||||
<a-form-item field="component" label="组件名称">
|
||||
<a-input v-model="modalForm.component" placeholder="请输入组件名称"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12" v-if="modalForm.menuType !== 'F'">
|
||||
<a-form-item field="path" label="路由地址">
|
||||
<a-input v-model="modalForm.path" placeholder="请输入路由地址" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12" v-if="modalForm.menuType === 'C' || modalForm.menuType === 'F'">
|
||||
<a-form-item field="permission" label="权限标识">
|
||||
<a-input v-model="modalForm.permission" placeholder="请输入权限标识"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item field="isFrame" label="是否外链" v-if="modalForm.menuType !== 'F'">
|
||||
<a-radio-group v-model="modalForm.isFrame">
|
||||
<a-radio value="0">是</a-radio>
|
||||
<a-radio value="1">否</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="visible" label="菜单状态" v-if="modalForm.menuType !== 'F'">
|
||||
<a-radio-group v-model="modalForm.visible">
|
||||
<a-radio value="0">显示</a-radio>
|
||||
<a-radio value="1">隐藏</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="apis" label="API权限" v-if="modalForm.menuType !== 'M'">
|
||||
<a-transfer v-model:model-value="modalForm.apis" :data="transferData" :title="['未授权', '已授权']" show-search />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230210 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeMenu"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230210 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, getCurrentInstance, computed, watch, watchEffect } from 'vue';
|
||||
import * as ArcoIconModules from '@arco-design/web-vue/es/icon';
|
||||
import { getMenu } from '@/api/admin/menu';
|
||||
import { getSysApi } from '@/api/admin/sys-api';
|
||||
import { addMenu, removeMenu, updateMenu, getMenuDetails } from '@/api/admin/menu';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysMenuInfo(pager);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const { queryForm, handleQuery } = useQueryData();
|
||||
|
||||
// 默认页码
|
||||
const currentPage = ref(1);
|
||||
// 表格分页
|
||||
const pager = {
|
||||
count: 0,
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// QueryModel
|
||||
function useQueryData() {
|
||||
// 表单查询
|
||||
const queryForm = reactive({});
|
||||
const handleQuery = () => {
|
||||
getSysMenuInfo(queryForm);
|
||||
};
|
||||
|
||||
return {
|
||||
queryForm,
|
||||
handleQuery,
|
||||
};
|
||||
}
|
||||
|
||||
// 默认表单
|
||||
const modalForm = reactive({
|
||||
menuType: 'M',
|
||||
sort: 0,
|
||||
isFrame: '1',
|
||||
visible: '0',
|
||||
});
|
||||
|
||||
// 表单校验
|
||||
const modalRules = {
|
||||
title: [{ required: true, message: '请输入菜单名称' }],
|
||||
path: [{ required: true, message: '请输入菜单路由地址' }],
|
||||
component: [
|
||||
{ required: true, message: '请输入菜单路由地址' },
|
||||
{ pattern: /^[\/A-Za-z.-.()*]+$/, trigger: 'blur', message: '校验规则: 只允许输入字母 a-z 或大写 A-Z 与 -'}
|
||||
],
|
||||
};
|
||||
|
||||
// 监听事件 20220715
|
||||
watchEffect(() => {
|
||||
// 当菜单类型为目录时组件地址则为 Layout
|
||||
if (modalForm.menuType == "M") {
|
||||
modalForm.component = "Layout"
|
||||
} else {
|
||||
// 当菜单类型设置为菜单时 如果为 Layout 则为空
|
||||
if (modalForm.component == "Layout") {
|
||||
modalForm.component = ""
|
||||
// 当菜单类型设置为菜单时 编辑页面显示正常内容
|
||||
} else {
|
||||
modalForm.component = modalForm.component
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// Table
|
||||
const columns = [
|
||||
{ title: '菜单名称', dataIndex: 'title',width: "220" },
|
||||
// { title: '图标', dataIndex: 'icon', slotName: 'icon' },
|
||||
{ title: '路由地址', dataIndex: 'path', width: "400" },
|
||||
{ title: '组件地址', dataIndex: 'component', width: "300" },
|
||||
{ title: '排序', dataIndex: 'sort', width: "80" },
|
||||
{ title: '类型', dataIndex: 'menuType', slotName: 'menutype', width: "100" },
|
||||
{ title: '是否外联', dataIndex: 'isFrame', slotName: 'isFrame', width: "100" },
|
||||
{ title: '显示状态', dataIndex: 'visible', slotName: 'visible', width: "100" },
|
||||
{ title: '操作', slotName: 'action' ,width: "220", fixed: "right" },
|
||||
];
|
||||
const tableData = ref([]);
|
||||
|
||||
// Transfer Data
|
||||
const transferData = ref([]);
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
// 创建新菜单
|
||||
const handleAddMenu = (parentId = null) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增菜单';
|
||||
if (parentId) modalForm.parentId = parentId;
|
||||
getSysMenuInfo();
|
||||
};
|
||||
|
||||
// TreeSearchFilter
|
||||
const filterTreeNode = (searchVal, nodeData) => {
|
||||
return nodeData.title.indexOf(searchVal) > -1;
|
||||
};
|
||||
|
||||
// 修改菜单
|
||||
const handleUpdate = async (record) => {
|
||||
const res = await getMenuDetails(record.menuId);
|
||||
Object.assign(modalForm, res.data);
|
||||
|
||||
modalTitle.value = '修改菜单';
|
||||
modalVisible.value = true;
|
||||
};
|
||||
|
||||
// handleSubmit 新增与修改按钮方法 20220713
|
||||
const handleSubmit = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
if (!modalForm.menuId) {
|
||||
console.log("dasdasd",modalForm)
|
||||
const { success } = await addMenu(modalForm);
|
||||
if (success) proxy.$message.success('新增成功');
|
||||
} else {
|
||||
const { success } = await updateMenu(modalForm, modalForm.menuId);
|
||||
if (success) proxy.$message.success('修改成功');
|
||||
}
|
||||
getSysMenuInfo(pager);
|
||||
done();
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 获取菜单信息
|
||||
const getSysMenuInfo = async (params = {}) => {
|
||||
const { code, data, msg } = await getMenu(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data;
|
||||
} else {
|
||||
proxy.$notification.error(msg)
|
||||
}
|
||||
};
|
||||
|
||||
// 获取API接口信息
|
||||
const getSysApiInfo = async () => {
|
||||
const { code, data, msg } = await getSysApi({ pageSize: 10000, type: 'BUS' });
|
||||
if ( code == 200 ) {
|
||||
transferData.value = data.list.map((item) => {
|
||||
return { value: item.id, label: item.title };
|
||||
});
|
||||
} else {
|
||||
proxy.$notification.error(msg)
|
||||
}
|
||||
};
|
||||
|
||||
// 转换Icon Object 为 list
|
||||
const parseIconName = computed(() => {
|
||||
const iconNameList = [];
|
||||
|
||||
for (let key in ArcoIconModules) {
|
||||
if (ArcoIconModules[key].name) {
|
||||
iconNameList.push({ value: ArcoIconModules[key].name });
|
||||
}
|
||||
}
|
||||
|
||||
return iconNameList;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getSysMenuInfo();
|
||||
getSysApiInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
// 覆盖默认 select trigger 样式
|
||||
.iconselect-trigger .arco-select-dropdown-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.iconselect-trigger .arco-select-option {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
// 覆盖默认穿梭框样式
|
||||
.menu-modal {
|
||||
.arco-transfer-view {
|
||||
height: 350px;
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select
|
||||
v-model="queryForm.status"
|
||||
placeholder="请选择系统操作日志状态"
|
||||
>
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">关闭</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="createdAt" label="创建时间">
|
||||
<a-range-picker
|
||||
disabled
|
||||
v-model="queryForm.createdAt"
|
||||
style="width: 254px"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery"
|
||||
><icon-search /> 搜索</a-button
|
||||
>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
status="danger"
|
||||
@click="handleBatchDelete"
|
||||
:disabled="selectedKeys.length === 0"
|
||||
><icon-delete /> 批量删除</a-button
|
||||
>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
row-key="id"
|
||||
:pagination="{
|
||||
'show-total': true,
|
||||
'show-jumper': true,
|
||||
'show-page-size': true,
|
||||
current: currentPage,
|
||||
total: pager.count,
|
||||
}"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-if="record.status == 1" color="red">关闭</a-tag>
|
||||
</template>
|
||||
<template #operTime="{ record }">
|
||||
{{ parseTime(record.operTime) }}
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-popconfirm
|
||||
content="是否删除当前数据?"
|
||||
type="warning"
|
||||
@ok="removeSysOperaLogInfo([record.id])"
|
||||
>
|
||||
<a-button type="text" status="danger">删除</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { getSysOperaLog, removeSysOperaLog } from '@/api/admin/sys-opera-log';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 当前页
|
||||
const currentPage = ref(1);
|
||||
|
||||
// 分页
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
//
|
||||
const selectedKeys = ref([]);
|
||||
|
||||
const queryForm = reactive({});
|
||||
|
||||
const tableData = ref([]);
|
||||
const columns = [
|
||||
{ title: '编号', dataIndex: 'id' },
|
||||
{ title: '请求信息', dataIndex: 'operUrl', width: 500 },
|
||||
{ title: '操作人员', dataIndex: 'operName' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '操作日期', dataIndex: 'operTime', slotName: 'operTime' },
|
||||
{ title: '操作', slotName: 'action', slotName: 'action' },
|
||||
];
|
||||
|
||||
// 查询
|
||||
const handleQuery = () => {
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
handlePageChange(1);
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const handleBatchDelete = () => {
|
||||
console.log(selectedKeys.value);
|
||||
if (selectedKeys.value.length === 0) {
|
||||
proxy.$message.error('请勾选需要删除的数据!');
|
||||
return;
|
||||
}
|
||||
|
||||
proxy.$modal.confirm({
|
||||
title: '注意',
|
||||
content: '是否批量删除选中?',
|
||||
onOk: () => {
|
||||
removeSysOperaLogInfo(selectedKeys.value);
|
||||
},
|
||||
onCancel: () => {
|
||||
proxy.$message.info('已取消操作');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 页码改变
|
||||
const handlePageChange = (page) => {
|
||||
currentPage.value = page;
|
||||
|
||||
pager.pageIndex = page;
|
||||
getSysOperaLogInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
//
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getSysOperaLogInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 获取操作日志
|
||||
const getSysOperaLogInfo = async (params = {}) => {
|
||||
const res = await getSysOperaLog(params);
|
||||
const { count, list, pageIndex, pageSize } = res.data;
|
||||
tableData.value = list;
|
||||
|
||||
Object.assign(pager, { total: count, pageIndex, pageSize });
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除操作日志
|
||||
* @params {array} ids
|
||||
*/
|
||||
const removeSysOperaLogInfo = async (ids) => {
|
||||
const res = await removeSysOperaLog({ ids });
|
||||
proxy.$message.success(res.msg);
|
||||
|
||||
getSysOperaLogInfo();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getSysOperaLogInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="postCode" label="岗位编码">
|
||||
<a-input v-model="queryForm.postCode" placeholder="请输入岗位编码" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="postName" label="岗位名称">
|
||||
<a-input v-model="queryForm.postName" placeholder="请输入岗位名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="岗位状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择岗位状态" :style="{ width: '181px' }">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysPost:query'" type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<!-- action -->
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysPost:add'" type="primary" @click="handleAdd"><icon-plus /> 新增 </a-button>
|
||||
<a-button v-has="'admin:sysPost:remove'" type="primary" status="danger" @click="() => { deleteVisible = true; }"><icon-delete /> 批量删除 </a-button>
|
||||
<a-button type="primary" status="warning" disabled><icon-download /> 导出 </a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- table -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
row-key="postId"
|
||||
@selection-change="(selection) => {deleteData = selection;}"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-else color="red">停用</a-tag>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysPost:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysPost:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.postId]; }"><icon-delete /> 删除</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- Modal -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
title-align="start"
|
||||
@before-ok="handleSubmit"
|
||||
@close="() => {$refs.modalFormRef.resetFields(); modalForm.postId = null;}"
|
||||
>
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef">
|
||||
<a-form-item field="postName" label="岗位名称">
|
||||
<a-input v-model="modalForm.postName" placeholder="请输入岗位名称" />
|
||||
</a-form-item>
|
||||
<a-form-item field="postCode" label="岗位编码">
|
||||
<a-input v-model="modalForm.postCode" placeholder="请输入岗位编码" />
|
||||
</a-form-item>
|
||||
<a-form-item field="sort" label="岗位排序">
|
||||
<a-input-number
|
||||
v-model="modalForm.sort"
|
||||
mode="button"
|
||||
:default-value="0"
|
||||
:style="{ width: '150px' }"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="岗位状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio :value="2"> 正常 </a-radio>
|
||||
<a-radio :value="1"> 停用 </a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea v-model="modalForm.remark" placeholder="请输入备注内容" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removePost"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onMounted, nextTick, watch } from 'vue';
|
||||
import { getPost, addPost, removePost, updatePost } from '@/api/admin/post';
|
||||
import { parseTime } from '@/utils/parseTime';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getPostInfo(pager);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
// form
|
||||
const queryForm = reactive({});
|
||||
const modalForm = reactive({
|
||||
sort: 0,
|
||||
status: 2,
|
||||
});
|
||||
|
||||
// Rules
|
||||
const rules = {
|
||||
postName: [{ required: true, message: '请输入岗位名称' }],
|
||||
postCode: [{ required: true, message: '请输入岗位编码' }],
|
||||
sort: [{ required: true, message: '请选择岗位排序' }],
|
||||
};
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
// Batch Del List
|
||||
let batchList = [];
|
||||
|
||||
// Table Columns
|
||||
const columns = [
|
||||
{ title: '岗位编号', dataIndex: 'postId' },
|
||||
{ title: '岗位编码', dataIndex: 'postCode' },
|
||||
{ title: '岗位名称', dataIndex: 'postName' },
|
||||
{ title: '岗位排序', dataIndex: 'sort' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', slotName: 'action' },
|
||||
];
|
||||
|
||||
// Table Data
|
||||
const tableData = ref([]);
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增岗位';
|
||||
};
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改岗位';
|
||||
|
||||
await nextTick();
|
||||
Object.assign(modalForm, record);
|
||||
};
|
||||
|
||||
// Modal ok
|
||||
// 异步关闭Modal需要调用 done()
|
||||
const handleSubmit = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
let res;
|
||||
if (!modalForm.postId) {
|
||||
const { code, msg } = await addPost(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await updatePost(modalForm, modalForm.postId);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('更新成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
done();
|
||||
getPostInfo(pager);
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const handleBatchDelete = () => {
|
||||
if (batchList.length !== 0) {
|
||||
proxy.$modal.warning({
|
||||
title: '提示',
|
||||
content: '是否批量删除以下选中的数据?',
|
||||
hideCancel: false,
|
||||
onOk: async () => {
|
||||
const res = await removePost({ ids: batchList });
|
||||
proxy.$message.success(res.msg);
|
||||
getPostInfo(pager);
|
||||
},
|
||||
onCancel: () => {
|
||||
proxy.$message.info('已取消批量删除数据');
|
||||
},
|
||||
});
|
||||
} else {
|
||||
proxy.$message.error('请勾选需要删除的数据!');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getPostInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getPostInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 获取岗位信息
|
||||
const getPostInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getPost(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 查询岗位信息
|
||||
const handleQuery = async () => {
|
||||
const params = {
|
||||
pageIndex: pager.pageIndex,
|
||||
pageSize: pager.pageSize,
|
||||
...queryForm,
|
||||
};
|
||||
|
||||
getPostInfo(params);
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
getPostInfo(queryForm);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getPostInfo(pager);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,382 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="roleName" label="角色名称">
|
||||
<a-input v-model="queryForm.roleName" placeholder="请输入角色名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="roleKey" label="权限字符">
|
||||
<a-input v-model="queryForm.roleKey" placeholder="请输入权限字符" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择角色状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysRole:add'" type="primary" @click="handleAdd"><icon-plus /> 新增</a-button>
|
||||
<a-button v-has="'admin:sysRole:remove'" type="primary" status="danger" @click="() => { deleteVisible = true; }"><icon-delete /> 批量删除</a-button>
|
||||
<a-button type="primary" status="warning" disabled><icon-download /> 导出</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
row-key="roleId"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
@selection-change="(selection) => {deleteData = selection;}"
|
||||
@select="handleSelect"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-else color="red">停用</a-tag>
|
||||
</template>
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-button v-has="'admin:sysRole:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysRole:update'" type="text" @click="handleDataScope(record)"><icon-check-circle /> 数据权限 </a-button>
|
||||
<a-button v-has="'admin:sysRole:remove'" type="text" @click="() => { deleteVisible = true; deleteData = [record.roleId]; }"><icon-check-circle /> 删除 </a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- Role Modal -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
:title="title"
|
||||
title-align="start"
|
||||
@before-ok="handleBeforeOk"
|
||||
@close="handleCancel"
|
||||
>
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef">
|
||||
<a-form-item field="roleName" label="角色名称">
|
||||
<a-input v-model="modalForm.roleName" placeholder="请输入角色名称" />
|
||||
</a-form-item>
|
||||
<a-form-item field="roleKey" label="权限字符">
|
||||
<a-input v-model="modalForm.roleKey" placeholder="请输入权限字符" />
|
||||
</a-form-item>
|
||||
<a-form-item field="roleSort" label="角色排序">
|
||||
<a-input-number
|
||||
v-model="modalForm.roleSort"
|
||||
mode="button"
|
||||
:default-value="0"
|
||||
:style="{ width: '150px' }"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio value="2">正常</a-radio>
|
||||
<a-radio value="1">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="权限设置">
|
||||
<a-tree
|
||||
v-model:checked-keys="checkedKeys"
|
||||
:checkable="true"
|
||||
:check-strictly="false"
|
||||
:data="treeData"
|
||||
:default-expand-all="false"
|
||||
:field-names="{ key: 'id', title: 'label' }"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea v-model="modalForm.remark" placeholder="请输入备注内容" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- DataScope Modal -->
|
||||
<a-modal
|
||||
v-model:visible="scopedModalVisible"
|
||||
:title="title"
|
||||
title-align="start"
|
||||
@before-ok="handleScopeBeforeOk"
|
||||
@close="handleCancel"
|
||||
>
|
||||
<a-form :model="scopeForm">
|
||||
<a-form-item field="roleName" label="角色名称">
|
||||
<a-input v-model="scopeForm.roleName" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item field="roleKey" label="权限字符">
|
||||
<a-input v-model="scopeForm.roleKey" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item field="dataScope" label="权限范围">
|
||||
<a-select v-model="scopeForm.dataScope" placeholder="请选择权限范围">
|
||||
<a-option
|
||||
v-for="item in dataScopeOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeRole"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, getCurrentInstance, nextTick, watch } from 'vue';
|
||||
import { getRole, addRole, updateRole, removeRole, updateRoleScoped, getRoleMenuTree } from '@/api/admin/role';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getRoleInfo({ ...pager, ...queryForm });
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// Batch Delete List
|
||||
const batchDeleteList = ref([]);
|
||||
|
||||
// Form
|
||||
const queryForm = reactive({});
|
||||
const modalForm = reactive({
|
||||
sort: 0,
|
||||
status: '2',
|
||||
});
|
||||
const scopeForm = reactive({});
|
||||
|
||||
// rules
|
||||
const rules = {
|
||||
roleName: [{ required: true, message: '请输入角色名称' }],
|
||||
roleKey: [{ required: true, message: '请输入权限字符' }],
|
||||
};
|
||||
|
||||
// ScopeOption
|
||||
const dataScopeOptions = [
|
||||
{
|
||||
value: '1',
|
||||
label: '全部数据权限',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: '自定数据权限',
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
label: '本部门数据权限',
|
||||
},
|
||||
{
|
||||
value: '4',
|
||||
label: '本部门及以下数据权限',
|
||||
},
|
||||
{
|
||||
value: '5',
|
||||
label: '仅本人数据权限',
|
||||
},
|
||||
];
|
||||
|
||||
// Table Columns
|
||||
const columns = [
|
||||
{ title: '编号', dataIndex: 'roleId' },
|
||||
{ title: '角色名称', dataIndex: 'roleName' },
|
||||
{ title: '权限字符', dataIndex: 'roleKey' },
|
||||
{ title: '排序', dataIndex: 'roleSort' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '创建时间', dataIndex: 'createdAt', slotName: 'createdAt' },
|
||||
{ title: '操作', slotName: 'action', width: 250 },
|
||||
];
|
||||
|
||||
// Table Data;
|
||||
const tableData = ref([]);
|
||||
|
||||
// Tree Data;
|
||||
const checkedKeys = ref([]);
|
||||
const treeData = ref([]);
|
||||
|
||||
// Modal
|
||||
const modalVisible = ref(false);
|
||||
const scopedModalVisible = ref(false);
|
||||
const title = ref('默认标题');
|
||||
|
||||
// Table Select
|
||||
const handleSelect = (rowKey) => {
|
||||
batchDeleteList.value = rowKey;
|
||||
};
|
||||
|
||||
// 查询
|
||||
const handleQuery = async () => {
|
||||
const res = await getRole({ ...pager, ...queryForm });
|
||||
const { count, list, pageIndex, pageSize } = res.data;
|
||||
|
||||
tableData.value = list;
|
||||
Object.assign(pager, { count, pageIndex, pageSize });
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
getRoleInfo(queryForm);
|
||||
};
|
||||
|
||||
// 创建
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
title.value = '创建角色';
|
||||
};
|
||||
|
||||
// 修改角色
|
||||
const handleUpdate = async (record) => {
|
||||
modalVisible.value = true;
|
||||
title.value = '修改角色';
|
||||
|
||||
await nextTick();
|
||||
Object.assign(modalForm, record);
|
||||
|
||||
// 显示勾选的菜单,checkedKeys 传入id数组即可
|
||||
const menuIdsChecked = [];
|
||||
record.sysMenu.forEach((item) => {
|
||||
menuIdsChecked.push(item.menuId);
|
||||
});
|
||||
checkedKeys.value = menuIdsChecked;
|
||||
};
|
||||
|
||||
// 分配数据权限
|
||||
const handleDataScope = async (record) => {
|
||||
scopedModalVisible.value = true;
|
||||
title.value = '分配数据权限';
|
||||
|
||||
const { roleKey, roleName, dataScope, roleId } = record;
|
||||
await nextTick();
|
||||
Object.assign(scopeForm, { roleKey, roleName, dataScope, roleId });
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getRoleInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getRoleInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 角色管理表单提交
|
||||
// 异步关闭表单需要使用 done() 回调函数
|
||||
const handleBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
// 如果 valid 为空则数据校验通过
|
||||
if (!valid) {
|
||||
modalForm.menuIds = checkedKeys.value;
|
||||
let res;
|
||||
if (modalForm.roleId) {
|
||||
const { code, msg } = await updateRole(modalForm, modalForm.roleId);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('更新成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await addRole(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
getRoleInfo();
|
||||
done();
|
||||
} else {
|
||||
proxy.$message.error('数据校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 数据权限表单提交
|
||||
const handleScopeBeforeOk = async (done) => {
|
||||
const res = await updateRoleScoped(scopeForm);
|
||||
proxy.$message.success(res.msg);
|
||||
done();
|
||||
|
||||
getRoleInfo();
|
||||
};
|
||||
|
||||
// 重置数据表单
|
||||
const handleCancel = () => {
|
||||
modalVisible.value = false;
|
||||
modalForm.roleId = null;
|
||||
scopeForm.roleId = null;
|
||||
checkedKeys.value = [];
|
||||
proxy.$refs.modalFormRef.resetFields();
|
||||
};
|
||||
|
||||
// 获取角色信息
|
||||
const getRoleInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getRole(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取角色菜单信息
|
||||
const getRoleMenuTreeInfo = async () => {
|
||||
const res = await getRoleMenuTree({}, 0);
|
||||
treeData.value = res.data.menus;
|
||||
checkedKeys.value = res.data.checkedKeys;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getRoleInfo();
|
||||
getRoleMenuTreeInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style setup>
|
||||
.action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-tabs default-active-key="1" position="left" :style="{ textAlign: 'right' }">
|
||||
<a-tab-pane key="1" title="系统内置">
|
||||
<a-form :model="form" :style="{ width: '50%' }">
|
||||
<a-form-item field="sys_app_name" label="系统名称">
|
||||
<a-input v-model="form.sys_app_name"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="sys_app_logo" label="系统Logo">
|
||||
<a-space>
|
||||
<div class="upload-logo-preview">
|
||||
<img width="150" height="150" />
|
||||
</div>
|
||||
<a-upload action="/">
|
||||
<template #upload-button>
|
||||
<div class="upload-logo-card">
|
||||
<div class="upload-logo-card-text">
|
||||
<IconPlus />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-upload>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<a-form-item field="sys_user_initPassword" label="初始密码">
|
||||
<a-input-password v-model="form.sys_user_initPassword"></a-input-password>
|
||||
</a-form-item>
|
||||
<a-form-item field="sys_index_skinName" label="皮肤样式">
|
||||
<a-select v-model="form.sys_index_skinName">
|
||||
<a-option>蓝色</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="sys_index_sideTheme" label="侧边栏主题">
|
||||
<a-select v-model="form.sys_index_sideTheme">
|
||||
<a-option>深色主题</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary">提交</a-button>
|
||||
<a-button>重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" title="其它">暂无内容</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { IconPlus } from '@arco-design/web-vue/es/icon'
|
||||
|
||||
const form = reactive({});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.upload-logo-preview {
|
||||
font-size: 0px;
|
||||
}
|
||||
|
||||
.upload-logo-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
box-sizing: border-box;
|
||||
border: 1px dashed #c0ccda;
|
||||
border-radius: 5px;
|
||||
color: #4e5969;
|
||||
&-text {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="tree-container">
|
||||
<a-tree
|
||||
:data="props.data"
|
||||
:field-names="{ key: 'deptId', title: 'deptName' }"
|
||||
block-node
|
||||
@select="handleTreeSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['nodeClick']);
|
||||
|
||||
const handleTreeSelect = (selectKeys) => {
|
||||
emits('nodeClick', { deptId: `/${selectKeys}/` });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.tree-container {
|
||||
padding-right: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,621 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- Query -->
|
||||
<a-form ref="queryFormRef" :model="queryForm" layout="inline">
|
||||
<a-form-item field="deptName" label="部门名称">
|
||||
<a-input v-model="queryForm.deptName" placeholder="请输入部门名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="username" label="用户名称">
|
||||
<a-input v-model="queryForm.username" placeholder="请输入用户名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="phone" label="手机号码">
|
||||
<a-input v-model="queryForm.phone" placeholder="请输入用户手机号" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="用户状态">
|
||||
<a-select
|
||||
v-model="queryForm.status"
|
||||
placeholder="请选择用户状态"
|
||||
:style="{ width: '205px' }"
|
||||
>
|
||||
<a-option value="2">正常</a-option>
|
||||
<a-option value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-divider direction="vertical" :style="{ height: '30px' }" />
|
||||
<a-form-item class="form-action">
|
||||
<a-space size="medium">
|
||||
<a-button v-has="'admin:sysUser:query'" type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- divider -->
|
||||
<a-divider />
|
||||
|
||||
<!-- Table -->
|
||||
<a-row>
|
||||
<a-col :span="4">
|
||||
<!-- tree组件只在组件第一次渲染时展开,此处等待数据加载完成再渲染组件 -->
|
||||
<tree-dept v-if="treeDeptData" :data="treeDeptData" @node-click="getSysUserInfo" />
|
||||
</a-col>
|
||||
<a-col :span="20">
|
||||
<!-- Action -->
|
||||
<a-space class="action">
|
||||
<a-button v-has="'admin:sysUser:add'" type="primary" @click="handleAdd" data-test="newUser"><icon-plus /> 新增</a-button>
|
||||
<a-button v-has="'admin:sysUser:remove'" type="primary" status="danger" @click="() => { deleteVisible = true; }"><icon-delete /> 批量删除</a-button>
|
||||
</a-space>
|
||||
|
||||
<!-- Table -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:bordered="false"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
:pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }"
|
||||
row-key="userId"
|
||||
@selection-change="(selection) => {deleteData = selection;}"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #dept="{ record }">
|
||||
{{ record.dept.deptName }}
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<a-switch
|
||||
v-model="record.status"
|
||||
checked-value="2"
|
||||
unchecked-value="1"
|
||||
@change="handleSwitchChange(record)"
|
||||
/>
|
||||
</template>
|
||||
<template #createdAt="{ record }">
|
||||
{{ parseTime(record.createdAt) }}
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-button v-has="'admin:sysUser:edit'" type="text" @click="handleUpdate(record)"><icon-edit /> 修改</a-button>
|
||||
<a-button v-has="'admin:sysUser:edit'" type="text" @click="() => { deleteVisible = true; deleteData = [record.userId]; }"><icon-delete /> 删除</a-button>
|
||||
<a-button v-has="'admin:sysUser:resetPassword'" type="text" @click="handleReset(record.userId)"><icon-refresh /> 重置</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- Modal -->
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
title-align="start"
|
||||
:width="600"
|
||||
@cancel="handleModalCancel('modalFormRef')"
|
||||
@before-ok="handleBeforeOk"
|
||||
>
|
||||
<template #title>
|
||||
{{ modalTitle }}
|
||||
</template>
|
||||
<a-form
|
||||
ref="modalFormRef"
|
||||
:model="modalForm"
|
||||
:rules="rules"
|
||||
auto-label-width
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="nickName" label="用户昵称">
|
||||
<a-input
|
||||
v-model="modalForm.nickName"
|
||||
placeholder="请输入用户昵称"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="deptId" label="所属部门">
|
||||
<a-tree-select
|
||||
v-model="modalForm.deptId"
|
||||
:data="treeDeptData"
|
||||
:field-names="{ key: 'deptId', title: 'deptName' }"
|
||||
placeholder="请选择所属部门"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="phone" label="手机号码">
|
||||
<a-input v-model="modalForm.phone" placeholder="请输入手机号码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="email" label="邮箱">
|
||||
<a-input v-model="modalForm.email" placeholder="请输入邮箱" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="username" label="用户名称">
|
||||
<a-input
|
||||
v-model="modalForm.username"
|
||||
placeholder="请输入用户名称"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col v-if="!modalForm.userId" :span="12">
|
||||
<a-form-item field="password" label="用户密码">
|
||||
<a-input-password
|
||||
v-model="modalForm.password"
|
||||
placeholder="请输入用户密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="sex" label="用户性别">
|
||||
<a-select v-model="modalForm.sex" placeholder="请选择用户性别">
|
||||
<a-option value="0"> 男 </a-option>
|
||||
<a-option value="1"> 女 </a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-radio-group v-model="modalForm.status">
|
||||
<a-radio value="2"> 正常 </a-radio>
|
||||
<a-radio value="1"> 停用 </a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="postId" label="岗位">
|
||||
<a-select v-model="modalForm.postId" placeholder="请选择岗位">
|
||||
<a-option
|
||||
v-for="item in postList"
|
||||
:key="item.postId"
|
||||
:value="item.postId"
|
||||
:label="item.postName"
|
||||
/>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="roleId" label="角色">
|
||||
<a-select v-model="modalForm.roleId" placeholder="请选择角色">
|
||||
<a-option
|
||||
v-for="item in roleList"
|
||||
:key="item.roleId"
|
||||
:value="item.roleId"
|
||||
:label="item.roleName"
|
||||
/>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea placeholder="请输入备注" allow-clear />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="resetPwdVisible"
|
||||
title="重置密码"
|
||||
@before-ok="handleResetPwd"
|
||||
@cancel="$refs.resetPwdFormRef.resetFields()"
|
||||
>
|
||||
<a-form
|
||||
ref="resetPwdFormRef"
|
||||
:model="resetPwdForm"
|
||||
:rules="resetPwdRules"
|
||||
auto-label-width
|
||||
>
|
||||
<a-form-item field="password" label="新密码">
|
||||
<a-input-password
|
||||
v-model="resetPwdForm.password"
|
||||
placeholder="请输入新密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item field="repeatPwd" label="确认密码">
|
||||
<a-input-password
|
||||
v-model="resetPwdForm.repeatPwd"
|
||||
placeholder="请输入确认密码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="removeUser"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onMounted, watch } from 'vue';
|
||||
import { IconSearch, IconLoop } from '@arco-design/web-vue/es/icon';
|
||||
import TreeDept from './components/TreeDept.vue';
|
||||
import { getUser, addUser, updateUser, removeUser, updateUserStatus, resetUserPwd } from '@/api/admin/sys-user';
|
||||
import { getRole } from '@/api/admin/role';
|
||||
import { getPost } from '@/api/admin/post';
|
||||
import { getDept } from '@/api/admin/sys-dept';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysUserInfo({ ...pager, ...queryForm });
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// Query
|
||||
const { queryForm, handleQuery, handleResetQuery } = useQueryData();
|
||||
// ApiInfo
|
||||
const { currentPage, getSysPostInfo, getSysRoleInfo, getSysDeptTreeInfo, getSysUserInfo } =
|
||||
useApiInfo();
|
||||
|
||||
// Pager
|
||||
const pager = reactive({
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10
|
||||
});
|
||||
|
||||
// Reset Pwd
|
||||
const {
|
||||
resetPwdForm,
|
||||
resetPwdVisible,
|
||||
resetPwdRules,
|
||||
handleReset,
|
||||
handleResetPwd,
|
||||
} = useResetPwd();
|
||||
|
||||
// Table Operate
|
||||
const {
|
||||
columns,
|
||||
tableData,
|
||||
treeDeptData,
|
||||
roleList,
|
||||
postList,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
handleSwitchChange,
|
||||
} = useTableList();
|
||||
|
||||
// ModalForm Operate
|
||||
const {
|
||||
rules,
|
||||
modalForm,
|
||||
modalVisible,
|
||||
modalTitle,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
handleBeforeOk,
|
||||
handleModalCancel,
|
||||
} = useModalOperate();
|
||||
|
||||
|
||||
function useQueryData() {
|
||||
const queryForm = reactive({});
|
||||
|
||||
// 查询
|
||||
const handleQuery = () => {
|
||||
getSysUserInfo(queryForm);
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
getSysUserInfo(queryForm);
|
||||
};
|
||||
|
||||
return { queryForm, handleQuery, handleResetQuery };
|
||||
}
|
||||
|
||||
function useResetPwd() {
|
||||
const resetPwdVisible = ref(false);
|
||||
const resetPwdForm = reactive({});
|
||||
|
||||
// Rules
|
||||
const resetPwdRules = {
|
||||
password: [{ required: true, message: '请输入密码' }],
|
||||
repeatPwd: [
|
||||
{
|
||||
required: true,
|
||||
message: '请重复输入密码',
|
||||
},
|
||||
{
|
||||
validator: (value, cb) => {
|
||||
if (value !== resetPwdForm.password) {
|
||||
cb('两次输入的密码不一致');
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 用户重置密码API参数
|
||||
const resetParams = {};
|
||||
|
||||
// 重置
|
||||
const handleReset = (userId) => {
|
||||
resetPwdVisible.value = true;
|
||||
resetParams.userId = userId;
|
||||
};
|
||||
|
||||
// 重置用户密码
|
||||
const handleResetPwd = (done) => {
|
||||
proxy.$refs.resetPwdFormRef.validate(async (err) => {
|
||||
if (!err) {
|
||||
resetParams.password = resetPwdForm.password;
|
||||
const { msg } = await resetUserPwd(resetParams);
|
||||
proxy.$message.success(msg);
|
||||
done();
|
||||
getSysUserInfo();
|
||||
} else {
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
resetPwdForm,
|
||||
resetPwdVisible,
|
||||
resetPwdRules,
|
||||
handleReset,
|
||||
handleResetPwd,
|
||||
};
|
||||
}
|
||||
|
||||
function useApiInfo() {
|
||||
const currentPage = ref(1);
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param {*} [params]
|
||||
*/
|
||||
const getSysUserInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await getUser(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取角色信息
|
||||
const getSysRoleInfo = async () => {
|
||||
const res = await getRole();
|
||||
roleList.value = res.data.list;
|
||||
};
|
||||
|
||||
// 获取岗位信息
|
||||
const getSysPostInfo = async () => {
|
||||
const res = await getPost();
|
||||
postList.value = res.data.list;
|
||||
};
|
||||
|
||||
// 获取部门树信息
|
||||
const getSysDeptTreeInfo = async () => {
|
||||
const res = await getDept();
|
||||
treeDeptData.value = res.data;
|
||||
};
|
||||
|
||||
return {
|
||||
currentPage,
|
||||
getSysPostInfo,
|
||||
getSysRoleInfo,
|
||||
getSysDeptTreeInfo,
|
||||
getSysUserInfo,
|
||||
};
|
||||
}
|
||||
|
||||
function useTableList() {
|
||||
// 部门数据
|
||||
const treeDeptData = ref();
|
||||
const roleList = ref([]);
|
||||
const postList = ref([]);
|
||||
|
||||
// Table columns
|
||||
const columns = [
|
||||
{
|
||||
title: '编号',
|
||||
dataIndex: 'userId',
|
||||
},
|
||||
{
|
||||
title: '登录名',
|
||||
dataIndex: 'username',
|
||||
},
|
||||
{
|
||||
title: '昵称',
|
||||
dataIndex: 'nickName',
|
||||
},
|
||||
{
|
||||
title: '部门',
|
||||
dataIndex: 'deptName',
|
||||
slotName: 'dept',
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
dataIndex: 'phone',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
slotName: 'createdAt',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slotName: 'action',
|
||||
},
|
||||
];
|
||||
|
||||
// Table Data
|
||||
const tableData = ref([]);
|
||||
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getSysUserInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getSysUserInfo({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 用户状态快速切换
|
||||
const handleSwitchChange = (record) => {
|
||||
proxy.$modal.warning({
|
||||
title: '注意',
|
||||
content: `是否${record.status == 1 ? '停用' : '启用'} ${
|
||||
record.username
|
||||
} 用户?`,
|
||||
hideCancel: false,
|
||||
onOk: async () => {
|
||||
const params = { userId: record.userId, status: record.status };
|
||||
const res = await updateUserStatus(params);
|
||||
proxy.$message.success(res.msg);
|
||||
},
|
||||
onCancel: () => {
|
||||
record.status = record.status == '2' ? '1' : '2';
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
treeDeptData,
|
||||
roleList,
|
||||
postList,
|
||||
columns,
|
||||
tableData,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
handleSwitchChange,
|
||||
};
|
||||
}
|
||||
|
||||
function useModalOperate() {
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
// Form
|
||||
const modalForm = reactive({ status: '2' });
|
||||
|
||||
// AddRules
|
||||
const rules = {
|
||||
nickName: [{ required: true, message: '请输入用户昵称' }],
|
||||
deptId: [{ required: true, message: '请选择所属部门' }],
|
||||
phone: [{ required: true, message: '请输入手机号' }],
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱' },
|
||||
{ type: 'email', message: '请输入正确的邮箱格式' },
|
||||
],
|
||||
username: [{ required: true, message: '请输入用户名称' }],
|
||||
password: [{ required: true, message: '请输入用户密码' }],
|
||||
};
|
||||
|
||||
// Modal 取消后重置表单
|
||||
const handleModalCancel = (formEl) => {
|
||||
modalVisible.value = false;
|
||||
resetForm(formEl);
|
||||
modalForm.userId = null;
|
||||
};
|
||||
|
||||
// 新增用户
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增用户';
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
* @param {Object} val
|
||||
*/
|
||||
const handleUpdate = (val) => {
|
||||
modalVisible.value = true;
|
||||
Object.assign(modalForm, val);
|
||||
};
|
||||
|
||||
// 重置Form
|
||||
const resetForm = (formEl) => {
|
||||
if (!formEl) return;
|
||||
proxy.$refs[formEl].resetFields();
|
||||
};
|
||||
|
||||
// 表单提交
|
||||
const handleBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
if (!modalForm.userId) {
|
||||
const { code, msg } = await addUser(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await updateUser(modalForm, modalForm.userId);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('更新成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
done();
|
||||
proxy.$refs.modalFormRef.resetFields();
|
||||
getSysUserInfo();
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
rules,
|
||||
modalForm,
|
||||
modalVisible,
|
||||
modalTitle,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
handleBeforeOk,
|
||||
handleModalCancel,
|
||||
};
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSysUserInfo();
|
||||
getSysDeptTreeInfo();
|
||||
getSysRoleInfo();
|
||||
getSysPostInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@media screen and (max-width: 1720px) {
|
||||
.form-action {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,410 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="dataInfo" :rules="rules" ref="modalFormRef" auto-label-width size="mini">
|
||||
<a-card bordered>
|
||||
<!-- 卡片插槽 开始 -->
|
||||
<template #actions>
|
||||
<a-button type="primary" @click="handleSubmit" long>提交</a-button>
|
||||
<a-button type="primary" @click="close" long>返回</a-button>
|
||||
</template>
|
||||
<!-- 卡片插槽 结束 -->
|
||||
<a-tabs default-active-key="2">
|
||||
<a-tab-pane key="1" title="基本信息">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="tableName">
|
||||
<template #label>
|
||||
数据表名称
|
||||
<a-tooltip content="数据库表名称,针对gorm对应的table()使用,⚠️这里必须是蛇形结构">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.tableName" placeholder="请输入表名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="tableComment">
|
||||
<template #label>
|
||||
菜单名称
|
||||
<a-tooltip content="同步的数据库表名称,生成配置数据时,用作菜单名称">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.tableComment" placeholder="请输入菜单名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="className">
|
||||
<template #label>
|
||||
结构体模型名称
|
||||
<a-tooltip content="结构体模型名称,代码中的struct名称定义使用">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.className" placeholder="请输入" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="functionAuthor" label="作者名称">
|
||||
<a-input v-model="dataInfo.functionAuthor" placeholder="请输入作者名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea v-model="dataInfo.remark" placeholder="请输入备注" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-tab-pane>
|
||||
|
||||
<a-tab-pane key="2" title="字段信息">
|
||||
<a-alert type="warning" style="margin-bottom: 16px;">表字段中的id、create_by、update_by、created_at、updated_at、deleted_at的字段在此列表中已经隐藏.</a-alert>
|
||||
<a-table :data="tableData" :virtual-list-props="{height:600}" :pagination="false" :scroll="{x: '100%'}">
|
||||
<template #columns>
|
||||
<!-- <a-table-column title="序号" data-index="tableId" :width="180" ellipsis tooltip/> -->
|
||||
<a-table-column title="字段列名" data-index="columnName"/>
|
||||
<a-table-column title="字段描述" :width="120">
|
||||
<template #cell="{ record }">
|
||||
<a-input v-model="record.columnComment" />
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="物理类型" data-index="columnType" align="center" :width="120" />
|
||||
<a-table-column title="go类型" align="center" :width="140">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.goType" placeholder="请选择 ...">
|
||||
<a-option value="int64">int64</a-option>
|
||||
<a-option value="string">string</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="go属性" align="center" >
|
||||
<template #cell="{ record }">
|
||||
<a-input v-model="record.goField"/>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="json属性" align="center" >
|
||||
<template #cell="{ record }">
|
||||
<a-input v-model="record.jsonField"/>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="编辑" align="center" :width="60">
|
||||
<template #cell="{ record }">
|
||||
<a-switch v-model="record.isInsert" type="large" checked-value="1" unchecked-value="0">
|
||||
<template #checked-icon>
|
||||
<icon-check/>
|
||||
</template>
|
||||
<template #unchecked-icon>
|
||||
<icon-close/>
|
||||
</template>
|
||||
</a-switch>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="列表" align="center" :width="60">
|
||||
<template #cell="{ record }">
|
||||
<a-switch v-model="record.isList" type="large" checked-value="1" unchecked-value="0">
|
||||
<template #checked-icon>
|
||||
<icon-check/>
|
||||
</template>
|
||||
<template #unchecked-icon>
|
||||
<icon-close/>
|
||||
</template>
|
||||
</a-switch>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="查询" align="center" :width="60">
|
||||
<template #cell="{ record }">
|
||||
<a-switch v-model="record.isQuery" type="large" checked-value="1" unchecked-value="0">
|
||||
<template #checked-icon>
|
||||
<icon-check/>
|
||||
</template>
|
||||
<template #unchecked-icon>
|
||||
<icon-close/>
|
||||
</template>
|
||||
</a-switch>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="查询方式" :width="90">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.queryType" placeholder="请选择 ...">
|
||||
<a-option label="=" value="EQ" />
|
||||
<a-option label="!=" value="NE" />
|
||||
<a-option label=">" value="GT" />
|
||||
<a-option label=">=" value="GTE" />
|
||||
<a-option label="<" value="LT" />
|
||||
<a-option label="<=" value="LTE" />
|
||||
<a-option label="LIKE" value="LIKE" />
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="必填" :width="70">
|
||||
<template #cell="{ record }">
|
||||
<!-- <a-checkbox v-model="record.isRequired"/> -->
|
||||
<a-switch v-model="record.isRequired" type="large" checked-value="1" unchecked-value="0">
|
||||
<template #checked-icon>
|
||||
<icon-check/>
|
||||
</template>
|
||||
<template #unchecked-icon>
|
||||
<icon-close/>
|
||||
</template>
|
||||
</a-switch>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="显示类型" :width="140">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.htmlType" placeholder="请选择 ...">
|
||||
<a-option value="input">文本框</a-option>
|
||||
<a-option value="select">下拉框</a-option>
|
||||
<a-option value="radio">单选框</a-option>
|
||||
<a-option value="textarea">文本域</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="字典类型">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.dictType" allow-clear allow-search placeholder="请选择">
|
||||
<a-option v-for="dict in dictOptions" :key="dict.dictType" :label="dict.dictName" :value="dict.dictType" >
|
||||
<a-descriptions :column="1" size="mini">
|
||||
<a-descriptions-item :label="dict.dictName">
|
||||
{{ dict.dictType }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="关系表">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.fkTableName" allow-clear allow-search placeholder="请选择" @change="handleChangeConfig(record)">
|
||||
<a-option v-for="table in tableTree" :key="table.tableName" :label="table.tableName" :value="table.tableName">
|
||||
<span style="float: left; margin-right: 5px; font-size: 12px;">{{ table.tableName }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ table.tableComment }}</span>
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="关系表key">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.fkLabelId" allow-clear allow-search placeholder="请选择">
|
||||
<a-option v-for="column in record.fkCol" :key="column.columnName" :label="column.columnName" :value="column.jsonField">
|
||||
<span style="float: left; margin-right: 5px; font-size: 12px;">{{ column.jsonField }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 12px;">{{ column.columnComment }}</span>
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="关系表value">
|
||||
<template #cell="{ record }">
|
||||
<a-select v-model="record.fkLabelName" allow-clear allow-search placeholder="请选择">
|
||||
<a-option v-for="column in record.fkCol" :key="column.columnName" :label="column.columnName" :value="column.jsonField">
|
||||
<span style="float: left; margin-right: 5px; font-size: 12px;">{{ column.jsonField }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ column.columnComment }}</span>
|
||||
</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
|
||||
<a-tab-pane key="3" title="生成信息">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="tplCategory" label="生成模板">
|
||||
<a-select v-model="dataInfo.tplCategory">
|
||||
<a-option label="关系表(增删改查)" value="crud" />
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="packageName">
|
||||
<template #label>
|
||||
应用名
|
||||
<a-tooltip content="应用名,例如:在app文件夹下将该功能发到那个应用中,默认:admin">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.packageName" placeholder="请输入应用名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="businessName">
|
||||
<template #label>
|
||||
业务名
|
||||
<a-tooltip content="可理解为功能英文名,例如 user">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.businessName" placeholder="请输入业务名" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="functionName" label="功能描述">
|
||||
<template #label>
|
||||
功能描述
|
||||
<a-tooltip content="同步的数据库表备注,用作类描述,例如:用户">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.functionName" placeholder="请输入功能描述" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="moduleName">
|
||||
<template #label>
|
||||
接口路径
|
||||
<a-tooltip content="接口路径,例如:api/v1/{sys-user}">
|
||||
<icon-question-circle-fill />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model="dataInfo.moduleName" placeholder="请输入接口路径" >
|
||||
<template #prepend>api/{version}/</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onMounted, unref } from 'vue';
|
||||
import { optionselect as getDictOptionselect } from '@/api/admin/sys-dict'
|
||||
import { getGenTable, updateGenTable, getTableTree } from '@/api/tools/gen';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// Rules
|
||||
const rules = {
|
||||
tableName: [
|
||||
{ required: true, message: '请输入表名称' },
|
||||
{ match: /^[a-z\._]*$/g, message: '只允许小写字母,例如 sys_demo 格式'}
|
||||
],
|
||||
tableComment: [
|
||||
{ required: true, message: '请输入菜单名称' },
|
||||
],
|
||||
className: [
|
||||
{ required: true, message: '请输入模型名称' },
|
||||
{ match: /^[A-Z][A-z0-9]*$/g, message: '只允许小写字母,例如 sys_demo 格式'}
|
||||
],
|
||||
functionAuthor: [
|
||||
{ required: true, message: '请输入作者' },
|
||||
{ match: /^[A-Za-z]+$/, message: '必须以大写字母开头,例如 SysDemo 格式'}
|
||||
],
|
||||
tplCategory: [
|
||||
{ required: true, message: '请选择生成模板' },
|
||||
],
|
||||
packageName: [
|
||||
{ required: true, message: '请输入生成包路径' },
|
||||
],
|
||||
moduleName: [
|
||||
{ required: true, message: '请输入生成模块名' },
|
||||
{ match: /^[a-z\-]*[a-z]$/g, message: '只允许小写字母,例如 sys-demo 格式'}
|
||||
],
|
||||
businessName: [
|
||||
{ required: true, message: '请输入生成业务名' },
|
||||
{ match: /^[a-z][A-Za-z]+$/, message: '校验规则: 只允许输入字母 a-z 或大写 A-Z ,并且小写字母开头'}
|
||||
],
|
||||
functionName: [
|
||||
{ required: true, message: '请输入生成功能名' },
|
||||
],
|
||||
}
|
||||
|
||||
// 字符串转布尔类型
|
||||
const stringToBool = (value) => {
|
||||
if (value == "1") {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 表详细信息
|
||||
const dataInfo = ref({})
|
||||
// 默认表格内容
|
||||
const tableData = ref([]);
|
||||
// 获取 服务数据
|
||||
const getGenTableQuery = async (params = {}) => {
|
||||
const res = await getGenTable(params);
|
||||
tableData.value = res.data.list;
|
||||
// 编辑 isInsert
|
||||
// 列表 isList
|
||||
// 查询 isQuery
|
||||
// 必填 isRequired
|
||||
dataInfo.value = res.data.info;
|
||||
};
|
||||
|
||||
|
||||
// 接收数据库表树
|
||||
const tableTree = ref([]);
|
||||
// 获取数据库表树
|
||||
const getTableTreeQuery = async (params = {}) => {
|
||||
getTableTree().then(response => {
|
||||
tableTree.value = response.data
|
||||
tableTree.value.unshift({ tableId: 0, className: '请选择' })
|
||||
})
|
||||
};
|
||||
// 关系表
|
||||
const handleChangeConfig = (row) => {
|
||||
tableTree.value.filter(function(item) {
|
||||
if (item.tableName === row.fkTableName) {
|
||||
row.fkCol = item.columns
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 字典类型列表
|
||||
const dictOptions = ref([]);
|
||||
// 查询字典下拉列表
|
||||
const getDictOption = async () => {
|
||||
const res =await getDictOptionselect();
|
||||
dictOptions.value = res.data
|
||||
};
|
||||
|
||||
|
||||
const modalFormRef = ref(null);
|
||||
// handleSubmit 修改按钮方法 20221101
|
||||
const handleSubmit = () => {
|
||||
const genTable = Object.assign({}, dataInfo.value)
|
||||
genTable.columns = tableData.value
|
||||
genTable.params = {
|
||||
treeCode: genTable.treeCode,
|
||||
treeName: genTable.treeName,
|
||||
treeParentCode: genTable.treeParentCode
|
||||
}
|
||||
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
const { code, msg } = await updateGenTable(genTable);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
// 成功调用关闭方法
|
||||
close()
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 关闭按钮 */
|
||||
const close = () => {
|
||||
proxy.$router.push({ path: '/dev-tools/gen', query: { t: Date.now() }})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const { tableId } = proxy.$route.query
|
||||
getGenTableQuery(tableId);
|
||||
getTableTreeQuery()
|
||||
getDictOption()
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="visible" title="导入表" @ok="handleImportTable" :ok-loading="loading" @cancel="visible = false" width="50%">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="tableName" label="表名称">
|
||||
<a-input v-model="queryForm.tableName" placeholder="请输入表名称" />
|
||||
</a-form-item>
|
||||
<a-form-item field="tableComment" label="表描述">
|
||||
<a-input v-model="queryForm.tableComment" placeholder="请输入表描述" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-divider />
|
||||
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
'show-total': true,
|
||||
'show-jumper': true,
|
||||
'show-page-size': true,
|
||||
total: pager.count,
|
||||
current: currentPage,
|
||||
}"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
row-key="tableName"
|
||||
@selection-change="selectionChange"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #columns>
|
||||
<a-table-column title="表名称" data-index="tableName" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="表描述" data-index="tableComment" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="创建时间" :width="180" ellipsis>
|
||||
<template #cell="{ record }">
|
||||
{{ parseTime(record.createTime) }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="更新时间" :width="180" ellipsis>
|
||||
<template #cell="{ record }">
|
||||
{{ parseTime(record.updateTime) }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted, getCurrentInstance } from 'vue';
|
||||
import { listDbTable,importTable,listTable} from '@/api/tools/gen';
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 对话框默认值
|
||||
const visible = ref(false);
|
||||
|
||||
// 按钮加载中状态
|
||||
const loading = ref(false)
|
||||
|
||||
// 对话框确认
|
||||
const handleImportTable = () => {
|
||||
loading.value = true
|
||||
importTable({ tables: batchList.join(',') }).then(res => {
|
||||
if (res.code === 200) {
|
||||
proxy.$message.info(res.msg);
|
||||
// 关闭对话框
|
||||
visible.value = false;
|
||||
// 完成刷新下页面
|
||||
location.reload();
|
||||
} else {
|
||||
proxy.$message.info(res.msg);
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
|
||||
const queryForm = reactive({});
|
||||
// 查询列表信息
|
||||
const handleQuery = async () => {
|
||||
const params = {
|
||||
pageIndex: pager.pageIndex,
|
||||
pageSize: pager.pageSize,
|
||||
...queryForm,
|
||||
};
|
||||
|
||||
getDbTables(params);
|
||||
};
|
||||
// 重置搜索
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
getDbTables(queryForm);
|
||||
}
|
||||
|
||||
|
||||
const tableData = ref([]);
|
||||
// Batch Del List
|
||||
let batchList = [];
|
||||
// Table 勾选数据
|
||||
const selectionChange = (rowKeys) => {
|
||||
batchList = rowKeys;
|
||||
};
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getDbTables({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getDbTables({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 初始页数
|
||||
const currentPage = ref(1);
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
// 获取列表数据
|
||||
const getDbTables = async (params = {}) => {
|
||||
const { data, code, msg } = await listDbTable(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDbTables(pager);
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,270 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="tableName" label="表名称">
|
||||
<a-input v-model="queryForm.tableName" placeholder="请输入表名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="tableComment" label="表描述">
|
||||
<a-input v-model="queryForm.tableComment" placeholder="请输入描述" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery"><icon-search /> 搜索</a-button>
|
||||
<a-button @click="handleResetQuery"><icon-loop /> 重置</a-button>
|
||||
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<!-- action -->
|
||||
<div class="action">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="openImportTable"><icon-plus /> 导入 </a-button>
|
||||
<a-button type="primary" status="danger" @click="() => { deleteVisible = true; }"><icon-delete /> 批量删除</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<!-- table -->
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||
row-key="tableId"
|
||||
@selection-change="(selection) => {deleteData = selection;}"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #columns>
|
||||
<a-table-column title="序号" data-index="tableId" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="表名称" data-index="tableName" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="表描述" data-index="tableComment" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="模型名称" data-index="className" :width="180" ellipsis tooltip/>
|
||||
<a-table-column title="动作" :width="370" align="center" >
|
||||
<template #cell="{ record }">
|
||||
<a-button-group>
|
||||
<a-button size="small" @click="handleEditTable(record)">编辑</a-button>
|
||||
<a-button size="small" @click="handlePreview(record)">预览</a-button>
|
||||
<a-popconfirm content="正在使用代码生成请确认?" okText="生成" type="warning" @ok="handleToProject(record)">
|
||||
<a-button size="small">代码生成 </a-button>
|
||||
</a-popconfirm>
|
||||
<a-popconfirm content="正在使用【菜单以及API生成到数据库】请确认?" okText="写入DB" type="warning" @ok="handleToDB(record)">
|
||||
<a-button size="small">生成配置 </a-button>
|
||||
</a-popconfirm>
|
||||
<a-popconfirm content="正在使用代码生成配置迁移脚本请确认?" okText="迁移" type="warning" @ok="handleToApiFile(record)">
|
||||
<a-button size="small">生成迁移脚本 </a-button>
|
||||
</a-popconfirm>
|
||||
<a-button size="small" @click="() => { deleteVisible = true; deleteData = [record.jobId]; }">删除</a-button>
|
||||
</a-button-group>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<import-table v-model:visible="modalVisible" />
|
||||
|
||||
<!-- 预览代码对话框 -->
|
||||
<a-modal v-model:visible="codeVisible" title="预览代码" width="80%">
|
||||
<a-space style="margin-bottom: 16px;">
|
||||
<a-tag v-for="(value, key) of preview.data" :key="key" bordered color="#165dff" @click="codeChange(key)">{{ key.substring(key.lastIndexOf('/')+1,key.indexOf('.go.template')) }}</a-tag>
|
||||
</a-space>
|
||||
<!-- 代码编辑器 -->
|
||||
<codemirror v-model="codestr" :style="{ height: '600px' }" :autofocus="true" :indent-with-tab="true" :tab-size="2" :extensions="extensions" />
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="delTable"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, getCurrentInstance, onMounted, nextTick, watch } from 'vue';
|
||||
import importTable from './importTable.vue'
|
||||
import { listTable,previewTable,delTable,apiToFile,toProjectTableCheckRole,toDBTable } from '@/api/tools/gen';
|
||||
import { parseTime } from '@/utils/parseTime';
|
||||
import { Codemirror } from 'vue-codemirror'
|
||||
import { javascript } from '@codemirror/lang-javascript'
|
||||
import { oneDark } from '@codemirror/theme-one-dark'
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysConfigInfo(pager);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
// form
|
||||
const queryForm = reactive({});
|
||||
const modalForm = reactive({
|
||||
sort: 0,
|
||||
status: 2,
|
||||
});
|
||||
|
||||
const modalVisible = ref(false);
|
||||
const openImportTable = () => {
|
||||
modalVisible.value = true;
|
||||
};
|
||||
// Batch Del List
|
||||
let batchList = [];
|
||||
|
||||
// Table Data
|
||||
const tableData = ref([]);
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改岗位';
|
||||
await nextTick();
|
||||
Object.assign(modalForm, record);
|
||||
};
|
||||
|
||||
// Table 勾选数据
|
||||
const selectionChange = (rowKeys) => {
|
||||
batchList = rowKeys;
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页改变
|
||||
* @param {Number} [page]
|
||||
*/
|
||||
const handlePageChange = (page) => {
|
||||
pager.pageIndex = page;
|
||||
// 修改当前页码
|
||||
currentPage.value = page;
|
||||
getListTable({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 每页数据量
|
||||
const handlePageSizeChange = (pageSize) => {
|
||||
pager.pageSize = pageSize;
|
||||
getListTable({ ...pager, ...queryForm });
|
||||
};
|
||||
|
||||
// 获取列表数据
|
||||
const getListTable = async (params = {}) => {
|
||||
const { data, code, msg } = await listTable(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 查询列表信息
|
||||
const handleQuery = async () => {
|
||||
const params = {
|
||||
pageIndex: pager.pageIndex,
|
||||
pageSize: pager.pageSize,
|
||||
...queryForm,
|
||||
};
|
||||
|
||||
getListTable(params);
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
getListTable(queryForm);
|
||||
}
|
||||
|
||||
// 按钮块
|
||||
// 编辑
|
||||
const handleEditTable = (row) => {
|
||||
const tableId = row.tableId || this.ids[0]
|
||||
proxy.$router.push({ path: '/dev-tools/editTable', query: { tableId: tableId }})
|
||||
}
|
||||
// 预览代码参数
|
||||
// 预览代码对话框
|
||||
const codeVisible = ref(false);
|
||||
// 预览参数
|
||||
const preview = ref({
|
||||
open: false,
|
||||
title: '代码预览',
|
||||
data: {},
|
||||
activeName: 'api.go'
|
||||
})
|
||||
/** 预览按钮 */
|
||||
const handlePreview = (row) => {
|
||||
codeVisible.value = true;
|
||||
previewTable(row.tableId).then(response => {
|
||||
console.log("sadasdasdasd",response)
|
||||
preview.value.data = response.data
|
||||
preview.value.open = true
|
||||
codeChange('template/api.go.template')
|
||||
})
|
||||
}
|
||||
// 代码编辑器参数
|
||||
const cmOptions = ref({
|
||||
tabSize: 4,
|
||||
theme: 'material-palenight',
|
||||
mode: 'text/javascript',
|
||||
lineNumbers: true,
|
||||
line: true
|
||||
})
|
||||
const extensions = [javascript(), oneDark];
|
||||
const codestr = ref('')
|
||||
// 预览代码 代码更改
|
||||
const codeChange = (e) => {
|
||||
if (e.indexOf('js') > -1) {
|
||||
|
||||
cmOptions.value.mode = 'text/javascript'
|
||||
}
|
||||
if (e.indexOf('model') > -1 || e.indexOf('router') > -1 || e.indexOf('api') > -1 || e.indexOf('service') > -1 || e.indexOf('dto') > -1) {
|
||||
cmOptions.value.mode = 'text/x-go'
|
||||
}
|
||||
if (e.indexOf('vue') > -1) {
|
||||
cmOptions.value.mode = 'text/x-vue'
|
||||
}
|
||||
codestr.value = preview.value.data[e]
|
||||
}
|
||||
// 代码生成
|
||||
const handleToProject = (row) => {
|
||||
console.log("dasdasd",row)
|
||||
toProjectTableCheckRole(row.tableId, false).then((response) => {
|
||||
proxy.$message.success(response.msg);
|
||||
}).catch(function() {})
|
||||
}
|
||||
// 生成配置
|
||||
const handleToDB = (row) => {
|
||||
toDBTable(row.tableId).then((response) => {
|
||||
proxy.$message.success(response.msg);
|
||||
}).catch(function() {})
|
||||
}
|
||||
// 生成迁移脚本
|
||||
const handleToApiFile = (row) => {
|
||||
apiToFile(row.tableId, true).then((response) => {
|
||||
proxy.$message.success(response.msg);
|
||||
}).catch(function() {})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getListTable(pager);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<iframe class="iframe-swagger" src="http://124.222.72.164:8000/swagger/index.html"></iframe>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.iframe-swagger {
|
||||
width: 100%;
|
||||
height: calc(100vh - 90px);
|
||||
border: none
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-result
|
||||
status="403"
|
||||
subtitle="对不起,您没有访问该资源的权限"
|
||||
>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary">
|
||||
返回
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: calc(100vh - 122px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-result
|
||||
status="404"
|
||||
subtitle="抱歉,页面走丢了~"
|
||||
>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary">
|
||||
返回
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: calc(100vh - 122px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-result
|
||||
status="500"
|
||||
subtitle="抱歉,服务器出了点问题~"
|
||||
>
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="primary">
|
||||
返回
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: calc(100vh - 122px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-result
|
||||
subtitle="找不到组件地址,请检查菜单: 菜单管理 > 对应菜单 > 组件管理"
|
||||
>
|
||||
<template #extra>
|
||||
</template>
|
||||
</a-result>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: calc(100vh - 122px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
@@ -0,0 +1,290 @@
|
||||
<template>
|
||||
<div class="account">
|
||||
<div class="account-container">
|
||||
<div class="account-wrap-login">
|
||||
<div class="login-pic">
|
||||
<div>
|
||||
<img
|
||||
src="/public/login_left_bg.jpg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-form" style="padding: 3rem !important">
|
||||
<div class="login-form-container">
|
||||
<div class="account-top">
|
||||
<div class="account-top-logo">
|
||||
<img :src="store.sysConfig.sys_app_logo" />
|
||||
<span class="project-title">用户登录</span>
|
||||
</div>
|
||||
<div class="account-top-desc">横看成峰侧成岭 远近高低各不同</div>
|
||||
</div>
|
||||
<!-- 登录表单 -->
|
||||
<a-form
|
||||
:model="loginForm"
|
||||
:rules="loginRules"
|
||||
ref="loginFormRef"
|
||||
layout="vertical"
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
<a-form-item field="userName" hide-asterisk>
|
||||
<a-input
|
||||
v-model="loginForm.userName"
|
||||
placeholder="请输入用户名"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-user />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="passWord" hide-asterisk>
|
||||
<a-input-password
|
||||
v-model="loginForm.passWord"
|
||||
placeholder="请输入密码"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-lock />
|
||||
</template>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<div style="display: flex">
|
||||
<div style="width: 65%">
|
||||
<a-form-item field="code" hide-asterisk>
|
||||
<a-input
|
||||
v-model="loginForm.code"
|
||||
placeholder="请输入验证码"
|
||||
>
|
||||
<template #prefix>
|
||||
<icon-safe />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div style="width: 5%"></div>
|
||||
<div style="width: 20%">
|
||||
<a-form-item field="code" hide-asterisk>
|
||||
<img
|
||||
:src="captchUrl"
|
||||
class="captcha"
|
||||
@click="loadCaptcha()"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<a-space direction="vertical" size="medium">
|
||||
<a-checkbox>记住密码</a-checkbox>
|
||||
<a-button
|
||||
size="large"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
long
|
||||
@click="handleLogin"
|
||||
>登录</a-button
|
||||
>
|
||||
</a-space>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { IconUser, IconLock, IconSafe } from '@arco-design/web-vue/es/icon';
|
||||
import { login, getCaptcha } from '@/api/admin/login';
|
||||
import { useUserStore } from '@/store/userInfo';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const store = useUserStore();
|
||||
// form
|
||||
const loginForm = reactive({});
|
||||
// 验证码
|
||||
const captchUrl = ref(null);
|
||||
// 按钮loading
|
||||
const loading = ref(false);
|
||||
|
||||
// rules
|
||||
const loginRules = {
|
||||
userName: [{ required: true, message: '请输入用户名' }],
|
||||
passWord: [{ required: true, message: '请输入密码' }],
|
||||
code: [{ required: true, message: '请输入验证码' }],
|
||||
};
|
||||
|
||||
// 获取验证码
|
||||
const loadCaptcha = async () => {
|
||||
const res = await getCaptcha();
|
||||
captchUrl.value = res.data;
|
||||
loginForm.uuid = res.id;
|
||||
};
|
||||
|
||||
// 登陆
|
||||
const handleLogin = () => {
|
||||
loading.value = true;
|
||||
// 如果 valid 为空则校验成功
|
||||
proxy.$refs.loginFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
try {
|
||||
const { code, token, msg } = await login(loginForm);
|
||||
if ( code == 200 ) {
|
||||
await store.setToken(token);
|
||||
proxy.$message.success({
|
||||
content: '登陆成功',
|
||||
duration: 2000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
proxy.$router.push('/admin/sys-api');
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
} else {
|
||||
proxy.$message.error(`登陆失败:${msg}`);
|
||||
}
|
||||
} catch (err) {
|
||||
// 登录失败 重新获取验证码
|
||||
loadCaptcha();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await loadCaptcha();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.captcha {
|
||||
width: 100px;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
*,
|
||||
:before,
|
||||
:after {
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: #e5e7eb;
|
||||
}
|
||||
|
||||
// 输入框重写
|
||||
.arco-input-wrapper {
|
||||
border-radius: 20px;
|
||||
height: 40px;
|
||||
border: 1px solid #ddd;
|
||||
background: #fff;
|
||||
}
|
||||
// 输入框 重写框颜色
|
||||
.arco-input-wrapper:hover {
|
||||
background: #fff;
|
||||
border: 1px solid #1e6fff;
|
||||
}
|
||||
|
||||
.account {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.account-container {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
background: #9053c7;
|
||||
background: linear-gradient(-135deg, #c850c0, #4158d0);
|
||||
}
|
||||
.account-wrap-login {
|
||||
width: 960px;
|
||||
height: 554px;
|
||||
// background: #fff;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
// justify-content:space-between;
|
||||
// padding:30px 95px 33px
|
||||
}
|
||||
.account-wrap-login .login-pic {
|
||||
background-color: #0259e6 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 50%;
|
||||
}
|
||||
.account-wrap-login .login-pic img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.account-wrap-login .login-form {
|
||||
width: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
}
|
||||
.account-wrap-login .login-form-container {
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
}
|
||||
.account-wrap-login .login-form-title {
|
||||
padding-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
@media (max-width: 991px) {
|
||||
.account-wrap-login .login-pic {
|
||||
display: none;
|
||||
}
|
||||
.account-wrap-login .login-form {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
.account-wrap-login .account-top {
|
||||
text-align: center;
|
||||
}
|
||||
.account-wrap-login .account-top-logo {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.account-wrap-login .account-top-logo img {
|
||||
width: 45px;
|
||||
}
|
||||
.account-wrap-login .account-top-logo .project-title {
|
||||
background: linear-gradient(
|
||||
92.06deg,
|
||||
#33c2ff -17.9%,
|
||||
#1e6fff 43.39%,
|
||||
#1e6fff 99.4%
|
||||
);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-size: 24px;
|
||||
line-height: 1.25;
|
||||
font-weight: 500;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.account-wrap-login .account-top-desc {
|
||||
font-size: 14px;
|
||||
color: #808695;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.account-wrap-login {
|
||||
width: 100%;
|
||||
padding: 30px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// 20230105 自动填充背景色问题
|
||||
:deep(.arco-input-wrapper .arco-input.arco-input-size-medium) {
|
||||
box-shadow: 0 0 0px 1000px #fff inset;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" viewBox="0 0 48 48" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M4 15a1 1 0 001 1h2a1 1 0 001-1V8h7a1 1 0 001-1V5a1 1 0 00-1-1H6a2 2 0 00-2 2v9zm4 18a1 1 0 00-1-1H5a1 1 0 00-1 1v9a2 2 0 002 2h9a1 1 0 001-1v-2a1 1 0 00-1-1H8v-7zm35-17a1 1 0 001-1V6a2 2 0 00-2-2h-9a1 1 0 00-1 1v2a1 1 0 001 1h7v7a1 1 0 001 1h2zm1 17a1 1 0 00-1-1h-2a1 1 0 00-1 1v7h-7a1 1 0 00-1 1v2a1 1 0 001 1h9a2 2 0 002-2v-9zM32.835 11h-6.108c-6.512 0-11.882 4.804-12.636 11h-1.992c-.382 0-.52.046-.66.134a.855.855 0 00-.325.378c-.074.162-.114.324-.114.77v1.436c0 .446.04.608.114.77.075.163.185.291.324.378.14.088.279.134.66.134h2.157c1.179 5.706 6.315 10 12.472 10h6.108c.405 0 .552-.041.7-.12a.819.819 0 00.344-.337c.079-.145.121-.29.121-.688V31h.901c.382 0 .52-.046.66-.134a.855.855 0 00.325-.378c.074-.162.114-.324.114-.77v-1.436c0-.446-.04-.608-.114-.77a.855.855 0 00-.325-.378c-.14-.088-.278-.134-.66-.134H34v-7h.901c.382 0 .52-.046.66-.134a.855.855 0 00.325-.378c.074-.162.114-.324.114-.77v-1.436c0-.446-.04-.608-.114-.77a.855.855 0 00-.325-.378c-.14-.088-.278-.134-.66-.134H34v-3.855c0-.398-.042-.543-.121-.688a.819.819 0 00-.344-.338c-.148-.078-.295-.119-.7-.119zm-2.744 3.571h-3.637c-5.02 0-9.09 3.998-9.09 8.929s4.07 8.929 9.09 8.929h3.637V14.57z" fill="currentColor"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,234 @@
|
||||
<template>
|
||||
<a-row :gutter="8" class="dict-wrapper">
|
||||
<a-col :span="8">
|
||||
<a-card hoverable>
|
||||
<div style="text-align: center;">
|
||||
<a-space direction="vertical" fill>
|
||||
<a-avatar :size="120">
|
||||
<template v-if="userInfoForm.user.avatar == '' ">
|
||||
<img alt="avatar" :src="store.sysConfig.sys_user_avatar" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<img alt="avatar" :src="userInfoForm.user.avatar"/>
|
||||
</template>
|
||||
</a-avatar>
|
||||
<a-typography-title :heading="6">{{userInfoForm.user.nickName}}</a-typography-title>
|
||||
</a-space>
|
||||
</div>
|
||||
<a-divider />
|
||||
<a-card :bordered="false">
|
||||
<a-descriptions title="用户详情" :column="1">
|
||||
<a-descriptions-item label="用户昵称">{{userInfoForm.user.nickName}}</a-descriptions-item>
|
||||
<a-descriptions-item label="登录账号">{{userInfoForm.user.username}}</a-descriptions-item>
|
||||
<a-descriptions-item label="用户手机">{{userInfoForm.user.phone}}</a-descriptions-item>
|
||||
<a-descriptions-item label="用户邮箱">{{userInfoForm.user.email}}</a-descriptions-item>
|
||||
<a-descriptions-item label="用户性别">
|
||||
<template v-if="userInfoForm.user.sex == '0' "><a-tag color="arcoblue">男</a-tag></template>
|
||||
<template v-else-if="userInfoForm.user.sex == '1' "><a-tag color="pinkpurple">女</a-tag></template>
|
||||
<template v-else><a-tag color="gold">中性</a-tag></template>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="所属部门" v-for="item of userInfoForm.posts" >
|
||||
<a-tag>{{ item.postName }}</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="用户角色" v-for="item of userInfoForm.roles" >
|
||||
<a-tag>{{ item.roleName }}</a-tag>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="16">
|
||||
<a-card hoverable>
|
||||
<a-tabs>
|
||||
<a-tab-pane key="1">
|
||||
<template #title>
|
||||
<icon-exclamation-circle /> 基础信息
|
||||
</template>
|
||||
<a-card hoverable :bordered="false">
|
||||
<a-form :model="modalForm" :rules="rules" ref="modalFormRef" label-align="left" auto-label-width>
|
||||
<a-form-item field="nickName" label="用户昵称">
|
||||
<a-input v-model="modalForm.nickName" placeholder="请输入用户昵称" />
|
||||
</a-form-item>
|
||||
<a-form-item field="phone" label="手机号码">
|
||||
<a-input v-model="modalForm.phone" placeholder="请输入手机号码" />
|
||||
</a-form-item>
|
||||
<a-form-item field="email" label="用户邮箱">
|
||||
<a-input v-model="modalForm.email" placeholder="请输入邮箱" />
|
||||
</a-form-item>
|
||||
<a-form-item field="sex" label="用户性别">
|
||||
<a-radio-group v-model="modalForm.sex">
|
||||
<a-radio value="0">男</a-radio>
|
||||
<a-radio value="1">女</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button @click="handleSubmit" type="primary">保存</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2">
|
||||
<template #title>
|
||||
<icon-user /> 修改密码
|
||||
</template>
|
||||
<a-card hoverable :bordered="false">
|
||||
<a-form :model="userPwdForm" :rules="rules" ref="userPwdFormRef" label-align="left" auto-label-width>
|
||||
<a-form-item field="oldPassword" label="旧密码">
|
||||
<a-input v-model="userPwdForm.oldPassword" placeholder="请输入旧密码" />
|
||||
</a-form-item>
|
||||
<a-form-item field="newPassword" label="新密码">
|
||||
<a-input v-model="userPwdForm.newPassword" placeholder="请输入新密码" />
|
||||
</a-form-item>
|
||||
<a-form-item field="confirmPassword" label="确认密码">
|
||||
<a-input v-model="userPwdForm.confirmPassword" placeholder="请输入确认密码" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button @click="handleSubmitUserPwd" type="primary">保存</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, getCurrentInstance } from 'vue';
|
||||
import { useUserStore } from '@/store/userInfo';
|
||||
import { updateUser } from '@/api/admin/sys-user';
|
||||
import { getUserProfile,putUserPwd } from '@/api/profile/profile';
|
||||
|
||||
// use Store
|
||||
const store = useUserStore();
|
||||
|
||||
// 获取当前实例
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const userInfoForm = ref({
|
||||
user: {},
|
||||
posts: [],
|
||||
roles: []
|
||||
});
|
||||
|
||||
const userPwdForm = reactive({});
|
||||
|
||||
// 默认表单
|
||||
const modalForm = reactive({});
|
||||
|
||||
// Rules
|
||||
const rules = {
|
||||
nickName: [{ required: true, message: '请输入用户昵称' }],
|
||||
phone: [
|
||||
{ required: true, message: '请输入联系手机' },
|
||||
// { match: /^1[3456789]\d{9}$/, message: '校验规则: 13011112222'}
|
||||
],
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱' },
|
||||
// { match: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/, message: '校验规则: aka@aka.com'}
|
||||
],
|
||||
sex: [
|
||||
{ required: true, message: '请选择性别' },
|
||||
// { match: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/, message: '校验规则: aka@aka.com'}
|
||||
],
|
||||
oldPassword: [
|
||||
{ required: true, message: '输入旧密码' },
|
||||
// { match: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/, message: '校验规则: aka@aka.com'}
|
||||
],
|
||||
newPassword: [
|
||||
{ required: true, message: '输入新密码' },
|
||||
// { match: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/, message: '校验规则: aka@aka.com'}
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '确认新密码', validator: (value, cb) => {
|
||||
return new Promise(resolve => {
|
||||
window.setTimeout(() => {
|
||||
if (userPwdForm.newPassword !== value) {
|
||||
cb('两次输入的密码不一致')
|
||||
}
|
||||
resolve()
|
||||
}, 1)
|
||||
})
|
||||
} }
|
||||
// { match: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/, message: '校验规则: aka@aka.com'}
|
||||
],
|
||||
};
|
||||
|
||||
const equalToPassword = (value, callback) => {
|
||||
if (modalForm.newPassword !== value) {
|
||||
cb('两次输入的密码不一致')
|
||||
console.log("aaa")
|
||||
} else {
|
||||
cb('密码输入正确')
|
||||
console.log("bbb")
|
||||
}
|
||||
}
|
||||
|
||||
// handleSubmit 新增与修改按钮方法 20220713
|
||||
const handleSubmit = () => {
|
||||
proxy.$refs.modalFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
const { success } = await updateUser(modalForm);
|
||||
if (success) proxy.$message.success('信息修改成功');
|
||||
getCurrentUserInfo();
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 修改用户密码 20221025
|
||||
const handleSubmitUserPwd = () => {
|
||||
proxy.$refs.userPwdFormRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
const { res } = await putUserPwd(userPwdForm);
|
||||
console.loh("success",res)
|
||||
if (success) proxy.$message.success('密码修改成功');
|
||||
getCurrentUserInfo();
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getCurrentUserInfo = async () => {
|
||||
const res = await getUserProfile()
|
||||
userInfoForm.value = res.data
|
||||
Object.assign(modalForm, res.data.user);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getCurrentUserInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.userinfo-row {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
.userinfo-item {
|
||||
&-label {
|
||||
width: 140px;
|
||||
color: #4e5969;
|
||||
text-align: right;
|
||||
}
|
||||
&-value {
|
||||
width: 140px;
|
||||
color: $primary-font-color;
|
||||
}
|
||||
div {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.userinfo-form {
|
||||
width: 520px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,277 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<a-form :model="queryForm" ref="queryFormRef" layout="inline">
|
||||
<a-form-item field="jobName" label="任务名称">
|
||||
<a-input v-model="queryForm.jobName" placeholder="请输入任务名称" @press-enter="handleQuery" />
|
||||
</a-form-item>
|
||||
<a-form-item field="jobGroup" label="任务分组">
|
||||
<a-select v-model="queryForm.jobGroup" placeholder="请选择任务分组">
|
||||
<a-option value="DEFAULT">默认</a-option>
|
||||
<a-option value="SYSTEM">系统</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select v-model="queryForm.status" placeholder="请选择任务状态">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">关闭</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleQuery">查询</a-button>
|
||||
<a-button @click="handleResetQuery">重置</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<div class="table-action">
|
||||
<a-button type="primary" @click="handleAdd">新增定时任务</a-button>
|
||||
</div>
|
||||
|
||||
<a-table :data="tableData" :columns="columns" :pagination="{ 'show-total': true, 'show-jumper': true, 'show-page-size': true, total: pager.count, current: currentPage }">
|
||||
<template #status="{ record }">
|
||||
<a-tag v-if="record.status == 2" color="green">正常</a-tag>
|
||||
<a-tag v-if="record.status == 1" color="red">停用</a-tag>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-button type="text" @click="handleUpdate(record)">修改</a-button>
|
||||
<a-button type="text" status="success" v-if="record.entry_id == 0" @click="handleStart(record.jobId)">启动</a-button>
|
||||
<a-button type="text" status="danger" v-if="record.entry_id !== 0" @click="handleStop(record.jobId)">停止</a-button>
|
||||
<a-button type="text" status="danger" @click="() => { deleteVisible = true; deleteData = [record.jobId]; }">删除</a-button>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="modalVisible"
|
||||
title-align="start"
|
||||
:title="modalTitle"
|
||||
:on-before-ok="onBeforeOk"
|
||||
@ok="handleOk"
|
||||
@cancel="$refs.modalFormRef.resetFields()"
|
||||
>
|
||||
<a-form
|
||||
:model="modalForm"
|
||||
:rules="rules"
|
||||
ref="modalFormRef"
|
||||
auto-label-width
|
||||
>
|
||||
<a-form-item field="jobName" label="任务名称">
|
||||
<a-input
|
||||
v-model="modalForm.jobName"
|
||||
placeholder="请输入任务名称"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="jobGroup" label="任务分组">
|
||||
<a-select v-model="modalForm.jobGroup" placeholder="请选择任务分组">
|
||||
<a-option value="DEFAULT">默认</a-option>
|
||||
<a-option value="SYSTEM">系统</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item field="invokeTarget" label="调用目标">
|
||||
<a-input
|
||||
v-model="modalForm.invokeTarget"
|
||||
placeholder="调用目标"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="args" label="目标参数">
|
||||
<a-input v-model="modalForm.args" placeholder="目标参iuu数"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="cronExpression" label="Cron表达式">
|
||||
<a-input
|
||||
v-model="modalForm.cronExpression"
|
||||
placeholder="Cron表达式"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item field="concurrent" label="是否并发">
|
||||
<a-radio-group v-model="modalForm.concurrent" type="button">
|
||||
<a-radio :value="0">允许</a-radio>
|
||||
<a-radio :value="1">禁止</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="jobType" label="调用类型">
|
||||
<a-radio-group v-model="modalForm.jobType" type="button">
|
||||
<a-radio :value="1">接口</a-radio>
|
||||
<a-radio :value="2">函数</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="misfirePolicy" label="执行策略">
|
||||
<a-radio-group v-model="modalForm.misfirePolicy" type="button">
|
||||
<a-radio :value="1">立即执行</a-radio>
|
||||
<a-radio :value="2">执行一次</a-radio>
|
||||
<a-radio :value="3">放弃执行</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="status" label="状态">
|
||||
<a-select v-model="modalForm.status">
|
||||
<a-option :value="2">正常</a-option>
|
||||
<a-option :value="1">停用</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- Akiraka 20230223 删除与批量删除 开始 -->
|
||||
<DeleteModal
|
||||
:data="deleteData"
|
||||
:visible="deleteVisible"
|
||||
:apiDelete="delSysJob"
|
||||
@deleteVisibleChange="() => deleteVisible = false"
|
||||
/>
|
||||
<!-- Akiraka 20230223 删除与批量删除 结束 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted, getCurrentInstance, watch } from 'vue';
|
||||
import { listSysJob, addSysJob, updateSysJob, delSysJob, startJob, removeJob } from '@/api/sys-job';
|
||||
|
||||
// Akiraka 20230210 删除数据
|
||||
const deleteData = ref([])
|
||||
// Akiraka 20230210 删除对话框
|
||||
const deleteVisible = ref(false)
|
||||
// Akiraka 20230210 监听删除事件
|
||||
watch(() => deleteVisible.value ,(value) => {
|
||||
if ( value == false ) {
|
||||
getSysJobListInfo(queryForm);
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const currentPage = ref(1);
|
||||
// Pager
|
||||
const pager = {
|
||||
count: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
const queryForm = reactive({});
|
||||
const modalForm = reactive({
|
||||
concurrent: 1,
|
||||
jobType: 1,
|
||||
misfirePolicy: 1,
|
||||
status: 2,
|
||||
});
|
||||
|
||||
// 表单检验规则
|
||||
const rules = {
|
||||
jobName: [{ required: true, message: '请输入任务名称' }],
|
||||
jobGroup: [{ required: true, message: '请选择任务分组' }],
|
||||
invokeTarget: [{ required: true, message: '请输入调用目标' }],
|
||||
cronExpression: [{ required: true, message: '请输入Cron表达式' }],
|
||||
status: [{ required: true, message: '请选择状态' }],
|
||||
};
|
||||
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref('默认标题');
|
||||
|
||||
const tableData = ref([]);
|
||||
const columns = [
|
||||
{ title: '编号', dataIndex: 'jobId' },
|
||||
{ title: '任务名称', dataIndex: 'jobName' },
|
||||
{ title: '任务分组', dataIndex: 'jobGroup' },
|
||||
{ title: '任务表达式', dataIndex: 'cronExpression' },
|
||||
{ title: '调用目标', dataIndex: 'invokeTarget' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status' },
|
||||
{ title: '操作', slotName: 'action' },
|
||||
];
|
||||
|
||||
// 查询任务
|
||||
const handleQuery = () => {
|
||||
getSysJobListInfo(queryForm);
|
||||
}
|
||||
|
||||
// 重置查询
|
||||
const handleResetQuery = () => {
|
||||
proxy.$refs.queryFormRef.resetFields();
|
||||
|
||||
getSysJobListInfo();
|
||||
}
|
||||
|
||||
// 新增任务
|
||||
const handleAdd = () => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '新增任务';
|
||||
};
|
||||
|
||||
// 修改任务
|
||||
const handleUpdate = (record) => {
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = '修改任务';
|
||||
|
||||
Object.assign(modalForm, record);
|
||||
};
|
||||
|
||||
// 启动定时任务
|
||||
const handleStart = async (jobId) => {
|
||||
await startJob(jobId);
|
||||
proxy.$notification.success('启动任务成功!')
|
||||
|
||||
getSysJobListInfo();
|
||||
}
|
||||
|
||||
// 关闭定时任务
|
||||
const handleStop = async (jobId) => {
|
||||
await removeJob(jobId);
|
||||
proxy.$notification.success('已停止任务!')
|
||||
|
||||
getSysJobListInfo();
|
||||
}
|
||||
|
||||
// Modal 触发oK事件前
|
||||
const onBeforeOk = (done) => {
|
||||
proxy.$refs.modalFormRef.validate((err) => {
|
||||
if (!err) {
|
||||
return done();
|
||||
} else {
|
||||
proxy.$message.error('表单校验失败');
|
||||
return done(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Modal 触发ok事件
|
||||
const handleOk = async () => {
|
||||
if (modalForm.jobId) {
|
||||
const { code, msg } = await updateSysJob(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('修改成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
} else {
|
||||
const { code, msg } = await addSysJob(modalForm);
|
||||
if (code == 200 ) {
|
||||
proxy.$notification.success('新增成功');
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
}
|
||||
getSysJobListInfo();
|
||||
};
|
||||
|
||||
// 获取系统任务信息
|
||||
const getSysJobListInfo = async (params = {}) => {
|
||||
const { data, code, msg } = await listSysJob(params);
|
||||
if ( code == 200 ) {
|
||||
tableData.value = data.list;
|
||||
Object.assign(pager, { count: data.count, pageIndex: data.pageIndex, pageSize: data.pageSize });
|
||||
} else {
|
||||
proxy.$notification.error(msg);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getSysJobListInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-action {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<div class="card-wrapper">
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="12">
|
||||
<a-card title="服务器信息">
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">主机名称</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.os?.hostName}}</span>
|
||||
</div>
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">操作系统</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.os?.goOs }}</span>
|
||||
</div>
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">服务器IP</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.os?.ip }}</span>
|
||||
</div>
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">系统架构</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.os?.arch }}</span>
|
||||
</div>
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">CPU</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.cpu?.cpuInfo[0]?.modelName }}</span>
|
||||
</div>
|
||||
<div class="card-item card-item-server">
|
||||
<span class="card-item-title">当前时间</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.os?.time }}</span>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-card :title="SystemInfo.location">
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">系统</span>
|
||||
<span class="card-item-desc">Linux</span>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">时间</span>
|
||||
<span class="card-item-desc">2022-05-06 15:32:22</span>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">内存</span>
|
||||
<span class="card-item-desc">129MB/2349MB</span>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">在线时间</span>
|
||||
<span class="card-item-desc">{{ SystemInfo.bootTime }}分钟</span>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">交换</span>
|
||||
<span class="card-item-desc">0/0</span>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">硬盘</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.disk?.used }}GB/{{ SystemInfo?.disk?.total }}GB</span>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="12">
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">下载</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.net?.in }}KB</span>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item">
|
||||
<span class="card-item-title">上传</span>
|
||||
<span class="card-item-desc">{{ SystemInfo?.net?.out }}KB</span>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- progress -->
|
||||
<div class="progress-wrapper">
|
||||
<span class="progress-title">CPU</span>
|
||||
<a-progress status="success" :percent="SystemInfo?.cpu?.percent / 100" />
|
||||
</div>
|
||||
<div class="progress-wrapper">
|
||||
<span class="progress-title">RAM</span>
|
||||
<a-progress status="warning" :percent="SystemInfo?.mem?.percent / 100" />
|
||||
</div>
|
||||
<div class="progress-wrapper">
|
||||
<span class="progress-title">硬盘</span>
|
||||
<a-progress :percent="SystemInfo?.disk?.percent / 100" />
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import { getServerMonitor } from '@/api/sys-tools/monitor';
|
||||
|
||||
|
||||
const SystemInfo = reactive({});
|
||||
|
||||
const getServerMonitorInfo = async () => {
|
||||
const res = await getServerMonitor();
|
||||
|
||||
Object.assign(SystemInfo, res)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getServerMonitorInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid rgb(229, 230, 235);
|
||||
&-title {
|
||||
color: $primary-font-color;
|
||||
}
|
||||
&-desc {
|
||||
color: $secondary-font-color;
|
||||
}
|
||||
}
|
||||
|
||||
.card-item-server:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.progress-wrapper {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
.progress-title {
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user