feat: 菜单权限获取方式调整、前端代码优化、数据库新增数据方式调整

This commit is contained in:
meilin.huang
2023-04-13 20:11:22 +08:00
parent 8e64ba67fa
commit 1d858118d5
22 changed files with 329 additions and 233 deletions

View File

@@ -28,7 +28,7 @@ class Api {
/** /**
* 操作该权限即请求对应的url * 操作该权限即请求对应的url
* @param {Object} param 请求该权限的参数 * @param {Object} param 请求该api的参数
*/ */
request(param: any = null, options: any = null): Promise<any> { request(param: any = null, options: any = null): Promise<any> {
return request.send(this, param, options); return request.send(this, param, options);
@@ -36,7 +36,8 @@ class Api {
/** /**
* 操作该权限即请求对应的url * 操作该权限即请求对应的url
* @param {Object} param 请求该权限的参数 * @param {Object} param 请求该api的参数
* @param headers headers
*/ */
requestWithHeaders(param: any, headers: any): Promise<any> { requestWithHeaders(param: any, headers: any): Promise<any> {
return request.sendWithHeaders(this, param, headers); return request.sendWithHeaders(this, param, headers);
@@ -50,9 +51,41 @@ class Api {
* @param url url * @param url url
* @param method 请求方法(get,post,put,delete...) * @param method 请求方法(get,post,put,delete...)
*/ */
static create(url: string, method: string) { static create(url: string, method: string) :Api {
return new Api(url, method); return new Api(url, method);
} }
/**
* 创建get api
* @param url url
*/
static newGet(url: string): Api {
return Api.create(url, 'get');
}
/**
* new post api
* @param url url
*/
static newPost(url: string): Api {
return Api.create(url, 'post');
}
/**
* new put api
* @param url url
*/
static newPut(url: string): Api {
return Api.create(url, 'put');
}
/**
* new delete api
* @param url url
*/
static newDelete(url: string): Api {
return Api.create(url, 'delete');
}
} }

View File

@@ -1,11 +1,11 @@
import request from './request' import Api from './Api'
export default { export default {
login: (param: any) => request.request('POST', '/sys/accounts/login', param), login: Api.newPost("/sys/accounts/login"),
changePwd: (param: any) => request.request('POST', '/sys/accounts/change-pwd', param), changePwd: Api.newPost("/sys/accounts/change-pwd"),
getPublicKey: () => request.request('GET', '/common/public-key'), getPublicKey: Api.newGet("/common/public-key"),
getConfigValue: (param: any) => request.request('GET', '/sys/configs/value', param), getConfigValue: Api.newGet("/sys/configs/value"),
captcha: () => request.request('GET', '/sys/captcha'), captcha: Api.newGet("/sys/captcha"),
logout: (param: any) => request.request('POST', '/sys/accounts/logout/{token}', param), logout: Api.newPost("/sys/accounts/logout/{token}"),
getMenuRoute: (param: any) => request.request('Get', '/sys/resources/account', param) getPermissions: Api.newGet("/sys/accounts/permissions")
} }

View File

@@ -22,7 +22,8 @@ export interface Result {
data?: any; data?: any;
} }
const baseUrl: string = config.baseApiUrl as string const baseUrl: string = config.baseApiUrl
const baseWsUrl: string = config.baseWsUrl
/** /**
* 通知错误消息 * 通知错误消息
@@ -115,9 +116,8 @@ function request(method: string, url: string, params: any = null, headers: any =
query.headers = headers query.headers = headers
} }
const lowMethod = method.toLowerCase();
// post和put使用json格式传参 // post和put使用json格式传参
if (lowMethod === 'post' || lowMethod === 'put') { if (method === 'post' || method === 'put') {
query.data = params; query.data = params;
} else { } else {
query.params = params; query.params = params;
@@ -155,6 +155,7 @@ function getApiUrl(url: string) {
return baseUrl + url + '?token=' + getSession('token'); return baseUrl + url + '?token=' + getSession('token');
} }
export default { export default {
request, request,
send, send,

View File

@@ -9,7 +9,7 @@ export async function getRsaPublicKey() {
if (publicKey) { if (publicKey) {
return publicKey return publicKey
} }
publicKey = await openApi.getPublicKey() as string publicKey = await openApi.getPublicKey.request() as string
sessionStorage.setItem('RsaPublicKey', publicKey) sessionStorage.setItem('RsaPublicKey', publicKey)
return publicKey return publicKey
} }

View File

@@ -11,7 +11,7 @@ const UseWartermarkConfigKey = "UseWartermark"
* @returns 配置值 * @returns 配置值
*/ */
export async function getConfigValue(key: string) : Promise<string> { export async function getConfigValue(key: string) : Promise<string> {
return await openApi.getConfigValue({key}) as string return await openApi.getConfigValue.request({key}) as string
} }
/** /**

View File

@@ -46,8 +46,8 @@ export function initAllFun() {
useRoutesList().setRoutesList(setFilterMenuFun(dynamicRoutes[0].children, useUserInfo().userInfo.menus)) useRoutesList().setRoutesList(setFilterMenuFun(dynamicRoutes[0].children, useUserInfo().userInfo.menus))
} }
// 后端控制路由:模拟执行路由数据初始化 // 后端控制路由:执行路由数据初始化
export function initBackEndControlRoutesFun() { export async function initBackEndControlRoutesFun() {
NextLoading.start(); // 界面 loading 动画开始执行 NextLoading.start(); // 界面 loading 动画开始执行
const token = getSession('token'); // 获取浏览器缓存 token 值 const token = getSession('token'); // 获取浏览器缓存 token 值
if (!token) { if (!token) {
@@ -55,11 +55,8 @@ export function initBackEndControlRoutesFun() {
return false return false
} }
useUserInfo().setUserInfo({}); useUserInfo().setUserInfo({});
let menuRoute = getSession('menus') // 获取路由
if (!menuRoute) { let menuRoute = await getBackEndControlRoutes();
menuRoute = getBackEndControlRoutes(); // 获取路由
// const oldRoutes = res; // 获取接口原始路由未处理component
}
dynamicRoutes[0].children = backEndRouterConverter(menuRoute); // 处理路由component dynamicRoutes[0].children = backEndRouterConverter(menuRoute); // 处理路由component
// 添加404界面 // 添加404界面
router.addRoute(pathMatch); router.addRoute(pathMatch);
@@ -72,8 +69,16 @@ export function initBackEndControlRoutesFun() {
} }
// 后端控制路由isRequestRoutes 为 true则开启后端控制路由 // 后端控制路由isRequestRoutes 为 true则开启后端控制路由
export function getBackEndControlRoutes() { export async function getBackEndControlRoutes() {
return openApi.getMenuRoute({}); try {
const menuAndPermission = await openApi.getPermissions.request();
// 赋值权限码,用于控制按钮等
useUserInfo().userInfo.permissions = menuAndPermission.permissions;
return menuAndPermission.menus;
} catch (e: any) {
console.error(e);
return []
}
} }
// 后端控制路由,后端返回路由 转换为vue route // 后端控制路由,后端返回路由 转换为vue route
@@ -124,18 +129,18 @@ export function backEndRouterConverter(routes: any, parentPath: string = "/") {
* @returns 返回处理成函数后的 component * @returns 返回处理成函数后的 component
*/ */
export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) { export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
const keys = Object.keys(dynamicViewsModules); const keys = Object.keys(dynamicViewsModules);
const matchKeys = keys.filter((key) => { const matchKeys = keys.filter((key) => {
const k = key.replace(/..\/views|../, ''); const k = key.replace(/..\/views|../, '');
return k.startsWith(`${component}`) || k.startsWith(`/${component}`); return k.startsWith(`${component}`) || k.startsWith(`/${component}`);
}); });
if (matchKeys?.length === 1) { if (matchKeys?.length === 1) {
const matchKey = matchKeys[0]; const matchKey = matchKeys[0];
return dynamicViewsModules[matchKey]; return dynamicViewsModules[matchKey];
} }
if (matchKeys?.length > 1) { if (matchKeys?.length > 1) {
return false; return false;
} }
} }
// 多级嵌套数组处理成一维数组 // 多级嵌套数组处理成一维数组
@@ -167,7 +172,6 @@ export function formatTwoStageRoutes(arr: any) {
} }
}); });
useKeepALiveNames().setCacheKeepAlive(cacheList); useKeepALiveNames().setCacheKeepAlive(cacheList);
// store.dispatch('keepAliveNames/setCacheKeepAlive', cacheList);
return newArr; return newArr;
} }
@@ -227,21 +231,22 @@ export function resetRoute() {
}); });
} }
// 初始化方法执行 export async function initRouter() {
const { isRequestRoutes } = useThemeConfig(pinia).themeConfig; // 初始化方法执行
if (!isRequestRoutes) { const { isRequestRoutes } = useThemeConfig(pinia).themeConfig;
// 未开启后端控制路由 if (!isRequestRoutes) {
initAllFun(); // 未开启后端控制路由
} else if (isRequestRoutes) { initAllFun();
// 后端控制路由isRequestRoutes 为 true则开启后端控制路由 } else if (isRequestRoutes) {
initBackEndControlRoutesFun(); // 后端控制路由isRequestRoutes 为 true则开启后端控制路由
await initBackEndControlRoutesFun();
}
} }
let SysWs: any; let SysWs: any;
// 路由加载前 // 路由加载前
router.beforeEach((to, from, next) => { router.beforeEach(async (to, from, next) => {
NProgress.configure({ showSpinner: false }); NProgress.configure({ showSpinner: false });
if (to.meta.title) NProgress.start(); if (to.meta.title) NProgress.start();
@@ -278,7 +283,10 @@ router.beforeEach((to, from, next) => {
if (!SysWs && to.path != '/machine/terminal') { if (!SysWs && to.path != '/machine/terminal') {
SysWs = sockets.sysMsgSocket(); SysWs = sockets.sysMsgSocket();
} }
if (useRoutesList().routesList.length > 0) { if (useRoutesList().routesList.length == 0) {
await initRouter();
next({ path: to.path, query: to.query });
} else {
next(); next();
} }
}); });

View File

@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { getSession } from '@/common/utils/storage.ts'; import { getSession } from '@/common/utils/storage';
export const useUserInfo = defineStore('userInfo', { export const useUserInfo = defineStore('userInfo', {
state: (): UserInfoState => ({ state: (): UserInfoState => ({

View File

@@ -1,6 +1,6 @@
import Api from '@/common/Api'; import Api from '@/common/Api';
export const indexApi = { export const indexApi = {
getIndexCount: Api.create("/common/index/count", 'get'), getIndexCount: Api.newGet("/common/index/count"),
} }

View File

@@ -63,7 +63,7 @@
import { nextTick, onMounted, ref, toRefs, reactive, computed } from 'vue'; import { nextTick, onMounted, ref, toRefs, reactive, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { initBackEndControlRoutesFun } from '@/router/index'; import { initRouter } from '@/router/index';
import { setSession, setUserInfo2Session, setUseWatermark2Session } from '@/common/utils/storage'; import { setSession, setUserInfo2Session, setUseWatermark2Session } from '@/common/utils/storage';
import { formatAxis } from '@/common/utils/format'; import { formatAxis } from '@/common/utils/format';
import openApi from '@/common/openApi'; import openApi from '@/common/openApi';
@@ -71,7 +71,6 @@ import { RsaEncrypt } from '@/common/rsa';
import { useLoginCaptcha, useWartermark } from '@/common/sysconfig'; import { useLoginCaptcha, useWartermark } from '@/common/sysconfig';
import { letterAvatar } from '@/common/utils/string'; import { letterAvatar } from '@/common/utils/string';
import { useUserInfo } from '@/store/userInfo'; import { useUserInfo } from '@/store/userInfo';
import { useThemeConfig } from '@/store/themeConfig';
const rules = { const rules = {
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }], username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
@@ -138,7 +137,7 @@ const getCaptcha = async () => {
if (!state.isUseLoginCaptcha) { if (!state.isUseLoginCaptcha) {
return; return;
} }
let res: any = await openApi.captcha(); let res: any = await openApi.captcha.request();
state.captchaImage = res.base64Captcha; state.captchaImage = res.base64Captcha;
state.loginForm.cid = res.cid; state.loginForm.cid = res.cid;
}; };
@@ -167,10 +166,9 @@ const onSignIn = async () => {
try { try {
const loginReq = { ...state.loginForm }; const loginReq = { ...state.loginForm };
loginReq.password = await RsaEncrypt(originPwd); loginReq.password = await RsaEncrypt(originPwd);
loginRes = await openApi.login(loginReq); loginRes = await openApi.login.request(loginReq);
// 存储 token 到浏览器缓存 // 存储 token 到浏览器缓存
setSession('token', loginRes.token); setSession('token', loginRes.token);
setSession('menus', loginRes.menus);
} catch (e: any) { } catch (e: any) {
state.loading.signIn = false; state.loading.signIn = false;
state.loginForm.captcha = ''; state.loginForm.captcha = '';
@@ -192,8 +190,6 @@ const onSignIn = async () => {
// 头像 // 头像
photo: letterAvatar(state.loginForm.username), photo: letterAvatar(state.loginForm.username),
time: new Date().getTime(), time: new Date().getTime(),
// // 菜单资源code数组
// menus: loginRes.menus,
permissions: loginRes.permissions, permissions: loginRes.permissions,
lastLoginTime: loginRes.lastLoginTime, lastLoginTime: loginRes.lastLoginTime,
lastLoginIp: loginRes.lastLoginIp, lastLoginIp: loginRes.lastLoginIp,
@@ -202,20 +198,9 @@ const onSignIn = async () => {
// 存储用户信息到浏览器缓存 // 存储用户信息到浏览器缓存
setUserInfo2Session(userInfos); setUserInfo2Session(userInfos);
// 1、请注意执行顺序(存储用户信息到vuex) // 1、请注意执行顺序(存储用户信息到vuex)
useUserInfo().setUserInfo(userInfos) useUserInfo().setUserInfo(userInfos);
// store.dispatch('userInfos/setUserInfos', userInfos); await initRouter();
if (!useThemeConfig().themeConfig.isRequestRoutes) { signInSuccess();
// 前端控制路由2、请注意执行顺序
// await initAllFun();
await initBackEndControlRoutesFun();
signInSuccess();
} else {
// 模拟后端控制路由isRequestRoutes 为 true则开启后端控制路由
// 添加完动态路由,再进行 router 跳转,否则可能报错 No match found for location with path "/"
await initBackEndControlRoutesFun();
// 执行完 initBackEndControlRoutesFun再执行 signInSuccess
signInSuccess();
}
}; };
// 登录成功后的跳转 // 登录成功后的跳转
@@ -248,7 +233,7 @@ const changePwd = () => {
const changePwdReq: any = { ...form }; const changePwdReq: any = { ...form };
changePwdReq.oldPassword = await RsaEncrypt(form.oldPassword); changePwdReq.oldPassword = await RsaEncrypt(form.oldPassword);
changePwdReq.newPassword = await RsaEncrypt(form.newPassword); changePwdReq.newPassword = await RsaEncrypt(form.newPassword);
await openApi.changePwd(changePwdReq); await openApi.changePwd.request(changePwdReq);
ElMessage.success('密码修改成功, 新密码已填充至登录密码框'); ElMessage.success('密码修改成功, 新密码已填充至登录密码框');
state.loginForm.password = state.changePwdDialog.form.newPassword; state.loginForm.password = state.changePwdDialog.form.newPassword;
state.changePwdDialog.visible = false; state.changePwdDialog.visible = false;

View File

@@ -135,7 +135,7 @@ const state = reactive({
tabs, tabs,
dataTabsTableHeight: '600', dataTabsTableHeight: '600',
editorHeight: '600', editorHeight: '600',
tagTreeHeight: window.innerHeight - 173 + 'px', tagTreeHeight: window.innerHeight - 178 + 'px',
genSqlDialog: { genSqlDialog: {
visible: false, visible: false,
sql: '', sql: '',

View File

@@ -2,27 +2,27 @@ import Api from '@/common/Api';
export const dbApi = { export const dbApi = {
// 获取权限列表 // 获取权限列表
dbs: Api.create("/dbs", 'get'), dbs: Api.newGet("/dbs"),
saveDb: Api.create("/dbs", 'post'), saveDb: Api.newPost("/dbs"),
getAllDatabase: Api.create("/dbs/databases", 'post'), getAllDatabase: Api.newPost("/dbs/databases"),
getDbPwd: Api.create("/dbs/{id}/pwd", 'get'), getDbPwd: Api.newGet("/dbs/{id}/pwd"),
deleteDb: Api.create("/dbs/{id}", 'delete'), deleteDb: Api.newDelete("/dbs/{id}"),
dumpDb: Api.create("/dbs/{id}/dump", 'post'), dumpDb: Api.newPost("/dbs/{id}/dump"),
tableInfos: Api.create("/dbs/{id}/t-infos", 'get'), tableInfos: Api.newGet("/dbs/{id}/t-infos"),
tableIndex: Api.create("/dbs/{id}/t-index", 'get'), tableIndex: Api.newGet("/dbs/{id}/t-index"),
tableDdl: Api.create("/dbs/{id}/t-create-ddl", 'get'), tableDdl: Api.newGet("/dbs/{id}/t-create-ddl"),
tableMetadata: Api.create("/dbs/{id}/t-metadata", 'get'), tableMetadata: Api.newGet("/dbs/{id}/t-metadata"),
columnMetadata: Api.create("/dbs/{id}/c-metadata", 'get'), columnMetadata: Api.newGet("/dbs/{id}/c-metadata"),
// 获取表即列提示 // 获取表即列提示
hintTables: Api.create("/dbs/{id}/hint-tables", 'get'), hintTables: Api.newGet("/dbs/{id}/hint-tables"),
sqlExec: Api.create("/dbs/{id}/exec-sql", 'post'), sqlExec: Api.newPost("/dbs/{id}/exec-sql"),
// 保存sql // 保存sql
saveSql: Api.create("/dbs/{id}/sql", 'post'), saveSql: Api.newPost("/dbs/{id}/sql"),
// 获取保存的sql // 获取保存的sql
getSql: Api.create("/dbs/{id}/sql", 'get'), getSql: Api.newGet("/dbs/{id}/sql"),
// 获取保存的sql names // 获取保存的sql names
getSqlNames: Api.create("/dbs/{id}/sql-names", 'get'), getSqlNames: Api.newGet("/dbs/{id}/sql-names"),
deleteDbSql: Api.create("/dbs/{id}/sql", 'delete'), deleteDbSql: Api.newDelete("/dbs/{id}/sql"),
// 获取数据库sql执行记录 // 获取数据库sql执行记录
getSqlExecs: Api.create("/dbs/{dbId}/sql-execs", 'get'), getSqlExecs: Api.newGet("/dbs/{dbId}/sql-execs"),
} }

View File

@@ -6,7 +6,7 @@
</el-link> </el-link>
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
<el-link @click="addRow()" type="primary" icon="plus" :underline="false"></el-link> <el-link @click="onShowAddDataDialog()" type="primary" icon="plus" :underline="false"></el-link>
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
<el-link @click="onDeleteData()" type="danger" icon="delete" :underline="false"></el-link> <el-link @click="onDeleteData()" type="danger" icon="delete" :underline="false"></el-link>
@@ -37,8 +37,8 @@
</el-tooltip> </el-tooltip>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16">
<el-input v-model="condition" placeholder="若需条件过滤,可选择列并点击对应的字段并输入需要过滤的内容点击查询按钮即可" clearable @clear="selectData" size="small" <el-input v-model="condition" placeholder="若需条件过滤,可选择列并点击对应的字段并输入需要过滤的内容点击查询按钮即可" clearable
style="width: 100%"> @clear="selectData" size="small" style="width: 100%">
<template #prepend> <template #prepend>
<el-popover trigger="click" :width="320" placement="right"> <el-popover trigger="click" :width="320" placement="right">
<template #reference> <template #reference>
@@ -64,9 +64,9 @@
</el-col> </el-col>
</el-row> </el-row>
<db-table ref="dbTableRef" :db-id="state.ti.dbId" :db="state.ti.db" :data="datas" <db-table ref="dbTableRef" :db-id="state.ti.dbId" :db="state.ti.db" :data="datas" :table="state.table"
:table="state.table" :column-names="columnNames" :loading="loading" :height="tableHeight" :column-names="columnNames" :loading="loading" :height="tableHeight" :show-column-tip="true"
:show-column-tip="true" :sortable="'custom'" @sort-change="(sort: any) => onTableSortChange(sort)" :sortable="'custom'" @sort-change="(sort: any) => onTableSortChange(sort)"
@selection-change="onDataSelectionChange" @change-updated-field="changeUpdatedField"></db-table> @selection-change="onDataSelectionChange" @change-updated-field="changeUpdatedField"></db-table>
<el-row type="flex" class="mt5" justify="center"> <el-row type="flex" class="mt5" justify="center">
@@ -99,6 +99,26 @@
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog v-model="addDataDialog.visible" :title="addDataDialog.title" :destroy-on-close="true" width="600px">
<el-form ref="dataForm" :model="addDataDialog.data" label-width="160px">
<el-form-item v-for="column in columns" class="w100" :prop="column.columnName" :label="column.columnName"
:required="column.nullable != 'YES' && column.columnKey != 'PRI'">
<el-input-number v-if="DbInst.isNumber(column.columnType)"
v-model="addDataDialog.data[`${column.columnName}`]"
:placeholder="`${column.columnType} ${column.columnComment}`" class="w100" />
<el-input v-else v-model="addDataDialog.data[`${column.columnName}`]"
:placeholder="`${column.columnType} ${column.columnComment}`" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="closeAddDataDialog">取消</el-button>
<el-button type="primary" @click="addRow">确定</el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
@@ -113,6 +133,7 @@ import { dateStrFormat } from '@/common/utils/date';
import DbTable from '../DbTable.vue' import DbTable from '../DbTable.vue'
const emits = defineEmits(['genInsertSql']) const emits = defineEmits(['genInsertSql'])
const dataForm: any = ref(null);
const props = defineProps({ const props = defineProps({
data: { data: {
@@ -136,7 +157,7 @@ const state = reactive({
condition: '', // 当前条件框的条件 condition: '', // 当前条件框的条件
loading: false, // 是否在加载数据 loading: false, // 是否在加载数据
columnNames: [], columnNames: [],
columns: [], columns: [] as any,
pageNum: 1, pageNum: 1,
count: 0, count: 0,
selectionDatas: [] as any, selectionDatas: [] as any,
@@ -149,6 +170,12 @@ const state = reactive({
condition: '=', condition: '=',
value: null value: null
}, },
addDataDialog: {
data: {},
title: '',
placeholder: '',
visible: false,
},
tableHeight: '600', tableHeight: '600',
hasUpdatedFileds: false, hasUpdatedFileds: false,
}); });
@@ -163,6 +190,7 @@ const {
count, count,
hasUpdatedFileds, hasUpdatedFileds,
conditionDialog, conditionDialog,
addDataDialog,
} = toRefs(state); } = toRefs(state);
watch(() => props.tableHeight, (newValue: any) => { watch(() => props.tableHeight, (newValue: any) => {
@@ -323,20 +351,43 @@ const cancelUpdateFields = () => {
dbTableRef.value.cancelUpdateFields(); dbTableRef.value.cancelUpdateFields();
} }
const onShowAddDataDialog = async () => {
state.addDataDialog.title = `添加'${state.table}'表数据`
state.addDataDialog.visible = true;
};
const closeAddDataDialog = () => {
state.addDataDialog.visible = false;
state.addDataDialog.data = {};
}
// 添加新数据行 // 添加新数据行
const addRow = async () => { const addRow = async () => {
const columns = state.ti.getNowDb().getColumns(state.table); dataForm.value.validate(async (valid: boolean) => {
// key: 字段名value: 字段名提示 if (valid) {
let obj: any = {}; const data = state.addDataDialog.data;
columns.forEach((item: any) => { // key: 字段名value: 字段名提示
obj[`${item.columnName}`] = `'${item.columnComment || ''} ${item.columnName}[${item.columnType}]${item.nullable == 'YES' ? '' : '[not null]'}'`; let obj: any = {};
}); for (let item of state.columns) {
let columnNames = Object.keys(obj).join(','); const value = data[item.columnName]
let values = Object.values(obj).join(','); if (!value) {
let sql = `INSERT INTO ${state.table} (${columnNames}) VALUES (${values});`; continue
state.ti.getNowDbInst().promptExeSql(state.ti.db, sql, null, () => { }
onRefresh(); obj[`${item.columnName}`] = DbInst.wrapValueByType(value);
}
let columnNames = Object.keys(obj).join(',');
let values = Object.values(obj).join(',');
let sql = `INSERT INTO ${state.table} (${columnNames}) VALUES (${values});`;
state.ti.getNowDbInst().promptExeSql(state.ti.db, sql, null, () => {
closeAddDataDialog();
onRefresh();
});
} else {
ElMessage.error('请正确填写数据信息');
return false;
}
}); });
}; };
</script> </script>

View File

@@ -274,12 +274,21 @@ export class DbInst {
* 根据字段类型包装字段值,如为字符串等则添加‘’,数字类型则直接返回即可 * 根据字段类型包装字段值,如为字符串等则添加‘’,数字类型则直接返回即可
*/ */
static wrapColumnValue(columnType: string, value: any) { static wrapColumnValue(columnType: string, value: any) {
if (columnType.match(/int|double|float|nubmer|decimal|byte|bit/gi)) { if (this.isNumber(columnType)) {
return value; return value;
} }
return `'${value}'`; return `'${value}'`;
}; };
/**
* 判断字段类型是否为数字类型
* @param columnType 字段类型
* @returns
*/
static isNumber(columnType: string) {
return columnType.match(/int|double|float|nubmer|decimal|byte|bit/gi);
};
/** /**
* *
* @param str 字符串 * @param str 字符串

View File

@@ -2,45 +2,45 @@ import Api from '@/common/Api';
export const machineApi = { export const machineApi = {
// 获取权限列表 // 获取权限列表
list: Api.create("/machines", 'get'), list: Api.newGet("/machines"),
getMachinePwd: Api.create("/machines/{id}/pwd", 'get'), getMachinePwd: Api.newGet("/machines/{id}/pwd"),
info: Api.create("/machines/{id}/sysinfo", 'get'), info: Api.newGet("/machines/{id}/sysinfo"),
stats: Api.create("/machines/{id}/stats", 'get'), stats: Api.newGet("/machines/{id}/stats"),
process: Api.create("/machines/{id}/process", 'get'), process: Api.newGet("/machines/{id}/process"),
// 终止进程 // 终止进程
killProcess: Api.create("/machines/{id}/process", 'delete'), killProcess: Api.newDelete("/machines/{id}/process"),
closeCli: Api.create("/machines/{id}/close-cli", 'delete'), closeCli: Api.newDelete("/machines/{id}/close-cli"),
testConn: Api.create("/machines/test-conn", 'post'), testConn: Api.newPost("/machines/test-conn"),
// 保存按钮 // 保存按钮
saveMachine: Api.create("/machines", 'post'), saveMachine: Api.newPost("/machines"),
// 调整状态 // 调整状态
changeStatus: Api.create("/machines/{id}/{status}", 'put'), changeStatus: Api.newPut("/machines/{id}/{status}"),
// 删除机器 // 删除机器
del: Api.create("/machines/{id}", 'delete'), del: Api.newDelete("/machines/{id}"),
scripts: Api.create("/machines/{machineId}/scripts", 'get'), scripts: Api.newGet("/machines/{machineId}/scripts"),
runScript: Api.create("/machines/{machineId}/scripts/{scriptId}/run", 'get'), runScript: Api.newGet("/machines/{machineId}/scripts/{scriptId}/run"),
saveScript: Api.create("/machines/{machineId}/scripts", 'post'), saveScript: Api.newPost("/machines/{machineId}/scripts"),
deleteScript: Api.create("/machines/{machineId}/scripts/{scriptId}", 'delete'), deleteScript: Api.newDelete("/machines/{machineId}/scripts/{scriptId}"),
// 获取配置文件列表 // 获取配置文件列表
files: Api.create("/machines/{id}/files", 'get'), files: Api.newGet("/machines/{id}/files"),
lsFile: Api.create("/machines/{machineId}/files/{fileId}/read-dir", 'get'), lsFile: Api.newGet("/machines/{machineId}/files/{fileId}/read-dir"),
rmFile: Api.create("/machines/{machineId}/files/{fileId}/remove", 'delete'), rmFile: Api.newDelete("/machines/{machineId}/files/{fileId}/remove"),
uploadFile: Api.create("/machines/{machineId}/files/{fileId}/upload?token={token}", 'post'), uploadFile: Api.newPost("/machines/{machineId}/files/{fileId}/upload?token={token}"),
fileContent: Api.create("/machines/{machineId}/files/{fileId}/read", 'get'), fileContent: Api.newGet("/machines/{machineId}/files/{fileId}/read"),
createFile: Api.create("/machines/{machineId}/files/{id}/create-file", 'post'), createFile: Api.newPost("/machines/{machineId}/files/{id}/create-file"),
// 修改文件内容 // 修改文件内容
updateFileContent: Api.create("/machines/{machineId}/files/{id}/write", 'post'), updateFileContent: Api.newPost("/machines/{machineId}/files/{id}/write"),
// 添加文件or目录 // 添加文件or目录
addConf: Api.create("/machines/{machineId}/files", 'post'), addConf: Api.newPost("/machines/{machineId}/files"),
// 删除配置的文件or目录 // 删除配置的文件or目录
delConf: Api.create("/machines/{machineId}/files/{id}", 'delete'), delConf: Api.newDelete("/machines/{machineId}/files/{id}"),
terminal: Api.create("/api/machines/{id}/terminal", 'get'), terminal: Api.newGet("/api/machines/{id}/terminal"),
recDirNames: Api.create("/machines/rec/names", 'get') recDirNames: Api.newGet("/machines/rec/names")
} }
export const authCertApi = { export const authCertApi = {
baseList : Api.create("/sys/authcerts/base", 'get'), baseList : Api.newGet("/sys/authcerts/base"),
list: Api.create("/sys/authcerts", 'get'), list: Api.newGet("/sys/authcerts"),
save: Api.create("/sys/authcerts", 'post'), save: Api.newPost("/sys/authcerts"),
delete: Api.create("/sys/authcerts/{id}", 'delete'), delete: Api.newDelete("/sys/authcerts/{id}"),
} }

View File

@@ -1,14 +1,14 @@
import Api from '@/common/Api'; import Api from '@/common/Api';
export const mongoApi = { export const mongoApi = {
mongoList : Api.create("/mongos", 'get'), mongoList : Api.newGet("/mongos"),
saveMongo : Api.create("/mongos", 'post'), saveMongo : Api.newPost("/mongos"),
deleteMongo : Api.create("/mongos/{id}", 'delete'), deleteMongo : Api.newDelete("/mongos/{id}"),
databases: Api.create("/mongos/{id}/databases", 'get'), databases: Api.newGet("/mongos/{id}/databases"),
collections: Api.create("/mongos/{id}/collections", 'get'), collections: Api.newGet("/mongos/{id}/collections"),
runCommand: Api.create("/mongos/{id}/run-command", 'post'), runCommand: Api.newPost("/mongos/{id}/run-command"),
findCommand: Api.create("/mongos/{id}/command/find", 'post'), findCommand: Api.newPost("/mongos/{id}/command/find"),
updateByIdCommand: Api.create("/mongos/{id}/command/update-by-id", 'post'), updateByIdCommand: Api.newPost("/mongos/{id}/command/update-by-id"),
deleteByIdCommand: Api.create("/mongos/{id}/command/delete-by-id", 'post'), deleteByIdCommand: Api.newPost("/mongos/{id}/command/delete-by-id"),
insertCommand: Api.create("/mongos/{id}/command/insert", 'post'), insertCommand: Api.newPost("/mongos/{id}/command/insert"),
} }

View File

@@ -1,26 +1,26 @@
import Api from '@/common/Api'; import Api from '@/common/Api';
export const redisApi = { export const redisApi = {
redisList : Api.create("/redis", 'get'), redisList: Api.newGet("/redis"),
getRedisPwd: Api.create("/redis/{id}/pwd", 'get'), getRedisPwd: Api.newGet("/redis/{id}/pwd"),
redisInfo: Api.create("/redis/{id}/info", 'get'), redisInfo: Api.newGet("/redis/{id}/info"),
clusterInfo: Api.create("/redis/{id}/cluster-info", 'get'), clusterInfo: Api.newGet("/redis/{id}/cluster-info"),
saveRedis: Api.create("/redis", 'post'), saveRedis: Api.newPost("/redis"),
delRedis: Api.create("/redis/{id}", 'delete'), delRedis: Api.newDelete("/redis/{id}"),
// 获取权限列表 // 获取权限列表
scan: Api.create("/redis/{id}/{db}/scan", 'post'), scan: Api.newPost("/redis/{id}/{db}/scan"),
getStringValue: Api.create("/redis/{id}/{db}/string-value", 'get'), getStringValue: Api.newGet("/redis/{id}/{db}/string-value"),
saveStringValue: Api.create("/redis/{id}/{db}/string-value", 'post'), saveStringValue: Api.newPost("/redis/{id}/{db}/string-value"),
getHashValue: Api.create("/redis/{id}/{db}/hash-value", 'get'), getHashValue: Api.newGet("/redis/{id}/{db}/hash-value"),
hscan: Api.create("/redis/{id}/{db}/hscan", 'get'), hscan: Api.newGet("/redis/{id}/{db}/hscan"),
hget: Api.create("/redis/{id}/{db}/hget", 'get'), hget: Api.newGet("/redis/{id}/{db}/hget"),
hdel: Api.create("/redis/{id}/{db}/hdel", 'delete'), hdel: Api.newDelete("/redis/{id}/{db}/hdel"),
saveHashValue: Api.create("/redis/{id}/{db}/hash-value", 'post'), saveHashValue: Api.newPost("/redis/{id}/{db}/hash-value"),
getSetValue: Api.create("/redis/{id}/{db}/set-value", 'get'), getSetValue: Api.newGet("/redis/{id}/{db}/set-value"),
saveSetValue: Api.create("/redis/{id}/{db}/set-value", 'post'), saveSetValue: Api.newPost("/redis/{id}/{db}/set-value"),
del: Api.create("/redis/{id}/{db}/scan/{cursor}/{count}", 'delete'), del: Api.newDelete("/redis/{id}/{db}/scan/{cursor}/{count}"),
delKey: Api.create("/redis/{id}/{db}/key", 'delete'), delKey: Api.newDelete("/redis/{id}/{db}/key"),
getListValue: Api.create("/redis/{id}/{db}/list-value", 'get'), getListValue: Api.newGet("/redis/{id}/{db}/list-value"),
saveListValue: Api.create("/redis/{id}/{db}/list-value", 'post'), saveListValue: Api.newPost("/redis/{id}/{db}/list-value"),
setListValue: Api.create("/redis/{id}/{db}/list-value/lset", 'post'), setListValue: Api.newPost("/redis/{id}/{db}/list-value/lset"),
} }

View File

@@ -1,20 +1,20 @@
import Api from '@/common/Api'; import Api from '@/common/Api';
export const tagApi = { export const tagApi = {
getAccountTags: Api.create("/tag-trees/account-has", 'get'), getAccountTags: Api.newGet("/tag-trees/account-has"),
listByQuery: Api.create("/tag-trees/query", 'get'), listByQuery: Api.newGet("/tag-trees/query"),
getTagTrees: Api.create("/tag-trees", 'get'), getTagTrees: Api.newGet("/tag-trees"),
saveTagTree: Api.create("/tag-trees", 'post'), saveTagTree: Api.newPost("/tag-trees"),
delTagTree: Api.create("/tag-trees/{id}", 'delete'), delTagTree: Api.newDelete("/tag-trees/{id}"),
getTeams: Api.create("/teams", 'get'), getTeams: Api.newGet("/teams"),
saveTeam: Api.create("/teams", 'post'), saveTeam: Api.newPost("/teams"),
delTeam: Api.create("/teams/{id}", 'delete'), delTeam: Api.newDelete("/teams/{id}"),
getTeamMem: Api.create("/teams/{teamId}/members", 'get'), getTeamMem: Api.newGet("/teams/{teamId}/members"),
saveTeamMem: Api.create("/teams/{teamId}/members", 'post'), saveTeamMem: Api.newPost("/teams/{teamId}/members"),
delTeamMem: Api.create("/teams/{teamId}/members/{accountId}", 'delete'), delTeamMem: Api.newDelete("/teams/{teamId}/members/{accountId}"),
getTeamTagIds: Api.create("/teams/{teamId}/tags", 'get'), getTeamTagIds: Api.newGet("/teams/{teamId}/tags"),
saveTeamTags: Api.create("/teams/{teamId}/tags", 'post'), saveTeamTags: Api.newPost("/teams/{teamId}/tags"),
} }

View File

@@ -1,8 +1,8 @@
import Api from '@/common/Api'; import Api from '@/common/Api';
export const personApi = { export const personApi = {
accountInfo: Api.create("/sys/accounts/self", 'get'), accountInfo: Api.newGet("/sys/accounts/self"),
updateAccount: Api.create("/sys/accounts/self", 'put'), updateAccount: Api.newPut("/sys/accounts/self"),
getMsgs: Api.create("/sys/accounts/msgs", 'get'), getMsgs: Api.newGet("/sys/accounts/msgs"),
} }

View File

@@ -1,43 +1,43 @@
import Api from '@/common/Api'; import Api from '@/common/Api';
export const resourceApi = { export const resourceApi = {
list: Api.create("/sys/resources", 'get'), list: Api.newGet("/sys/resources"),
detail: Api.create("/sys/resources/{id}", 'get'), detail: Api.newGet("/sys/resources/{id}"),
save: Api.create("/sys/resources", 'post'), save: Api.newPost("/sys/resources"),
update: Api.create("/sys/resources/{id}", 'put'), update: Api.newPut("/sys/resources/{id}"),
del: Api.create("/sys/resources/{id}", 'delete'), del: Api.newDelete("/sys/resources/{id}"),
changeStatus: Api.create("/sys/resources/{id}/{status}", 'put') changeStatus: Api.newPut("/sys/resources/{id}/{status}")
} }
export const roleApi = { export const roleApi = {
list: Api.create("/sys/roles", 'get'), list: Api.newGet("/sys/roles"),
save: Api.create("/sys/roles", 'post'), save: Api.newPost("/sys/roles"),
update: Api.create("/sys/roles/{id}", 'put'), update: Api.newPut("/sys/roles/{id}"),
del: Api.create("/sys/roles/{id}", 'delete'), del: Api.newDelete("/sys/roles/{id}"),
// 获取指定角色拥有的资源id // 获取指定角色拥有的资源id
roleResourceIds: Api.create("/sys/roles/{id}/resourceIds", 'get'), roleResourceIds: Api.newGet("/sys/roles/{id}/resourceIds"),
roleResources: Api.create("/sys/roles/{id}/resources", 'get'), roleResources: Api.newGet("/sys/roles/{id}/resources"),
saveResources: Api.create("/sys/roles/{id}/resources", 'post') saveResources: Api.newPost("/sys/roles/{id}/resources")
} }
export const accountApi = { export const accountApi = {
list: Api.create("/sys/accounts", 'get'), list: Api.newGet("/sys/accounts"),
save: Api.create("/sys/accounts", 'post'), save: Api.newPost("/sys/accounts"),
update: Api.create("/sys/accounts/{id}", 'put'), update: Api.newPut("/sys/accounts/{id}"),
del: Api.create("/sys/accounts/{id}", 'delete'), del: Api.newDelete("/sys/accounts/{id}"),
changeStatus: Api.create("/sys/accounts/change-status/{id}/{status}", 'put'), changeStatus: Api.newPut("/sys/accounts/change-status/{id}/{status}"),
roleIds: Api.create("/sys/accounts/{id}/roleIds", 'get'), roleIds: Api.newGet("/sys/accounts/{id}/roleIds"),
roles: Api.create("/sys/accounts/{id}/roles", 'get'), roles: Api.newGet("/sys/accounts/{id}/roles"),
resources: Api.create("/sys/accounts/{id}/resources", 'get'), resources: Api.newGet("/sys/accounts/{id}/resources"),
saveRoles: Api.create("/sys/accounts/roles", 'post') saveRoles: Api.newPost("/sys/accounts/roles")
} }
export const configApi = { export const configApi = {
list: Api.create("/sys/configs", 'get'), list: Api.newGet("/sys/configs"),
save: Api.create("/sys/configs", 'post'), save: Api.newPost("/sys/configs"),
getValue: Api.create("/sys/configs/value", 'get'), getValue: Api.newGet("/sys/configs/value"),
} }
export const logApi = { export const logApi = {
list: Api.create("/syslogs", "get") list: Api.newGet("/syslogs")
} }

View File

@@ -18,8 +18,8 @@ require (
golang.org/x/crypto v0.8.0 // ssh golang.org/x/crypto v0.8.0 // ssh
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
// gorm // gorm
gorm.io/driver/mysql v1.4.7 gorm.io/driver/mysql v1.5.0
gorm.io/gorm v1.24.6 gorm.io/gorm v1.25.0
) )
require ( require (

View File

@@ -39,6 +39,10 @@ func (a *Account) Login(rc *req.Ctx) {
biz.IsTrue(captcha.Verify(loginForm.Cid, loginForm.Captcha), "验证码错误") biz.IsTrue(captcha.Verify(loginForm.Cid, loginForm.Captcha), "验证码错误")
} }
clientIp := rc.GinCtx.ClientIP()
rc.ReqParam = loginForm.Username
rc.ReqParam = fmt.Sprintf("username: %s | ip: %s", loginForm.Username, clientIp)
originPwd, err := utils.DefaultRsaDecrypt(loginForm.Password, true) originPwd, err := utils.DefaultRsaDecrypt(loginForm.Password, true)
biz.ErrIsNilAppendErr(err, "解密密码错误: %s") biz.ErrIsNilAppendErr(err, "解密密码错误: %s")
@@ -50,6 +54,20 @@ func (a *Account) Login(rc *req.Ctx) {
// 校验密码强度是否符合 // 校验密码强度是否符合
biz.IsTrueBy(CheckPasswordLever(originPwd), biz.NewBizErrCode(401, "您的密码安全等级较低,请修改后重新登录")) biz.IsTrueBy(CheckPasswordLever(originPwd), biz.NewBizErrCode(401, "您的密码安全等级较低,请修改后重新登录"))
// 保存登录消息
go a.saveLogin(account, clientIp)
rc.ResData = map[string]interface{}{
"token": req.CreateToken(account.Id, account.Username),
"name": account.Name,
"username": account.Username,
"lastLoginTime": account.LastLoginTime,
"lastLoginIp": account.LastLoginIp,
}
}
func (a *Account) GetPermissions(rc *req.Ctx) {
account := rc.LoginAccount
var resources vo.AccountResourceVOList var resources vo.AccountResourceVOList
// 获取账号菜单资源 // 获取账号菜单资源
@@ -66,23 +84,9 @@ func (a *Account) Login(rc *req.Ctx) {
} }
// 保存该账号的权限codes // 保存该账号的权限codes
req.SavePermissionCodes(account.Id, permissions) req.SavePermissionCodes(account.Id, permissions)
clientIp := rc.GinCtx.ClientIP()
// 保存登录消息
go a.saveLogin(account, clientIp)
rc.ReqParam = fmt.Sprintf("登录ip: %s", clientIp)
// 赋值loginAccount 主要用于记录操作日志,因为操作日志保存请求上下文没有该信息不保存日志
rc.LoginAccount = &model.LoginAccount{Id: account.Id, Username: account.Username}
rc.ResData = map[string]interface{}{ rc.ResData = map[string]interface{}{
"token": req.CreateToken(account.Id, account.Username), "menus": menus.ToTrees(0),
"name": account.Name, "permissions": permissions,
"username": account.Username,
"lastLoginTime": account.LastLoginTime,
"lastLoginIp": account.LastLoginIp,
"menus": menus.ToTrees(0),
"permissions": permissions,
} }
} }
@@ -143,7 +147,7 @@ func (a *Account) saveLogin(account *entity.Account, ip string) {
// 创建登录消息 // 创建登录消息
loginMsg := &entity.Msg{ loginMsg := &entity.Msg{
RecipientId: int64(account.Id), RecipientId: int64(account.Id),
Msg: fmt.Sprintf("于%s登录", now.Format("2006-01-02 15:04:05")), Msg: fmt.Sprintf("于[%s]-[%s]登录", ip, now.Format("2006-01-02 15:04:05")),
Type: 1, Type: 1,
} }
loginMsg.CreateTime = &now loginMsg.CreateTime = &now

View File

@@ -27,6 +27,11 @@ func InitAccountRouter(router *gin.RouterGroup) {
Handle(a.Login) Handle(a.Login)
}) })
// 获取个人账号的权限资源信息
account.GET("/permissions", func(c *gin.Context) {
req.NewCtxWithGin(c).Handle(a.GetPermissions)
})
changePwdLog := req.NewLogInfo("用户修改密码").WithSave(true) changePwdLog := req.NewLogInfo("用户修改密码").WithSave(true)
account.POST("change-pwd", func(g *gin.Context) { account.POST("change-pwd", func(g *gin.Context) {
req.NewCtxWithGin(g). req.NewCtxWithGin(g).