mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 00:10:25 +08:00 
			
		
		
		
	refactor: 引入dayjs、新增refreshToken无感刷新、团队新增有效期、数据库等问题修复
This commit is contained in:
		@@ -15,6 +15,7 @@
 | 
				
			|||||||
    "axios": "^1.6.2",
 | 
					    "axios": "^1.6.2",
 | 
				
			||||||
    "clipboard": "^2.0.11",
 | 
					    "clipboard": "^2.0.11",
 | 
				
			||||||
    "cropperjs": "^1.6.1",
 | 
					    "cropperjs": "^1.6.1",
 | 
				
			||||||
 | 
					    "dayjs": "^1.11.11",
 | 
				
			||||||
    "echarts": "^5.5.0",
 | 
					    "echarts": "^5.5.0",
 | 
				
			||||||
    "element-plus": "^2.7.2",
 | 
					    "element-plus": "^2.7.2",
 | 
				
			||||||
    "js-base64": "^3.7.7",
 | 
					    "js-base64": "^3.7.7",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ const config = {
 | 
				
			|||||||
    baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
 | 
					    baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 系统版本
 | 
					    // 系统版本
 | 
				
			||||||
    version: 'v1.8.3',
 | 
					    version: 'v1.8.4',
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default config;
 | 
					export default config;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ import request from './request';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    login: (param: any) => request.post('/auth/accounts/login', param),
 | 
					    login: (param: any) => request.post('/auth/accounts/login', param),
 | 
				
			||||||
 | 
					    refreshToken: (param: any) => request.get('/auth/accounts/refreshToken', param),
 | 
				
			||||||
    otpVerify: (param: any) => request.post('/auth/accounts/otp-verify', param),
 | 
					    otpVerify: (param: any) => request.post('/auth/accounts/otp-verify', param),
 | 
				
			||||||
    getPublicKey: () => request.get('/common/public-key'),
 | 
					    getPublicKey: () => request.get('/common/public-key'),
 | 
				
			||||||
    getConfigValue: (params: any) => request.get('/sys/configs/value', params),
 | 
					    getConfigValue: (params: any) => request.get('/sys/configs/value', params),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,6 +38,7 @@ export enum ResultEnum {
 | 
				
			|||||||
    PARAM_ERROR = 405,
 | 
					    PARAM_ERROR = 405,
 | 
				
			||||||
    SERVER_ERROR = 500,
 | 
					    SERVER_ERROR = 500,
 | 
				
			||||||
    NO_PERMISSION = 501,
 | 
					    NO_PERMISSION = 501,
 | 
				
			||||||
 | 
					    ACCESS_TOKEN_INVALID = 502, // accessToken失效
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const baseUrl: string = config.baseApiUrl;
 | 
					export const baseUrl: string = config.baseApiUrl;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,31 +0,0 @@
 | 
				
			|||||||
export function dateFormat2(fmt: string, date: Date) {
 | 
					 | 
				
			||||||
    let ret;
 | 
					 | 
				
			||||||
    const opt = {
 | 
					 | 
				
			||||||
        'y+': date.getFullYear().toString(), // 年
 | 
					 | 
				
			||||||
        'M+': (date.getMonth() + 1).toString(), // 月
 | 
					 | 
				
			||||||
        'd+': date.getDate().toString(), // 日
 | 
					 | 
				
			||||||
        'H+': date.getHours().toString(), // 时
 | 
					 | 
				
			||||||
        'm+': date.getMinutes().toString(), // 分
 | 
					 | 
				
			||||||
        's+': date.getSeconds().toString(), // 秒
 | 
					 | 
				
			||||||
        'S+': date.getMilliseconds() ? date.getMilliseconds().toString() : '', // 毫秒
 | 
					 | 
				
			||||||
        // 有其他格式化字符需求可以继续添加,必须转化成字符串
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    for (const k in opt) {
 | 
					 | 
				
			||||||
        ret = new RegExp('(' + k + ')').exec(fmt);
 | 
					 | 
				
			||||||
        if (ret) {
 | 
					 | 
				
			||||||
            fmt = fmt.replace(ret[1], ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, '0'));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return fmt;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function dateStrFormat(fmt: string, dateStr: string) {
 | 
					 | 
				
			||||||
    return dateFormat2(fmt, new Date(dateStr));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function dateFormat(dateStr: string) {
 | 
					 | 
				
			||||||
    if (!dateStr) {
 | 
					 | 
				
			||||||
        return '';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return dateFormat2('yyyy-MM-dd HH:mm:ss', new Date(dateStr));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,3 +1,18 @@
 | 
				
			|||||||
 | 
					import dayjs from 'dayjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 格式化日期
 | 
				
			||||||
 | 
					 * @param date 日期 字符串 Date 时间戳等
 | 
				
			||||||
 | 
					 * @param format 格式化格式  默认 YYYY-MM-DD HH:mm:ss
 | 
				
			||||||
 | 
					 * @returns 格式化后内容
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function formatDate(date: any, format: string = 'YYYY-MM-DD HH:mm:ss') {
 | 
				
			||||||
 | 
					    if (!date) {
 | 
				
			||||||
 | 
					        return '';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return dayjs(date).format(format);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 格式化字节单位
 | 
					 * 格式化字节单位
 | 
				
			||||||
 * @param size byte size
 | 
					 * @param size byte size
 | 
				
			||||||
@@ -46,110 +61,6 @@ export function convertToBytes(sizeStr: string) {
 | 
				
			|||||||
    return bytes;
 | 
					    return bytes;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * 年(Y) 可用1-4个占位符
 | 
					 | 
				
			||||||
 * 月(m)、日(d)、小时(H)、分(M)、秒(S) 可用1-2个占位符
 | 
					 | 
				
			||||||
 * 星期(W) 可用1-3个占位符
 | 
					 | 
				
			||||||
 * 季度(q为阿拉伯数字,Q为中文数字)可用1或4个占位符
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * let date = new Date()
 | 
					 | 
				
			||||||
 * formatDate(date, "YYYY-mm-dd HH:MM:SS")           // 2020-02-09 14:04:23
 | 
					 | 
				
			||||||
 * formatDate(date, "YYYY-mm-dd HH:MM:SS Q")         // 2020-02-09 14:09:03 一
 | 
					 | 
				
			||||||
 * formatDate(date, "YYYY-mm-dd HH:MM:SS WWW")       // 2020-02-09 14:45:12 星期日
 | 
					 | 
				
			||||||
 * formatDate(date, "YYYY-mm-dd HH:MM:SS QQQQ")      // 2020-02-09 14:09:36 第一季度
 | 
					 | 
				
			||||||
 * formatDate(date, "YYYY-mm-dd HH:MM:SS WWW QQQQ")  // 2020-02-09 14:46:12 星期日 第一季度
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export function formatDate(date: Date, format: string) {
 | 
					 | 
				
			||||||
    let we = date.getDay(); // 星期
 | 
					 | 
				
			||||||
    let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // 季度
 | 
					 | 
				
			||||||
    const opt: any = {
 | 
					 | 
				
			||||||
        'Y+': date.getFullYear().toString(), // 年
 | 
					 | 
				
			||||||
        'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
 | 
					 | 
				
			||||||
        'd+': date.getDate().toString(), // 日
 | 
					 | 
				
			||||||
        'H+': date.getHours().toString(), // 时
 | 
					 | 
				
			||||||
        'M+': date.getMinutes().toString(), // 分
 | 
					 | 
				
			||||||
        'S+': date.getSeconds().toString(), // 秒
 | 
					 | 
				
			||||||
        'q+': qut, // 季度
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    // 中文数字 (星期)
 | 
					 | 
				
			||||||
    const week: any = {
 | 
					 | 
				
			||||||
        '0': '日',
 | 
					 | 
				
			||||||
        '1': '一',
 | 
					 | 
				
			||||||
        '2': '二',
 | 
					 | 
				
			||||||
        '3': '三',
 | 
					 | 
				
			||||||
        '4': '四',
 | 
					 | 
				
			||||||
        '5': '五',
 | 
					 | 
				
			||||||
        '6': '六',
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    // 中文数字(季度)
 | 
					 | 
				
			||||||
    const quarter: any = {
 | 
					 | 
				
			||||||
        '1': '一',
 | 
					 | 
				
			||||||
        '2': '二',
 | 
					 | 
				
			||||||
        '3': '三',
 | 
					 | 
				
			||||||
        '4': '四',
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    if (/(W+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]);
 | 
					 | 
				
			||||||
    if (/(Q+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]);
 | 
					 | 
				
			||||||
    for (let k in opt) {
 | 
					 | 
				
			||||||
        let r = new RegExp('(' + k + ')').exec(format);
 | 
					 | 
				
			||||||
        // 若输入的长度不为1,则前面补零
 | 
					 | 
				
			||||||
        if (r) format = format.replace(r[1], RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0'));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return format;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 10秒:  10 * 1000
 | 
					 | 
				
			||||||
 * 1分:   60 * 1000
 | 
					 | 
				
			||||||
 * 1小时: 60 * 60 * 1000
 | 
					 | 
				
			||||||
 * 24小时:60 * 60 * 24 * 1000
 | 
					 | 
				
			||||||
 * 3天:   60 * 60* 24 * 1000 * 3
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * let data = new Date()
 | 
					 | 
				
			||||||
 * formatPast(data)                                           // 刚刚
 | 
					 | 
				
			||||||
 * formatPast(data - 11 * 1000)                               // 11秒前
 | 
					 | 
				
			||||||
 * formatPast(data - 2 * 60 * 1000)                           // 2分钟前
 | 
					 | 
				
			||||||
 * formatPast(data - 60 * 60 * 2 * 1000)                      // 2小时前
 | 
					 | 
				
			||||||
 * formatPast(data - 60 * 60 * 2 * 1000)                      // 2小时前
 | 
					 | 
				
			||||||
 * formatPast(data - 60 * 60 * 71 * 1000)                     // 2天前
 | 
					 | 
				
			||||||
 * formatPast("2020-06-01")                                   // 2020-06-01
 | 
					 | 
				
			||||||
 * formatPast("2020-06-01", "YYYY-mm-dd HH:MM:SS WWW QQQQ")   // 2020-06-01 08:00:00 星期一 第二季度
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export function formatPast(param: any, format: string = 'YYYY-mm-dd') {
 | 
					 | 
				
			||||||
    // 传入格式处理、存储转换值
 | 
					 | 
				
			||||||
    let t: any, s: any;
 | 
					 | 
				
			||||||
    // 获取js 时间戳
 | 
					 | 
				
			||||||
    let time: any = new Date().getTime();
 | 
					 | 
				
			||||||
    // 是否是对象
 | 
					 | 
				
			||||||
    typeof param === 'string' || 'object' ? (t = new Date(param).getTime()) : (t = param);
 | 
					 | 
				
			||||||
    // 当前时间戳 - 传入时间戳
 | 
					 | 
				
			||||||
    time = Number.parseInt(`${time - t}`);
 | 
					 | 
				
			||||||
    if (time < 10000) {
 | 
					 | 
				
			||||||
        // 10秒内
 | 
					 | 
				
			||||||
        return '刚刚';
 | 
					 | 
				
			||||||
    } else if (time < 60000 && time >= 10000) {
 | 
					 | 
				
			||||||
        // 超过10秒少于1分钟内
 | 
					 | 
				
			||||||
        s = Math.floor(time / 1000);
 | 
					 | 
				
			||||||
        return `${s}秒前`;
 | 
					 | 
				
			||||||
    } else if (time < 3600000 && time >= 60000) {
 | 
					 | 
				
			||||||
        // 超过1分钟少于1小时
 | 
					 | 
				
			||||||
        s = Math.floor(time / 60000);
 | 
					 | 
				
			||||||
        return `${s}分钟前`;
 | 
					 | 
				
			||||||
    } else if (time < 86400000 && time >= 3600000) {
 | 
					 | 
				
			||||||
        // 超过1小时少于24小时
 | 
					 | 
				
			||||||
        s = Math.floor(time / 3600000);
 | 
					 | 
				
			||||||
        return `${s}小时前`;
 | 
					 | 
				
			||||||
    } else if (time < 259200000 && time >= 86400000) {
 | 
					 | 
				
			||||||
        // 超过1天少于3天内
 | 
					 | 
				
			||||||
        s = Math.floor(time / 86400000);
 | 
					 | 
				
			||||||
        return `${s}天前`;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        // 超过3天
 | 
					 | 
				
			||||||
        let date = typeof param === 'string' || 'object' ? new Date(param) : param;
 | 
					 | 
				
			||||||
        return formatDate(date, format);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 格式化指定时间数为人性化可阅读的内容(默认time为秒单位)
 | 
					 * 格式化指定时间数为人性化可阅读的内容(默认time为秒单位)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import { randomUuid } from './string';
 | 
					import { randomUuid } from './string';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TokenKey = 'm-token';
 | 
					const TokenKey = 'm-token';
 | 
				
			||||||
 | 
					const RefreshTokenKey = 'm-refresh-token';
 | 
				
			||||||
const UserKey = 'm-user';
 | 
					const UserKey = 'm-user';
 | 
				
			||||||
const TagViewsKey = 'm-tagViews';
 | 
					const TagViewsKey = 'm-tagViews';
 | 
				
			||||||
const ClientIdKey = 'm-clientId';
 | 
					const ClientIdKey = 'm-clientId';
 | 
				
			||||||
@@ -15,6 +16,14 @@ export function saveToken(token: string) {
 | 
				
			|||||||
    setLocal(TokenKey, token);
 | 
					    setLocal(TokenKey, token);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function getRefreshToken(): string {
 | 
				
			||||||
 | 
					    return getLocal(RefreshTokenKey);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function saveRefreshToken(refreshToken: string) {
 | 
				
			||||||
 | 
					    return setLocal(RefreshTokenKey, refreshToken);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取登录用户基础信息
 | 
					// 获取登录用户基础信息
 | 
				
			||||||
export function getUser() {
 | 
					export function getUser() {
 | 
				
			||||||
    return getLocal(UserKey);
 | 
					    return getLocal(UserKey);
 | 
				
			||||||
@@ -39,6 +48,7 @@ export function getThemeConfig() {
 | 
				
			|||||||
export function clearUser() {
 | 
					export function clearUser() {
 | 
				
			||||||
    removeLocal(TokenKey);
 | 
					    removeLocal(TokenKey);
 | 
				
			||||||
    removeLocal(UserKey);
 | 
					    removeLocal(UserKey);
 | 
				
			||||||
 | 
					    removeLocal(RefreshTokenKey);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getTagViews() {
 | 
					export function getTagViews() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import EnumValue from '@/common/Enum';
 | 
					import EnumValue from '@/common/Enum';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import { getValueByPath } from '@/common/utils/object';
 | 
					import { getValueByPath } from '@/common/utils/object';
 | 
				
			||||||
import { getTextWidth } from '@/common/utils/string';
 | 
					import { getTextWidth } from '@/common/utils/string';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -172,7 +172,7 @@ export class TableColumn {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    isTime(): TableColumn {
 | 
					    isTime(): TableColumn {
 | 
				
			||||||
        this.setFormatFunc((data: any, prop: string) => {
 | 
					        this.setFormatFunc((data: any, prop: string) => {
 | 
				
			||||||
            return dateFormat(getValueByPath(data, prop));
 | 
					            return formatDate(getValueByPath(data, prop));
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,8 +24,33 @@
 | 
				
			|||||||
                <SvgIcon name="Refresh" @click="connect(0, 0)" :size="20" class="pointer-icon mr10" title="重新连接" />
 | 
					                <SvgIcon name="Refresh" @click="connect(0, 0)" :size="20" class="pointer-icon mr10" title="重新连接" />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <clipboard-dialog ref="clipboardRef" v-model:visible="state.clipboardDialog.visible" @close="closePaste" @submit="onsubmitClipboard" />
 | 
					            <clipboard-dialog ref="clipboardRef" v-model:visible="state.clipboardDialog.visible" @close="closePaste" @submit="onsubmitClipboard" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-dialog
 | 
				
			||||||
 | 
					                v-if="!state.fullscreen"
 | 
				
			||||||
 | 
					                destroy-on-close
 | 
				
			||||||
 | 
					                :title="state.filesystemDialog.title"
 | 
				
			||||||
 | 
					                v-model="state.filesystemDialog.visible"
 | 
				
			||||||
 | 
					                :close-on-click-modal="false"
 | 
				
			||||||
 | 
					                width="70%"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <machine-file
 | 
				
			||||||
 | 
					                    :machine-id="state.filesystemDialog.machineId"
 | 
				
			||||||
 | 
					                    :auth-cert-name="state.filesystemDialog.authCertName"
 | 
				
			||||||
 | 
					                    :protocol="state.filesystemDialog.protocol"
 | 
				
			||||||
 | 
					                    :file-id="state.filesystemDialog.fileId"
 | 
				
			||||||
 | 
					                    :path="state.filesystemDialog.path"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					            </el-dialog>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <el-dialog destroy-on-close :title="state.filesystemDialog.title" v-model="state.filesystemDialog.visible" :close-on-click-modal="false" width="70%">
 | 
					
 | 
				
			||||||
 | 
					        <el-dialog
 | 
				
			||||||
 | 
					            v-if="!state.fullscreen"
 | 
				
			||||||
 | 
					            destroy-on-close
 | 
				
			||||||
 | 
					            :title="state.filesystemDialog.title"
 | 
				
			||||||
 | 
					            v-model="state.filesystemDialog.visible"
 | 
				
			||||||
 | 
					            :close-on-click-modal="false"
 | 
				
			||||||
 | 
					            width="70%"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
            <machine-file
 | 
					            <machine-file
 | 
				
			||||||
                :machine-id="state.filesystemDialog.machineId"
 | 
					                :machine-id="state.filesystemDialog.machineId"
 | 
				
			||||||
                :auth-cert-name="state.filesystemDialog.authCertName"
 | 
					                :auth-cert-name="state.filesystemDialog.authCertName"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import router from '@/router';
 | 
					import router from '@/router';
 | 
				
			||||||
import { getClientId, getToken } from '@/common/utils/storage';
 | 
					import { clearUser, getClientId, getRefreshToken, getToken, saveRefreshToken, saveToken } from '@/common/utils/storage';
 | 
				
			||||||
import { templateResolve } from '@/common/utils/string';
 | 
					import { templateResolve } from '@/common/utils/string';
 | 
				
			||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
import { createFetch } from '@vueuse/core';
 | 
					import { createFetch } from '@vueuse/core';
 | 
				
			||||||
@@ -8,6 +8,7 @@ import { Result, ResultEnum } from '@/common/request';
 | 
				
			|||||||
import config from '@/common/config';
 | 
					import config from '@/common/config';
 | 
				
			||||||
import { unref } from 'vue';
 | 
					import { unref } from 'vue';
 | 
				
			||||||
import { URL_401 } from '@/router/staticRouter';
 | 
					import { URL_401 } from '@/router/staticRouter';
 | 
				
			||||||
 | 
					import openApi from '@/common/openApi';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const baseUrl: string = config.baseApiUrl;
 | 
					const baseUrl: string = config.baseApiUrl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -88,61 +89,104 @@ export function useApiFetch<T>(api: Api, params: any = null, reqOptions: Request
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
        execute: async function () {
 | 
					        execute: async function () {
 | 
				
			||||||
            try {
 | 
					            return execUaf(uaf);
 | 
				
			||||||
                await uaf.execute(true);
 | 
					 | 
				
			||||||
            } catch (e: any) {
 | 
					 | 
				
			||||||
                const rejectPromise = Promise.reject(e);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (e?.name == 'AbortError') {
 | 
					 | 
				
			||||||
                    console.log('请求已取消');
 | 
					 | 
				
			||||||
                    return rejectPromise;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                const respStatus = uaf.response.value?.status;
 | 
					 | 
				
			||||||
                if (respStatus == 404) {
 | 
					 | 
				
			||||||
                    ElMessage.error('请求接口不存在');
 | 
					 | 
				
			||||||
                    return rejectPromise;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (respStatus == 500) {
 | 
					 | 
				
			||||||
                    ElMessage.error('服务器响应异常');
 | 
					 | 
				
			||||||
                    return rejectPromise;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                console.error(e);
 | 
					 | 
				
			||||||
                ElMessage.error('网络请求错误');
 | 
					 | 
				
			||||||
                return rejectPromise;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const result: Result = uaf.data.value as any;
 | 
					 | 
				
			||||||
            if (!result) {
 | 
					 | 
				
			||||||
                ElMessage.error('网络请求失败');
 | 
					 | 
				
			||||||
                return Promise.reject(result);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 如果返回为成功结果,则将结果的data赋值给响应式data
 | 
					 | 
				
			||||||
            if (result.code === ResultEnum.SUCCESS) {
 | 
					 | 
				
			||||||
                uaf.data.value = result.data;
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 如果提示没有权限,则跳转至无权限页面
 | 
					 | 
				
			||||||
            if (result.code === ResultEnum.NO_PERMISSION) {
 | 
					 | 
				
			||||||
                router.push({
 | 
					 | 
				
			||||||
                    path: URL_401,
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
                return Promise.reject(result);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // 如果返回的code不为成功,则会返回对应的错误msg,则直接统一通知即可。忽略登录超时或没有权限的提示(直接跳转至401页面)
 | 
					 | 
				
			||||||
            if (result.msg && result?.code != ResultEnum.NO_PERMISSION) {
 | 
					 | 
				
			||||||
                ElMessage.error(result.msg);
 | 
					 | 
				
			||||||
                uaf.error.value = new Error(result.msg);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return Promise.reject(result);
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        isFetching: uaf.isFetching,
 | 
					        isFetching: uaf.isFetching,
 | 
				
			||||||
        data: uaf.data,
 | 
					        data: uaf.data,
 | 
				
			||||||
        abort: uaf.abort,
 | 
					        abort: uaf.abort,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let refreshingToken = false;
 | 
				
			||||||
 | 
					let queue: any[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function execUaf(uaf: any) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        await uaf.execute(true);
 | 
				
			||||||
 | 
					    } catch (e: any) {
 | 
				
			||||||
 | 
					        const rejectPromise = Promise.reject(e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (e?.name == 'AbortError') {
 | 
				
			||||||
 | 
					            console.log('请求已取消');
 | 
				
			||||||
 | 
					            return rejectPromise;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const respStatus = uaf.response.value?.status;
 | 
				
			||||||
 | 
					        if (respStatus == 404) {
 | 
				
			||||||
 | 
					            ElMessage.error('请求接口不存在');
 | 
				
			||||||
 | 
					            return rejectPromise;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (respStatus == 500) {
 | 
				
			||||||
 | 
					            ElMessage.error('服务器响应异常');
 | 
				
			||||||
 | 
					            return rejectPromise;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        console.error(e);
 | 
				
			||||||
 | 
					        ElMessage.error('网络请求错误');
 | 
				
			||||||
 | 
					        return rejectPromise;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const result: Result = uaf.data.value as any;
 | 
				
			||||||
 | 
					    if (!result) {
 | 
				
			||||||
 | 
					        ElMessage.error('网络请求失败');
 | 
				
			||||||
 | 
					        return Promise.reject(result);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const resultCode = result.code;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 如果返回为成功结果,则将结果的data赋值给响应式data
 | 
				
			||||||
 | 
					    if (resultCode === ResultEnum.SUCCESS) {
 | 
				
			||||||
 | 
					        uaf.data.value = result.data;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 如果是accessToken失效,则使用refreshToken刷新token
 | 
				
			||||||
 | 
					    if (resultCode == ResultEnum.ACCESS_TOKEN_INVALID) {
 | 
				
			||||||
 | 
					        if (refreshingToken) {
 | 
				
			||||||
 | 
					            // 请求加入队列等待, 防止并发多次请求refreshToken
 | 
				
			||||||
 | 
					            return new Promise((resolve) => {
 | 
				
			||||||
 | 
					                queue.push(() => {
 | 
				
			||||||
 | 
					                    resolve(execUaf(uaf));
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            refreshingToken = true;
 | 
				
			||||||
 | 
					            const res = await openApi.refreshToken({ refresh_token: getRefreshToken() });
 | 
				
			||||||
 | 
					            saveToken(res.token);
 | 
				
			||||||
 | 
					            saveRefreshToken(res.refresh_token);
 | 
				
			||||||
 | 
					            // 重新缓存后端用户权限code
 | 
				
			||||||
 | 
					            await openApi.getPermissions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 执行accessToken失效的请求
 | 
				
			||||||
 | 
					            queue.forEach((resolve: any) => {
 | 
				
			||||||
 | 
					                resolve();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } catch (e: any) {
 | 
				
			||||||
 | 
					            clearUser();
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            refreshingToken = false;
 | 
				
			||||||
 | 
					            queue = [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await execUaf(uaf);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 如果提示没有权限,则跳转至无权限页面
 | 
				
			||||||
 | 
					    if (resultCode === ResultEnum.NO_PERMISSION) {
 | 
				
			||||||
 | 
					        router.push({
 | 
				
			||||||
 | 
					            path: URL_401,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return Promise.reject(result);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 如果返回的code不为成功,则会返回对应的错误msg,则直接统一通知即可。忽略登录超时或没有权限的提示(直接跳转至401页面)
 | 
				
			||||||
 | 
					    if (result.msg && resultCode != ResultEnum.NO_PERMISSION) {
 | 
				
			||||||
 | 
					        ElMessage.error(result.msg);
 | 
				
			||||||
 | 
					        uaf.error.value = new Error(result.msg);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Promise.reject(result);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import { defineStore } from 'pinia';
 | 
					import { defineStore } from 'pinia';
 | 
				
			||||||
import { dateFormat2 } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import { useUserInfo } from '@/store/userInfo';
 | 
					import { useUserInfo } from '@/store/userInfo';
 | 
				
			||||||
import { getSysStyleConfig } from '@/common/sysconfig';
 | 
					import { getSysStyleConfig } from '@/common/sysconfig';
 | 
				
			||||||
import { getLocal, getThemeConfig } from '@/common/utils/storage';
 | 
					import { getLocal, getThemeConfig } from '@/common/utils/storage';
 | 
				
			||||||
@@ -191,7 +191,7 @@ export const useThemeConfig = defineStore('themeConfig', {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        // 设置水印时间为当前时间
 | 
					        // 设置水印时间为当前时间
 | 
				
			||||||
        setWatermarkNowTime() {
 | 
					        setWatermarkNowTime() {
 | 
				
			||||||
            this.themeConfig.watermarkText[1] = dateFormat2('yyyy-MM-dd HH:mm:ss', new Date());
 | 
					            this.themeConfig.watermarkText[1] = formatDate(new Date());
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        // 切换暗黑模式
 | 
					        // 切换暗黑模式
 | 
				
			||||||
        switchDark(isDark: boolean) {
 | 
					        switchDark(isDark: boolean) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,11 +17,11 @@
 | 
				
			|||||||
                        <AccountInfo :account-id="procinst.creatorId" :username="procinst.creator" />
 | 
					                        <AccountInfo :account-id="procinst.creatorId" :username="procinst.creator" />
 | 
				
			||||||
                        <!-- {{ procinst.creator }} -->
 | 
					                        <!-- {{ procinst.creator }} -->
 | 
				
			||||||
                    </el-descriptions-item>
 | 
					                    </el-descriptions-item>
 | 
				
			||||||
                    <el-descriptions-item label="发起时间">{{ dateFormat(procinst.createTime) }}</el-descriptions-item>
 | 
					                    <el-descriptions-item label="发起时间">{{ formatDate(procinst.createTime) }}</el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <div v-if="procinst.duration">
 | 
					                    <div v-if="procinst.duration">
 | 
				
			||||||
                        <el-descriptions-item label="持续时间">{{ formatTime(procinst.duration) }}</el-descriptions-item>
 | 
					                        <el-descriptions-item label="持续时间">{{ formatTime(procinst.duration) }}</el-descriptions-item>
 | 
				
			||||||
                        <el-descriptions-item label="结束时间">{{ dateFormat(procinst.endTime) }}</el-descriptions-item>
 | 
					                        <el-descriptions-item label="结束时间">{{ formatDate(procinst.endTime) }}</el-descriptions-item>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <el-descriptions-item label="流程状态">
 | 
					                    <el-descriptions-item label="流程状态">
 | 
				
			||||||
@@ -86,11 +86,11 @@ import { procinstApi } from './api';
 | 
				
			|||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
 | 
					import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
 | 
				
			||||||
import { FlowBizType, ProcinstBizStatus, ProcinstTaskStatus, ProcinstStatus } from './enums';
 | 
					import { FlowBizType, ProcinstBizStatus, ProcinstTaskStatus, ProcinstStatus } from './enums';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					 | 
				
			||||||
import ProcdefTasks from './components/ProcdefTasks.vue';
 | 
					import ProcdefTasks from './components/ProcdefTasks.vue';
 | 
				
			||||||
import { formatTime } from '@/common/utils/format';
 | 
					import { formatTime } from '@/common/utils/format';
 | 
				
			||||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
 | 
					import EnumTag from '@/components/enumtag/EnumTag.vue';
 | 
				
			||||||
import AccountInfo from '@/views/system/account/components/AccountInfo.vue';
 | 
					import AccountInfo from '@/views/system/account/components/AccountInfo.vue';
 | 
				
			||||||
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DbSqlExecBiz = defineAsyncComponent(() => import('./flowbiz/DbSqlExecBiz.vue'));
 | 
					const DbSqlExecBiz = defineAsyncComponent(() => import('./flowbiz/DbSqlExecBiz.vue'));
 | 
				
			||||||
const RedisRunWriteCmdBiz = defineAsyncComponent(() => import('./flowbiz/RedisRunWriteCmdBiz.vue'));
 | 
					const RedisRunWriteCmdBiz = defineAsyncComponent(() => import('./flowbiz/RedisRunWriteCmdBiz.vue'));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
        <el-step v-for="task in tasksArr" :status="getStepStatus(task)" :title="task.name" :key="task.taskKey">
 | 
					        <el-step v-for="task in tasksArr" :status="getStepStatus(task)" :title="task.name" :key="task.taskKey">
 | 
				
			||||||
            <template #description>
 | 
					            <template #description>
 | 
				
			||||||
                <div>{{ `${task.accountUsername}(${task.accountName})` }}</div>
 | 
					                <div>{{ `${task.accountUsername}(${task.accountName})` }}</div>
 | 
				
			||||||
                <div v-if="task.completeTime">{{ `${dateFormat(task.completeTime)}` }}</div>
 | 
					                <div v-if="task.completeTime">{{ `${formatDate(task.completeTime)}` }}</div>
 | 
				
			||||||
                <div v-if="task.remark">{{ task.remark }}</div>
 | 
					                <div v-if="task.remark">{{ task.remark }}</div>
 | 
				
			||||||
            </template>
 | 
					            </template>
 | 
				
			||||||
        </el-step>
 | 
					        </el-step>
 | 
				
			||||||
@@ -14,7 +14,7 @@
 | 
				
			|||||||
import { toRefs, reactive, watch, onMounted } from 'vue';
 | 
					import { toRefs, reactive, watch, onMounted } from 'vue';
 | 
				
			||||||
import { accountApi } from '../../system/api';
 | 
					import { accountApi } from '../../system/api';
 | 
				
			||||||
import { ProcinstTaskStatus } from '../enums';
 | 
					import { ProcinstTaskStatus } from '../enums';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import { ElSteps, ElStep } from 'element-plus';
 | 
					import { ElSteps, ElStep } from 'element-plus';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps({
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@
 | 
				
			|||||||
                                        </el-col>
 | 
					                                        </el-col>
 | 
				
			||||||
                                        <el-col :xs="24" :sm="12" class="personal-item mb6">
 | 
					                                        <el-col :xs="24" :sm="12" class="personal-item mb6">
 | 
				
			||||||
                                            <div class="personal-item-label">上次登录时间:</div>
 | 
					                                            <div class="personal-item-label">上次登录时间:</div>
 | 
				
			||||||
                                            <div class="personal-item-value">{{ dateFormat(userInfo.lastLoginTime) }}</div>
 | 
					                                            <div class="personal-item-value">{{ formatDate(userInfo.lastLoginTime) }}</div>
 | 
				
			||||||
                                        </el-col>
 | 
					                                        </el-col>
 | 
				
			||||||
                                    </el-row>
 | 
					                                    </el-row>
 | 
				
			||||||
                                </el-col>
 | 
					                                </el-col>
 | 
				
			||||||
@@ -84,7 +84,7 @@
 | 
				
			|||||||
                            <el-table :data="state.machine.opLogs" :height="state.resourceOpTableHeight" stripe size="small" empty-text="暂无操作记录">
 | 
					                            <el-table :data="state.machine.opLogs" :height="state.resourceOpTableHeight" stripe size="small" empty-text="暂无操作记录">
 | 
				
			||||||
                                <el-table-column prop="createTime" show-overflow-tooltip width="135">
 | 
					                                <el-table-column prop="createTime" show-overflow-tooltip width="135">
 | 
				
			||||||
                                    <template #default="scope">
 | 
					                                    <template #default="scope">
 | 
				
			||||||
                                        {{ dateFormat(scope.row.createTime) }}
 | 
					                                        {{ formatDate(scope.row.createTime) }}
 | 
				
			||||||
                                    </template>
 | 
					                                    </template>
 | 
				
			||||||
                                </el-table-column>
 | 
					                                </el-table-column>
 | 
				
			||||||
                                <el-table-column prop="codePath" min-width="400" show-overflow-tooltip>
 | 
					                                <el-table-column prop="codePath" min-width="400" show-overflow-tooltip>
 | 
				
			||||||
@@ -118,7 +118,7 @@
 | 
				
			|||||||
                            <el-table :data="state.db.opLogs" :height="state.resourceOpTableHeight" stripe size="small" empty-text="暂无操作记录">
 | 
					                            <el-table :data="state.db.opLogs" :height="state.resourceOpTableHeight" stripe size="small" empty-text="暂无操作记录">
 | 
				
			||||||
                                <el-table-column prop="createTime" show-overflow-tooltip min-width="135">
 | 
					                                <el-table-column prop="createTime" show-overflow-tooltip min-width="135">
 | 
				
			||||||
                                    <template #default="scope">
 | 
					                                    <template #default="scope">
 | 
				
			||||||
                                        {{ dateFormat(scope.row.createTime) }}
 | 
					                                        {{ formatDate(scope.row.createTime) }}
 | 
				
			||||||
                                    </template>
 | 
					                                    </template>
 | 
				
			||||||
                                </el-table-column>
 | 
					                                </el-table-column>
 | 
				
			||||||
                                <el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
 | 
					                                <el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
 | 
				
			||||||
@@ -159,7 +159,7 @@
 | 
				
			|||||||
                            <el-table :data="state.redis.opLogs" :height="state.resourceOpTableHeight" stripe size="small" empty-text="暂无操作记录">
 | 
					                            <el-table :data="state.redis.opLogs" :height="state.resourceOpTableHeight" stripe size="small" empty-text="暂无操作记录">
 | 
				
			||||||
                                <el-table-column prop="createTime" show-overflow-tooltip min-width="135">
 | 
					                                <el-table-column prop="createTime" show-overflow-tooltip min-width="135">
 | 
				
			||||||
                                    <template #default="scope">
 | 
					                                    <template #default="scope">
 | 
				
			||||||
                                        {{ dateFormat(scope.row.createTime) }}
 | 
					                                        {{ formatDate(scope.row.createTime) }}
 | 
				
			||||||
                                    </template>
 | 
					                                    </template>
 | 
				
			||||||
                                </el-table-column>
 | 
					                                </el-table-column>
 | 
				
			||||||
                                <el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
 | 
					                                <el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
 | 
				
			||||||
@@ -198,7 +198,7 @@
 | 
				
			|||||||
                            <el-table :data="state.mongo.opLogs" :height="state.resourceOpTableHeight" stripe size="small" empty-text="暂无操作记录">
 | 
					                            <el-table :data="state.mongo.opLogs" :height="state.resourceOpTableHeight" stripe size="small" empty-text="暂无操作记录">
 | 
				
			||||||
                                <el-table-column prop="createTime" show-overflow-tooltip min-width="135">
 | 
					                                <el-table-column prop="createTime" show-overflow-tooltip min-width="135">
 | 
				
			||||||
                                    <template #default="scope">
 | 
					                                    <template #default="scope">
 | 
				
			||||||
                                        {{ dateFormat(scope.row.createTime) }}
 | 
					                                        {{ formatDate(scope.row.createTime) }}
 | 
				
			||||||
                                    </template>
 | 
					                                    </template>
 | 
				
			||||||
                                </el-table-column>
 | 
					                                </el-table-column>
 | 
				
			||||||
                                <el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
 | 
					                                <el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
 | 
				
			||||||
@@ -228,7 +228,7 @@
 | 
				
			|||||||
                <el-table-column property="msg" label="消息"></el-table-column>
 | 
					                <el-table-column property="msg" label="消息"></el-table-column>
 | 
				
			||||||
                <el-table-column property="createTime" label="时间" width="150">
 | 
					                <el-table-column property="createTime" label="时间" width="150">
 | 
				
			||||||
                    <template #default="scope">
 | 
					                    <template #default="scope">
 | 
				
			||||||
                        {{ dateFormat(scope.row.createTime) }}
 | 
					                        {{ formatDate(scope.row.createTime) }}
 | 
				
			||||||
                    </template>
 | 
					                    </template>
 | 
				
			||||||
                </el-table-column>
 | 
					                </el-table-column>
 | 
				
			||||||
            </el-table>
 | 
					            </el-table>
 | 
				
			||||||
@@ -257,7 +257,7 @@ import { useRouter } from 'vue-router';
 | 
				
			|||||||
import { storeToRefs } from 'pinia';
 | 
					import { storeToRefs } from 'pinia';
 | 
				
			||||||
import { useUserInfo } from '@/store/userInfo';
 | 
					import { useUserInfo } from '@/store/userInfo';
 | 
				
			||||||
import { personApi } from '../personal/api';
 | 
					import { personApi } from '../personal/api';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import SvgIcon from '@/components/svgIcon/index.vue';
 | 
					import SvgIcon from '@/components/svgIcon/index.vue';
 | 
				
			||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
import { resourceOpLogApi } from '../ops/tag/api';
 | 
					import { resourceOpLogApi } from '../ops/tag/api';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,7 +132,7 @@ 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 { initRouter } from '@/router/index';
 | 
					import { initRouter } from '@/router/index';
 | 
				
			||||||
import { saveToken, saveUser } from '@/common/utils/storage';
 | 
					import { getRefreshToken, saveRefreshToken, saveToken, saveUser } 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';
 | 
				
			||||||
import { RsaEncrypt } from '@/common/rsa';
 | 
					import { RsaEncrypt } from '@/common/rsa';
 | 
				
			||||||
@@ -279,19 +279,20 @@ const login = () => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const otpVerify = async () => {
 | 
					const otpVerify = async () => {
 | 
				
			||||||
    otpFormRef.value.validate(async (valid: boolean) => {
 | 
					    try {
 | 
				
			||||||
        if (!valid) {
 | 
					        await otpFormRef.value.validate();
 | 
				
			||||||
            return false;
 | 
					    } catch (e: any) {
 | 
				
			||||||
        }
 | 
					        return false;
 | 
				
			||||||
        try {
 | 
					    }
 | 
				
			||||||
            state.loading.otpConfirm = true;
 | 
					
 | 
				
			||||||
            const accessToken = await openApi.otpVerify(state.otpDialog.form);
 | 
					    try {
 | 
				
			||||||
            await signInSuccess(accessToken);
 | 
					        state.loading.otpConfirm = true;
 | 
				
			||||||
            state.otpDialog.visible = false;
 | 
					        const res = await openApi.otpVerify(state.otpDialog.form);
 | 
				
			||||||
        } finally {
 | 
					        await signInSuccess(res.token, res.refresh_token);
 | 
				
			||||||
            state.loading.otpConfirm = false;
 | 
					        state.otpDialog.visible = false;
 | 
				
			||||||
        }
 | 
					    } finally {
 | 
				
			||||||
    });
 | 
					        state.loading.otpConfirm = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 登录
 | 
					// 登录
 | 
				
			||||||
@@ -327,22 +328,23 @@ const onSignIn = async () => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const updateUserInfo = async () => {
 | 
					const updateUserInfo = async () => {
 | 
				
			||||||
    baseInfoFormRef.value.validate(async (valid: boolean) => {
 | 
					    try {
 | 
				
			||||||
        if (!valid) {
 | 
					        await baseInfoFormRef.value.validate();
 | 
				
			||||||
            return false;
 | 
					    } catch (e: any) {
 | 
				
			||||||
        }
 | 
					        return false;
 | 
				
			||||||
        try {
 | 
					    }
 | 
				
			||||||
            state.loading.updateUserConfirm = true;
 | 
					
 | 
				
			||||||
            const form = state.baseInfoDialog.form;
 | 
					    try {
 | 
				
			||||||
            await personApi.updateAccount.request(state.baseInfoDialog.form);
 | 
					        state.loading.updateUserConfirm = true;
 | 
				
			||||||
            state.baseInfoDialog.visible = false;
 | 
					        const form = state.baseInfoDialog.form;
 | 
				
			||||||
            useUserInfo().userInfo.username = form.username;
 | 
					        await personApi.updateAccount.request(state.baseInfoDialog.form);
 | 
				
			||||||
            useUserInfo().userInfo.name = form.name;
 | 
					        state.baseInfoDialog.visible = false;
 | 
				
			||||||
            await toIndex();
 | 
					        useUserInfo().userInfo.username = form.username;
 | 
				
			||||||
        } finally {
 | 
					        useUserInfo().userInfo.name = form.name;
 | 
				
			||||||
            state.loading.updateUserConfirm = false;
 | 
					        await toIndex();
 | 
				
			||||||
        }
 | 
					    } finally {
 | 
				
			||||||
    });
 | 
					        state.loading.updateUserConfirm = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const loginResDeal = (loginRes: any) => {
 | 
					const loginResDeal = (loginRes: any) => {
 | 
				
			||||||
@@ -366,7 +368,7 @@ const loginResDeal = (loginRes: any) => {
 | 
				
			|||||||
    const token = loginRes.token;
 | 
					    const token = loginRes.token;
 | 
				
			||||||
    // 如果不需要    otp校验,则该token即为accessToken,否则为otp校验token
 | 
					    // 如果不需要    otp校验,则该token即为accessToken,否则为otp校验token
 | 
				
			||||||
    if (loginRes.otp == -1) {
 | 
					    if (loginRes.otp == -1) {
 | 
				
			||||||
        signInSuccess(token);
 | 
					        signInSuccess(token, loginRes.refresh_token);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -379,12 +381,16 @@ const loginResDeal = (loginRes: any) => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 登录成功后的跳转
 | 
					// 登录成功后的跳转
 | 
				
			||||||
const signInSuccess = async (accessToken: string = '') => {
 | 
					const signInSuccess = async (accessToken: string = '', refreshToken = '') => {
 | 
				
			||||||
    if (!accessToken) {
 | 
					    if (!accessToken) {
 | 
				
			||||||
        accessToken = getToken();
 | 
					        accessToken = getToken();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (!refreshToken) {
 | 
				
			||||||
 | 
					        refreshToken = getRefreshToken();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    // 存储 token 到浏览器缓存
 | 
					    // 存储 token 到浏览器缓存
 | 
				
			||||||
    saveToken(accessToken);
 | 
					    saveToken(accessToken);
 | 
				
			||||||
 | 
					    saveRefreshToken(refreshToken);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 初始化路由
 | 
					    // 初始化路由
 | 
				
			||||||
    await initRouter();
 | 
					    await initRouter();
 | 
				
			||||||
@@ -415,26 +421,27 @@ const toIndex = async () => {
 | 
				
			|||||||
    }, 300);
 | 
					    }, 300);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const changePwd = () => {
 | 
					const changePwd = async () => {
 | 
				
			||||||
    changePwdFormRef.value.validate(async (valid: boolean) => {
 | 
					    try {
 | 
				
			||||||
        if (!valid) {
 | 
					        await changePwdFormRef.value.validate();
 | 
				
			||||||
            return false;
 | 
					    } catch (e: any) {
 | 
				
			||||||
        }
 | 
					        return false;
 | 
				
			||||||
        try {
 | 
					    }
 | 
				
			||||||
            state.loading.changePwd = true;
 | 
					
 | 
				
			||||||
            const form = state.changePwdDialog.form;
 | 
					    try {
 | 
				
			||||||
            const changePwdReq: any = { ...form };
 | 
					        state.loading.changePwd = true;
 | 
				
			||||||
            changePwdReq.oldPassword = await RsaEncrypt(form.oldPassword);
 | 
					        const form = state.changePwdDialog.form;
 | 
				
			||||||
            changePwdReq.newPassword = await RsaEncrypt(form.newPassword);
 | 
					        const changePwdReq: any = { ...form };
 | 
				
			||||||
            await openApi.changePwd(changePwdReq);
 | 
					        changePwdReq.oldPassword = await RsaEncrypt(form.oldPassword);
 | 
				
			||||||
            ElMessage.success('密码修改成功, 新密码已填充至登录密码框');
 | 
					        changePwdReq.newPassword = await RsaEncrypt(form.newPassword);
 | 
				
			||||||
            state.loginForm.password = state.changePwdDialog.form.newPassword;
 | 
					        await openApi.changePwd(changePwdReq);
 | 
				
			||||||
            state.changePwdDialog.visible = false;
 | 
					        ElMessage.success('密码修改成功, 新密码已填充至登录密码框');
 | 
				
			||||||
            getCaptcha();
 | 
					        state.loginForm.password = state.changePwdDialog.form.newPassword;
 | 
				
			||||||
        } finally {
 | 
					        state.changePwdDialog.visible = false;
 | 
				
			||||||
            state.loading.changePwd = false;
 | 
					        getCaptcha();
 | 
				
			||||||
        }
 | 
					    } finally {
 | 
				
			||||||
    });
 | 
					        state.loading.changePwd = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cancelChangePwd = () => {
 | 
					const cancelChangePwd = () => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -187,10 +187,10 @@
 | 
				
			|||||||
                <el-descriptions-item :span="3" label="数据库">{{ infoDialog.data?.database }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="3" label="数据库">{{ infoDialog.data?.database }}</el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="3" label="备注">{{ infoDialog.data?.remark }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="3" label="备注">{{ infoDialog.data?.remark }}</el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-descriptions-item :span="2" label="创建时间">{{ dateFormat(infoDialog.data?.createTime) }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="2" label="创建时间">{{ formatDate(infoDialog.data?.createTime) }} </el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="创建者">{{ infoDialog.data?.creator }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="创建者">{{ infoDialog.data?.creator }}</el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-descriptions-item :span="2" label="更新时间">{{ dateFormat(infoDialog.data?.updateTime) }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="2" label="更新时间">{{ formatDate(infoDialog.data?.updateTime) }} </el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="修改者">{{ infoDialog.data?.modifier }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="修改者">{{ infoDialog.data?.modifier }}</el-descriptions-item>
 | 
				
			||||||
            </el-descriptions>
 | 
					            </el-descriptions>
 | 
				
			||||||
        </el-dialog>
 | 
					        </el-dialog>
 | 
				
			||||||
@@ -205,7 +205,7 @@ import { dbApi } from './api';
 | 
				
			|||||||
import config from '@/common/config';
 | 
					import config from '@/common/config';
 | 
				
			||||||
import { joinClientParams } from '@/common/request';
 | 
					import { joinClientParams } from '@/common/request';
 | 
				
			||||||
import { isTrue } from '@/common/assert';
 | 
					import { isTrue } from '@/common/assert';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
					import PageTable from '@/components/pagetable/PageTable.vue';
 | 
				
			||||||
import { TableColumn } from '@/components/pagetable';
 | 
					import { TableColumn } from '@/components/pagetable';
 | 
				
			||||||
import { hasPerms } from '@/components/auth/auth';
 | 
					import { hasPerms } from '@/components/auth/auth';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,14 +45,14 @@
 | 
				
			|||||||
            <el-descriptions :column="1" border>
 | 
					            <el-descriptions :column="1" border>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="数据库名称">{{ infoDialog.data.dbName }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="数据库名称">{{ infoDialog.data.dbName }}</el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item v-if="infoDialog.data.pointInTime" :span="1" label="恢复时间点">{{
 | 
					                <el-descriptions-item v-if="infoDialog.data.pointInTime" :span="1" label="恢复时间点">{{
 | 
				
			||||||
                    dateFormat(infoDialog.data.pointInTime)
 | 
					                    formatDate(infoDialog.data.pointInTime)
 | 
				
			||||||
                }}</el-descriptions-item>
 | 
					                }}</el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item v-if="!infoDialog.data.pointInTime" :span="1" label="数据库备份">{{
 | 
					                <el-descriptions-item v-if="!infoDialog.data.pointInTime" :span="1" label="数据库备份">{{
 | 
				
			||||||
                    infoDialog.data.dbBackupHistoryName
 | 
					                    infoDialog.data.dbBackupHistoryName
 | 
				
			||||||
                }}</el-descriptions-item>
 | 
					                }}</el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="开始时间">{{ dateFormat(infoDialog.data.startTime) }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="开始时间">{{ formatDate(infoDialog.data.startTime) }}</el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="是否启用">{{ infoDialog.data.enabledDesc }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="是否启用">{{ infoDialog.data.enabledDesc }}</el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="执行时间">{{ dateFormat(infoDialog.data.lastTime) }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="执行时间">{{ formatDate(infoDialog.data.lastTime) }}</el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="执行结果">{{ infoDialog.data.lastResult }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="执行结果">{{ infoDialog.data.lastResult }}</el-descriptions-item>
 | 
				
			||||||
            </el-descriptions>
 | 
					            </el-descriptions>
 | 
				
			||||||
        </el-dialog>
 | 
					        </el-dialog>
 | 
				
			||||||
@@ -66,7 +66,7 @@ import PageTable from '@/components/pagetable/PageTable.vue';
 | 
				
			|||||||
import { TableColumn } from '@/components/pagetable';
 | 
					import { TableColumn } from '@/components/pagetable';
 | 
				
			||||||
import { SearchItem } from '@/components/SearchForm';
 | 
					import { SearchItem } from '@/components/SearchForm';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
const DbRestoreEdit = defineAsyncComponent(() => import('./DbRestoreEdit.vue'));
 | 
					const DbRestoreEdit = defineAsyncComponent(() => import('./DbRestoreEdit.vue'));
 | 
				
			||||||
const pageTableRef: Ref<any> = ref(null);
 | 
					const pageTableRef: Ref<any> = ref(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,10 +53,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                <el-descriptions-item :span="3" label="SSH隧道">{{ infoDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="3" label="SSH隧道">{{ infoDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-descriptions-item :span="2" label="创建时间">{{ dateFormat(infoDialog.data.createTime) }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="2" label="创建时间">{{ formatDate(infoDialog.data.createTime) }} </el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-descriptions-item :span="2" label="更新时间">{{ dateFormat(infoDialog.data.updateTime) }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="2" label="更新时间">{{ formatDate(infoDialog.data.updateTime) }} </el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="修改者">{{ infoDialog.data.modifier }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="修改者">{{ infoDialog.data.modifier }}</el-descriptions-item>
 | 
				
			||||||
            </el-descriptions>
 | 
					            </el-descriptions>
 | 
				
			||||||
        </el-dialog>
 | 
					        </el-dialog>
 | 
				
			||||||
@@ -76,7 +76,7 @@
 | 
				
			|||||||
import { defineAsyncComponent, onMounted, reactive, ref, Ref, toRefs } from 'vue';
 | 
					import { defineAsyncComponent, onMounted, reactive, ref, Ref, toRefs } from 'vue';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { dbApi } from './api';
 | 
					import { dbApi } from './api';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
					import PageTable from '@/components/pagetable/PageTable.vue';
 | 
				
			||||||
import { TableColumn } from '@/components/pagetable';
 | 
					import { TableColumn } from '@/components/pagetable';
 | 
				
			||||||
import { hasPerms } from '@/components/auth/auth';
 | 
					import { hasPerms } from '@/components/auth/auth';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -296,44 +296,37 @@ const onRunSql = async (newTab = false) => {
 | 
				
			|||||||
    notBlank(sql && sql.trim(), '请选中需要执行的sql');
 | 
					    notBlank(sql && sql.trim(), '请选中需要执行的sql');
 | 
				
			||||||
    // 去除字符串前的空格、换行等
 | 
					    // 去除字符串前的空格、换行等
 | 
				
			||||||
    sql = sql.replace(/(^\s*)/g, '');
 | 
					    sql = sql.replace(/(^\s*)/g, '');
 | 
				
			||||||
    let execRemark = '';
 | 
					 | 
				
			||||||
    let canRun = true;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 简单截取前十个字符
 | 
					    // 简单截取前十个字符
 | 
				
			||||||
    const sqlPrefix = sql.slice(0, 10).toLowerCase();
 | 
					    const sqlPrefix = sql.slice(0, 10).toLowerCase();
 | 
				
			||||||
    if (
 | 
					    const nonQuery =
 | 
				
			||||||
        sqlPrefix.startsWith('update') ||
 | 
					        sqlPrefix.startsWith('update') ||
 | 
				
			||||||
        sqlPrefix.startsWith('insert') ||
 | 
					        sqlPrefix.startsWith('insert') ||
 | 
				
			||||||
        sqlPrefix.startsWith('delete') ||
 | 
					        sqlPrefix.startsWith('delete') ||
 | 
				
			||||||
        sqlPrefix.startsWith('alert') ||
 | 
					        sqlPrefix.startsWith('alert') ||
 | 
				
			||||||
        sqlPrefix.startsWith('drop') ||
 | 
					        sqlPrefix.startsWith('drop') ||
 | 
				
			||||||
        sqlPrefix.startsWith('create')
 | 
					        sqlPrefix.startsWith('create');
 | 
				
			||||||
    ) {
 | 
					
 | 
				
			||||||
        const res: any = await ElMessageBox.prompt('请输入备注', 'Tip', {
 | 
					    // 启用工单审批
 | 
				
			||||||
            confirmButtonText: '确定',
 | 
					    if (nonQuery && getNowDbInst().flowProcdef) {
 | 
				
			||||||
            cancelButtonText: '取消',
 | 
					        try {
 | 
				
			||||||
            inputPattern: /^[\s\S]*.*[^\s][\s\S]*$/,
 | 
					            getNowDbInst().promptExeSql(props.dbName, sql, null, () => {
 | 
				
			||||||
            inputErrorMessage: '请输入执行该sql的备注信息',
 | 
					                ElMessage.success('工单提交成功');
 | 
				
			||||||
        });
 | 
					            });
 | 
				
			||||||
        execRemark = res.value;
 | 
					        } catch (e) {
 | 
				
			||||||
        if (!execRemark) {
 | 
					            ElMessage.success('工单提交失败');
 | 
				
			||||||
            canRun = false;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!canRun) {
 | 
					 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 启用工单审批
 | 
					    let execRemark;
 | 
				
			||||||
    if (execRemark && getNowDbInst().flowProcdef) {
 | 
					    if (nonQuery) {
 | 
				
			||||||
        try {
 | 
					        const res: any = await ElMessageBox.prompt('请输入备注', 'Tip', {
 | 
				
			||||||
            await getNowDbInst().runSql(props.dbName, sql, execRemark);
 | 
					            confirmButtonText: '确定',
 | 
				
			||||||
            ElMessage.success('工单提交成功');
 | 
					            cancelButtonText: '取消',
 | 
				
			||||||
            return;
 | 
					            inputErrorMessage: '输入执行该sql的备注信息',
 | 
				
			||||||
        } catch (e) {
 | 
					        });
 | 
				
			||||||
            ElMessage.success('工单提交失败');
 | 
					        execRemark = res.value;
 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let execRes: ExecResTab;
 | 
					    let execRes: ExecResTab;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,6 +66,8 @@ const runSql = async () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
        state.btnLoading = true;
 | 
					        state.btnLoading = true;
 | 
				
			||||||
 | 
					        runSuccess = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const res = await dbApi.sqlExec.request({
 | 
					        const res = await dbApi.sqlExec.request({
 | 
				
			||||||
            id: props.dbId,
 | 
					            id: props.dbId,
 | 
				
			||||||
            db: props.db,
 | 
					            db: props.db,
 | 
				
			||||||
@@ -75,7 +77,6 @@ const runSql = async () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // 存在流程审批
 | 
					        // 存在流程审批
 | 
				
			||||||
        if (props.flowProcdef) {
 | 
					        if (props.flowProcdef) {
 | 
				
			||||||
            runSuccess = false;
 | 
					 | 
				
			||||||
            ElMessage.success('工单提交成功');
 | 
					            ElMessage.success('工单提交成功');
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -87,7 +88,6 @@ const runSql = async () => {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        runSuccess = true;
 | 
					 | 
				
			||||||
        ElMessage.success('执行成功');
 | 
					        ElMessage.success('执行成功');
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
        runSuccess = false;
 | 
					        runSuccess = false;
 | 
				
			||||||
@@ -96,9 +96,9 @@ const runSql = async () => {
 | 
				
			|||||||
            if (props.runSuccessCallback) {
 | 
					            if (props.runSuccessCallback) {
 | 
				
			||||||
                props.runSuccessCallback();
 | 
					                props.runSuccessCallback();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            cancel();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        state.btnLoading = false;
 | 
					        state.btnLoading = false;
 | 
				
			||||||
        cancel();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -161,7 +161,7 @@ import { DbInst } from '@/views/ops/db/db';
 | 
				
			|||||||
import { Contextmenu, ContextmenuItem } from '@/components/contextmenu';
 | 
					import { Contextmenu, ContextmenuItem } from '@/components/contextmenu';
 | 
				
			||||||
import SvgIcon from '@/components/svgIcon/index.vue';
 | 
					import SvgIcon from '@/components/svgIcon/index.vue';
 | 
				
			||||||
import { exportCsv, exportFile } from '@/common/utils/export';
 | 
					import { exportCsv, exportFile } from '@/common/utils/export';
 | 
				
			||||||
import { dateStrFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import { useIntervalFn, useStorage } from '@vueuse/core';
 | 
					import { useIntervalFn, useStorage } from '@vueuse/core';
 | 
				
			||||||
import { ColumnTypeSubscript, compatibleMysql, DataType, DbDialect, getDbDialect } from '../../dialect/index';
 | 
					import { ColumnTypeSubscript, compatibleMysql, DataType, DbDialect, getDbDialect } from '../../dialect/index';
 | 
				
			||||||
import ColumnFormItem from './ColumnFormItem.vue';
 | 
					import ColumnFormItem from './ColumnFormItem.vue';
 | 
				
			||||||
@@ -617,6 +617,10 @@ const onDeleteData = async () => {
 | 
				
			|||||||
    const db = state.db;
 | 
					    const db = state.db;
 | 
				
			||||||
    const dbInst = getNowDbInst();
 | 
					    const dbInst = getNowDbInst();
 | 
				
			||||||
    dbInst.promptExeSql(db, await dbInst.genDeleteByPrimaryKeysSql(db, state.table, deleteDatas as any), null, () => {
 | 
					    dbInst.promptExeSql(db, await dbInst.genDeleteByPrimaryKeysSql(db, state.table, deleteDatas as any), null, () => {
 | 
				
			||||||
 | 
					        // 存在流程则恢复原值,需工单流程审批完后自动执行
 | 
				
			||||||
 | 
					        if (dbInst.flowProcdef) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        emits('dataDelete', deleteDatas);
 | 
					        emits('dataDelete', deleteDatas);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -628,7 +632,7 @@ const onEditRowData = () => {
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const data = selectionDatas[0];
 | 
					    const data = selectionDatas[0];
 | 
				
			||||||
    state.tableDataFormDialog.data = data;
 | 
					    state.tableDataFormDialog.data = { ...data };
 | 
				
			||||||
    state.tableDataFormDialog.title = `编辑表'${props.table}'数据`;
 | 
					    state.tableDataFormDialog.title = `编辑表'${props.table}'数据`;
 | 
				
			||||||
    state.tableDataFormDialog.visible = true;
 | 
					    state.tableDataFormDialog.visible = true;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -674,13 +678,13 @@ const onExportCsv = () => {
 | 
				
			|||||||
            columnNames.push(column.columnName);
 | 
					            columnNames.push(column.columnName);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    exportCsv(`数据导出-${state.table}-${dateStrFormat('yyyyMMddHHmm', new Date().toString())}`, columnNames, dataList);
 | 
					    exportCsv(`数据导出-${state.table}-${formatDate(new Date(), 'yyyyMMddHHmm')}`, columnNames, dataList);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onExportSql = async () => {
 | 
					const onExportSql = async () => {
 | 
				
			||||||
    const selectionDatas = state.datas;
 | 
					    const selectionDatas = state.datas;
 | 
				
			||||||
    exportFile(
 | 
					    exportFile(
 | 
				
			||||||
        `数据导出-${state.table}-${dateStrFormat('yyyyMMddHHmm', new Date().toString())}.sql`,
 | 
					        `数据导出-${state.table}-${formatDate(new Date(), 'yyyyMMddHHmm')}.sql`,
 | 
				
			||||||
        await getNowDbInst().genInsertSql(state.db, state.table, selectionDatas)
 | 
					        await getNowDbInst().genInsertSql(state.db, state.table, selectionDatas)
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -763,7 +767,12 @@ const submitUpdateFields = async () => {
 | 
				
			|||||||
        res += await dbInst.genUpdateSql(db, state.table, updateColumnValue, rowData);
 | 
					        res += await dbInst.genUpdateSql(db, state.table, updateColumnValue, rowData);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dbInst.promptExeSql(db, res, cancelUpdateFields, () => {
 | 
					    dbInst.promptExeSql(db, res, null, () => {
 | 
				
			||||||
 | 
					        // 存在流程则恢复原值,需工单流程审批完后自动执行
 | 
				
			||||||
 | 
					        if (dbInst.flowProcdef) {
 | 
				
			||||||
 | 
					            cancelUpdateFields();
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        triggerRefresh();
 | 
					        triggerRefresh();
 | 
				
			||||||
        cellUpdateMap.clear();
 | 
					        cellUpdateMap.clear();
 | 
				
			||||||
        changeUpdatedField();
 | 
					        changeUpdatedField();
 | 
				
			||||||
@@ -810,11 +819,11 @@ const getFormatTimeValue = (dataType: DataType, originValue: string): string =>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    switch (dataType) {
 | 
					    switch (dataType) {
 | 
				
			||||||
        case DataType.Time:
 | 
					        case DataType.Time:
 | 
				
			||||||
            return dateStrFormat('HH:mm:ss', originValue);
 | 
					            return formatDate(originValue, 'HH:mm:ss');
 | 
				
			||||||
        case DataType.Date:
 | 
					        case DataType.Date:
 | 
				
			||||||
            return dateStrFormat('yyyy-MM-dd', originValue);
 | 
					            return formatDate(originValue, 'YYYY-MM-DD');
 | 
				
			||||||
        case DataType.DateTime:
 | 
					        case DataType.DateTime:
 | 
				
			||||||
            return dateStrFormat('yyyy-MM-dd HH:mm:ss', originValue);
 | 
					            return formatDate(originValue, 'YYYY-MM-DD HH:mm:ss');
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
            return originValue;
 | 
					            return originValue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,35 +85,35 @@ const closeDialog = () => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const confirm = async () => {
 | 
					const confirm = async () => {
 | 
				
			||||||
    dataForm.value.validate(async (valid: boolean) => {
 | 
					    try {
 | 
				
			||||||
        if (!valid) {
 | 
					        await dataForm.value.validate();
 | 
				
			||||||
            ElMessage.error('请正确填写数据信息');
 | 
					    } catch (e: any) {
 | 
				
			||||||
            return false;
 | 
					        ElMessage.error('请正确填写数据信息');
 | 
				
			||||||
        }
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const dbInst = props.dbInst;
 | 
					    const dbInst = props.dbInst;
 | 
				
			||||||
        const data = modelValue.value;
 | 
					    const data = modelValue.value;
 | 
				
			||||||
        const db = props.dbName;
 | 
					    const db = props.dbName;
 | 
				
			||||||
        const tableName = props.tableName;
 | 
					    const tableName = props.tableName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let sql = '';
 | 
					    let sql = '';
 | 
				
			||||||
        if (oldValue) {
 | 
					    if (oldValue) {
 | 
				
			||||||
            const updateColumnValue = {};
 | 
					        const updateColumnValue = {};
 | 
				
			||||||
            Object.keys(oldValue).forEach((key) => {
 | 
					        Object.keys(oldValue).forEach((key) => {
 | 
				
			||||||
                // 如果新旧值不相等,则为需要更新的字段
 | 
					            // 如果新旧值不相等,则为需要更新的字段
 | 
				
			||||||
                if (oldValue[key] !== modelValue.value[key]) {
 | 
					            if (oldValue[key] !== modelValue.value[key]) {
 | 
				
			||||||
                    updateColumnValue[key] = modelValue.value[key];
 | 
					                updateColumnValue[key] = modelValue.value[key];
 | 
				
			||||||
                }
 | 
					            }
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            sql = await dbInst.genUpdateSql(db, tableName, updateColumnValue, oldValue);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            sql = await dbInst.genInsertSql(db, tableName, [data], true);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        dbInst.promptExeSql(db, sql, null, () => {
 | 
					 | 
				
			||||||
            closeDialog();
 | 
					 | 
				
			||||||
            emit('submitSuccess');
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        sql = await dbInst.genUpdateSql(db, tableName, updateColumnValue, oldValue);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        sql = await dbInst.genInsertSql(db, tableName, [data], true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dbInst.promptExeSql(db, sql, null, () => {
 | 
				
			||||||
 | 
					        closeDialog();
 | 
				
			||||||
 | 
					        emit('submitSuccess');
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,17 +50,6 @@
 | 
				
			|||||||
                    </el-tooltip>
 | 
					                    </el-tooltip>
 | 
				
			||||||
                    <el-divider direction="vertical" border-style="dashed" />
 | 
					                    <el-divider direction="vertical" border-style="dashed" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <el-tooltip :show-after="500" class="box-item" effect="dark" content="commit" placement="top">
 | 
					 | 
				
			||||||
                        <template #content>
 | 
					 | 
				
			||||||
                            1. 右击数据/表头可显示操作菜单 <br />
 | 
					 | 
				
			||||||
                            2. 按住Ctrl点击数据则为多选 <br />
 | 
					 | 
				
			||||||
                            3. 双击单元格可编辑数据 <br />
 | 
					 | 
				
			||||||
                            4. 鼠标悬停字段名或标签树的表名可提示相关备注
 | 
					 | 
				
			||||||
                        </template>
 | 
					 | 
				
			||||||
                        <el-link icon="QuestionFilled" :underline="false"> </el-link>
 | 
					 | 
				
			||||||
                    </el-tooltip>
 | 
					 | 
				
			||||||
                    <el-divider direction="vertical" border-style="dashed" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <!-- 表数据展示配置 -->
 | 
					                    <!-- 表数据展示配置 -->
 | 
				
			||||||
                    <el-popover
 | 
					                    <el-popover
 | 
				
			||||||
                        popper-style="max-height: 550px; overflow: auto; max-width: 450px"
 | 
					                        popper-style="max-height: 550px; overflow: auto; max-width: 450px"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div class="db-table">
 | 
					    <div class="db-table">
 | 
				
			||||||
        <el-row class="mb5">
 | 
					        <el-row class="mb5">
 | 
				
			||||||
            <el-popover v-model:visible="showDumpInfo" :width="470" placement="right" trigger="click">
 | 
					            <el-popover v-model:visible="state.dumpInfo.visible" trigger="click" :width="470" placement="right">
 | 
				
			||||||
                <template #reference>
 | 
					                <template #reference>
 | 
				
			||||||
                    <el-button class="ml5" type="success" size="small">导出</el-button>
 | 
					                    <el-button class="ml5" type="success" size="small">导出</el-button>
 | 
				
			||||||
                </template>
 | 
					                </template>
 | 
				
			||||||
@@ -13,16 +13,15 @@
 | 
				
			|||||||
                    </el-radio-group>
 | 
					                    </el-radio-group>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-form-item label="导出表: ">
 | 
					                <el-form-item>
 | 
				
			||||||
                    <el-table @selection-change="handleDumpTableSelectionChange" max-height="300" size="small" :data="tables">
 | 
					                    <el-table :data="state.dumpInfo.tables" empty-text="请先选择要导出的表" max-height="300" size="small">
 | 
				
			||||||
                        <el-table-column type="selection" width="45" />
 | 
					 | 
				
			||||||
                        <el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip> </el-table-column>
 | 
					                        <el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
                        <el-table-column property="tableComment" label="备注" min-width="150" show-overflow-tooltip> </el-table-column>
 | 
					                        <el-table-column property="tableComment" label="备注" min-width="150" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
                    </el-table>
 | 
					                    </el-table>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <div style="text-align: right">
 | 
					                <div style="text-align: right">
 | 
				
			||||||
                    <el-button @click="showDumpInfo = false" size="small">取消</el-button>
 | 
					                    <el-button @click="state.dumpInfo.visible = false" size="small">取消</el-button>
 | 
				
			||||||
                    <el-button @click="dump(db)" type="success" size="small">确定</el-button>
 | 
					                    <el-button @click="dump(db)" type="success" size="small">确定</el-button>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </el-popover>
 | 
					            </el-popover>
 | 
				
			||||||
@@ -30,7 +29,9 @@
 | 
				
			|||||||
            <el-button type="primary" size="small" @click="openEditTable(false)">创建表</el-button>
 | 
					            <el-button type="primary" size="small" @click="openEditTable(false)">创建表</el-button>
 | 
				
			||||||
        </el-row>
 | 
					        </el-row>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <el-table v-loading="loading" border stripe :data="filterTableInfos" size="small" :height="height">
 | 
					        <el-table v-loading="loading" @selection-change="handleDumpTableSelectionChange" border stripe :data="filterTableInfos" size="small" :height="height">
 | 
				
			||||||
 | 
					            <el-table-column type="selection" width="30" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip>
 | 
					            <el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip>
 | 
				
			||||||
                <template #header>
 | 
					                <template #header>
 | 
				
			||||||
                    <el-input v-model="tableNameSearch" size="small" placeholder="表名: 输入可过滤" clearable />
 | 
					                    <el-input v-model="tableNameSearch" size="small" placeholder="表名: 输入可过滤" clearable />
 | 
				
			||||||
@@ -161,8 +162,8 @@ const state = reactive({
 | 
				
			|||||||
    tables: [],
 | 
					    tables: [],
 | 
				
			||||||
    tableNameSearch: '',
 | 
					    tableNameSearch: '',
 | 
				
			||||||
    tableCommentSearch: '',
 | 
					    tableCommentSearch: '',
 | 
				
			||||||
    showDumpInfo: false,
 | 
					 | 
				
			||||||
    dumpInfo: {
 | 
					    dumpInfo: {
 | 
				
			||||||
 | 
					        visible: false,
 | 
				
			||||||
        id: 0,
 | 
					        id: 0,
 | 
				
			||||||
        db: '',
 | 
					        db: '',
 | 
				
			||||||
        type: 3,
 | 
					        type: 3,
 | 
				
			||||||
@@ -201,19 +202,7 @@ const state = reactive({
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {
 | 
					const { loading, tableNameSearch, tableCommentSearch, dumpInfo, chooseTableName, columnDialog, indexDialog, ddlDialog, tableCreateDialog } = toRefs(state);
 | 
				
			||||||
    loading,
 | 
					 | 
				
			||||||
    tables,
 | 
					 | 
				
			||||||
    tableNameSearch,
 | 
					 | 
				
			||||||
    tableCommentSearch,
 | 
					 | 
				
			||||||
    showDumpInfo,
 | 
					 | 
				
			||||||
    dumpInfo,
 | 
					 | 
				
			||||||
    chooseTableName,
 | 
					 | 
				
			||||||
    columnDialog,
 | 
					 | 
				
			||||||
    indexDialog,
 | 
					 | 
				
			||||||
    ddlDialog,
 | 
					 | 
				
			||||||
    tableCreateDialog,
 | 
					 | 
				
			||||||
} = toRefs(state);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
    getTables();
 | 
					    getTables();
 | 
				
			||||||
@@ -259,21 +248,22 @@ const getTables = async () => {
 | 
				
			|||||||
 * 选择导出数据库表
 | 
					 * 选择导出数据库表
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const handleDumpTableSelectionChange = (vals: any) => {
 | 
					const handleDumpTableSelectionChange = (vals: any) => {
 | 
				
			||||||
    state.dumpInfo.tables = vals.map((x: any) => x.tableName);
 | 
					    state.dumpInfo.tables = vals;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 数据库信息导出
 | 
					 * 数据库信息导出
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const dump = (db: string) => {
 | 
					const dump = (db: string) => {
 | 
				
			||||||
    isTrue(state.dumpInfo.tables.length > 0, '请选择要导出的表');
 | 
					    isTrue(state.dumpInfo.tables.length > 0, '请先选择要导出的表');
 | 
				
			||||||
 | 
					    const tableNames = state.dumpInfo.tables.map((x: any) => x.tableName);
 | 
				
			||||||
    const a = document.createElement('a');
 | 
					    const a = document.createElement('a');
 | 
				
			||||||
    a.setAttribute(
 | 
					    a.setAttribute(
 | 
				
			||||||
        'href',
 | 
					        'href',
 | 
				
			||||||
        `${config.baseApiUrl}/dbs/${props.dbId}/dump?db=${db}&type=${state.dumpInfo.type}&tables=${state.dumpInfo.tables.join(',')}&${joinClientParams()}`
 | 
					        `${config.baseApiUrl}/dbs/${props.dbId}/dump?db=${db}&type=${state.dumpInfo.type}&tables=${tableNames.join(',')}&${joinClientParams()}`
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    a.click();
 | 
					    a.click();
 | 
				
			||||||
    state.showDumpInfo = false;
 | 
					    state.dumpInfo.visible = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const showColumns = async (row: any) => {
 | 
					const showColumns = async (row: any) => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -352,7 +352,6 @@ export class DbInst {
 | 
				
			|||||||
     * 弹框提示是否执行sql
 | 
					     * 弹框提示是否执行sql
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    promptExeSql = (db: string, sql: string, cancelFunc: any = null, successFunc: any = null) => {
 | 
					    promptExeSql = (db: string, sql: string, cancelFunc: any = null, successFunc: any = null) => {
 | 
				
			||||||
        console.log(this);
 | 
					 | 
				
			||||||
        SqlExecBox({
 | 
					        SqlExecBox({
 | 
				
			||||||
            sql,
 | 
					            sql,
 | 
				
			||||||
            dbId: this.id,
 | 
					            dbId: this.id,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -160,10 +160,10 @@
 | 
				
			|||||||
                <el-descriptions-item :span="1.5" label="SSH隧道">{{ infoDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="1.5" label="SSH隧道">{{ infoDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1.5" label="终端回放">{{ infoDialog.data.enableRecorder == 1 ? '是' : '否' }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="1.5" label="终端回放">{{ infoDialog.data.enableRecorder == 1 ? '是' : '否' }} </el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-descriptions-item :span="2" label="创建时间">{{ dateFormat(infoDialog.data.createTime) }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="2" label="创建时间">{{ formatDate(infoDialog.data.createTime) }} </el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-descriptions-item :span="2" label="更新时间">{{ dateFormat(infoDialog.data.updateTime) }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="2" label="更新时间">{{ formatDate(infoDialog.data.updateTime) }} </el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="修改者">{{ infoDialog.data.modifier }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="修改者">{{ infoDialog.data.modifier }}</el-descriptions-item>
 | 
				
			||||||
            </el-descriptions>
 | 
					            </el-descriptions>
 | 
				
			||||||
        </el-dialog>
 | 
					        </el-dialog>
 | 
				
			||||||
@@ -236,12 +236,11 @@ import { defineAsyncComponent, onMounted, reactive, ref, Ref, toRefs } from 'vue
 | 
				
			|||||||
import { useRoute, useRouter } from 'vue-router';
 | 
					import { useRoute, useRouter } from 'vue-router';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { getMachineTerminalSocketUrl, machineApi } from './api';
 | 
					import { getMachineTerminalSocketUrl, machineApi } from './api';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					 | 
				
			||||||
import ResourceTags from '../component/ResourceTags.vue';
 | 
					import ResourceTags from '../component/ResourceTags.vue';
 | 
				
			||||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
					import PageTable from '@/components/pagetable/PageTable.vue';
 | 
				
			||||||
import { TableColumn } from '@/components/pagetable';
 | 
					import { TableColumn } from '@/components/pagetable';
 | 
				
			||||||
import { hasPerms } from '@/components/auth/auth';
 | 
					import { hasPerms } from '@/components/auth/auth';
 | 
				
			||||||
import { formatByteSize } from '@/common/utils/format';
 | 
					import { formatByteSize, formatDate } from '@/common/utils/format';
 | 
				
			||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
import { SearchItem } from '@/components/SearchForm';
 | 
					import { SearchItem } from '@/components/SearchForm';
 | 
				
			||||||
import { getTagPathSearchItem } from '../component/tag';
 | 
					import { getTagPathSearchItem } from '../component/tag';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,10 +103,10 @@
 | 
				
			|||||||
                            <el-descriptions-item :span="1.5" label="SSH隧道">{{ infoDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
 | 
					                            <el-descriptions-item :span="1.5" label="SSH隧道">{{ infoDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
 | 
				
			||||||
                            <el-descriptions-item :span="1.5" label="终端回放">{{ infoDialog.data.enableRecorder == 1 ? '是' : '否' }} </el-descriptions-item>
 | 
					                            <el-descriptions-item :span="1.5" label="终端回放">{{ infoDialog.data.enableRecorder == 1 ? '是' : '否' }} </el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            <el-descriptions-item :span="2" label="创建时间">{{ dateFormat(infoDialog.data.createTime) }} </el-descriptions-item>
 | 
					                            <el-descriptions-item :span="2" label="创建时间">{{ formatDate(infoDialog.data.createTime) }} </el-descriptions-item>
 | 
				
			||||||
                            <el-descriptions-item :span="1" label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
 | 
					                            <el-descriptions-item :span="1" label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            <el-descriptions-item :span="2" label="更新时间">{{ dateFormat(infoDialog.data.updateTime) }} </el-descriptions-item>
 | 
					                            <el-descriptions-item :span="2" label="更新时间">{{ formatDate(infoDialog.data.updateTime) }} </el-descriptions-item>
 | 
				
			||||||
                            <el-descriptions-item :span="1" label="修改者">{{ infoDialog.data.modifier }}</el-descriptions-item>
 | 
					                            <el-descriptions-item :span="1" label="修改者">{{ infoDialog.data.modifier }}</el-descriptions-item>
 | 
				
			||||||
                        </el-descriptions>
 | 
					                        </el-descriptions>
 | 
				
			||||||
                    </el-dialog>
 | 
					                    </el-dialog>
 | 
				
			||||||
@@ -157,7 +157,7 @@
 | 
				
			|||||||
import { defineAsyncComponent, nextTick, onMounted, reactive, ref, toRefs, watch } from 'vue';
 | 
					import { defineAsyncComponent, nextTick, onMounted, reactive, ref, toRefs, watch } from 'vue';
 | 
				
			||||||
import { useRouter } from 'vue-router';
 | 
					import { useRouter } from 'vue-router';
 | 
				
			||||||
import { getMachineTerminalSocketUrl, machineApi } from './api';
 | 
					import { getMachineTerminalSocketUrl, machineApi } from './api';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import { hasPerms } from '@/components/auth/auth';
 | 
					import { hasPerms } from '@/components/auth/auth';
 | 
				
			||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
					import { TagResourceTypeEnum } from '@/common/commonEnum';
 | 
				
			||||||
import { NodeType, TagTreeNode, getTagTypeCodeByPath } from '../component/tag';
 | 
					import { NodeType, TagTreeNode, getTagTypeCodeByPath } from '../component/tag';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@
 | 
				
			|||||||
                <el-table-column prop="cmd" label="命令" show-overflow-tooltip min-width="150px"> </el-table-column>
 | 
					                <el-table-column prop="cmd" label="命令" show-overflow-tooltip min-width="150px"> </el-table-column>
 | 
				
			||||||
                <el-table-column prop="time" label="执行时间" min-width="80" show-overflow-tooltip>
 | 
					                <el-table-column prop="time" label="执行时间" min-width="80" show-overflow-tooltip>
 | 
				
			||||||
                    <template #default="scope">
 | 
					                    <template #default="scope">
 | 
				
			||||||
                        {{ dateFormat(new Date(scope.row.time * 1000).toString()) }}
 | 
					                        {{ formatDate(new Date(scope.row.time * 1000).toString()) }}
 | 
				
			||||||
                    </template>
 | 
					                    </template>
 | 
				
			||||||
                </el-table-column>
 | 
					                </el-table-column>
 | 
				
			||||||
            </el-table>
 | 
					            </el-table>
 | 
				
			||||||
@@ -48,7 +48,7 @@ import * as AsciinemaPlayer from 'asciinema-player';
 | 
				
			|||||||
import 'asciinema-player/dist/bundle/asciinema-player.css';
 | 
					import 'asciinema-player/dist/bundle/asciinema-player.css';
 | 
				
			||||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
					import PageTable from '@/components/pagetable/PageTable.vue';
 | 
				
			||||||
import { TableColumn } from '@/components/pagetable';
 | 
					import { TableColumn } from '@/components/pagetable';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps({
 | 
				
			||||||
    visible: { type: Boolean },
 | 
					    visible: { type: Boolean },
 | 
				
			||||||
@@ -122,8 +122,8 @@ const playRec = async (rec: any) => {
 | 
				
			|||||||
                idleTimeLimit: 2,
 | 
					                idleTimeLimit: 2,
 | 
				
			||||||
                // fit: false,
 | 
					                // fit: false,
 | 
				
			||||||
                // terminalFontSize: 'small',
 | 
					                // terminalFontSize: 'small',
 | 
				
			||||||
                cols: 144,
 | 
					                // cols: 144,
 | 
				
			||||||
                rows: 32,
 | 
					                // rows: 32,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -130,10 +130,10 @@
 | 
				
			|||||||
                <el-descriptions-item :span="3" label="备注">{{ detailDialog.data.remark }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="3" label="备注">{{ detailDialog.data.remark }}</el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="3" label="SSH隧道">{{ detailDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="3" label="SSH隧道">{{ detailDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-descriptions-item :span="2" label="创建时间">{{ dateFormat(detailDialog.data.createTime) }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="2" label="创建时间">{{ formatDate(detailDialog.data.createTime) }} </el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="创建者">{{ detailDialog.data.creator }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="创建者">{{ detailDialog.data.creator }}</el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-descriptions-item :span="2" label="更新时间">{{ dateFormat(detailDialog.data.updateTime) }} </el-descriptions-item>
 | 
					                <el-descriptions-item :span="2" label="更新时间">{{ formatDate(detailDialog.data.updateTime) }} </el-descriptions-item>
 | 
				
			||||||
                <el-descriptions-item :span="1" label="修改者">{{ detailDialog.data.modifier }}</el-descriptions-item>
 | 
					                <el-descriptions-item :span="1" label="修改者">{{ detailDialog.data.modifier }}</el-descriptions-item>
 | 
				
			||||||
            </el-descriptions>
 | 
					            </el-descriptions>
 | 
				
			||||||
        </el-dialog>
 | 
					        </el-dialog>
 | 
				
			||||||
@@ -153,7 +153,7 @@ import { redisApi } from './api';
 | 
				
			|||||||
import { onMounted, reactive, ref, Ref, toRefs } from 'vue';
 | 
					import { onMounted, reactive, ref, Ref, toRefs } from 'vue';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import RedisEdit from './RedisEdit.vue';
 | 
					import RedisEdit from './RedisEdit.vue';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import ResourceTags from '../component/ResourceTags.vue';
 | 
					import ResourceTags from '../component/ResourceTags.vue';
 | 
				
			||||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
					import PageTable from '@/components/pagetable/PageTable.vue';
 | 
				
			||||||
import { TableColumn } from '@/components/pagetable';
 | 
					import { TableColumn } from '@/components/pagetable';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -82,9 +82,9 @@
 | 
				
			|||||||
                                <el-descriptions-item label="备注">{{ currentTag.remark }}</el-descriptions-item>
 | 
					                                <el-descriptions-item label="备注">{{ currentTag.remark }}</el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                <el-descriptions-item label="创建者">{{ currentTag.creator }}</el-descriptions-item>
 | 
					                                <el-descriptions-item label="创建者">{{ currentTag.creator }}</el-descriptions-item>
 | 
				
			||||||
                                <el-descriptions-item label="创建时间">{{ dateFormat(currentTag.createTime) }}</el-descriptions-item>
 | 
					                                <el-descriptions-item label="创建时间">{{ formatDate(currentTag.createTime) }}</el-descriptions-item>
 | 
				
			||||||
                                <el-descriptions-item label="修改者">{{ currentTag.modifier }}</el-descriptions-item>
 | 
					                                <el-descriptions-item label="修改者">{{ currentTag.modifier }}</el-descriptions-item>
 | 
				
			||||||
                                <el-descriptions-item label="更新时间">{{ dateFormat(currentTag.updateTime) }}</el-descriptions-item>
 | 
					                                <el-descriptions-item label="更新时间">{{ formatDate(currentTag.updateTime) }}</el-descriptions-item>
 | 
				
			||||||
                            </el-descriptions>
 | 
					                            </el-descriptions>
 | 
				
			||||||
                        </el-tab-pane>
 | 
					                        </el-tab-pane>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -148,7 +148,7 @@
 | 
				
			|||||||
import { toRefs, ref, watch, reactive, onMounted, Ref, defineAsyncComponent } from 'vue';
 | 
					import { toRefs, ref, watch, reactive, onMounted, Ref, defineAsyncComponent } from 'vue';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { tagApi } from './api';
 | 
					import { tagApi } from './api';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import { Contextmenu, ContextmenuItem } from '@/components/contextmenu/index';
 | 
					import { Contextmenu, ContextmenuItem } from '@/components/contextmenu/index';
 | 
				
			||||||
import { useUserInfo } from '@/store/userInfo';
 | 
					import { useUserInfo } from '@/store/userInfo';
 | 
				
			||||||
import { Splitpanes, Pane } from 'splitpanes';
 | 
					import { Splitpanes, Pane } from 'splitpanes';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,8 @@
 | 
				
			|||||||
                <TagCodePath :path="data.tags?.map((tag: any) => tag.codePath)" />
 | 
					                <TagCodePath :path="data.tags?.map((tag: any) => tag.codePath)" />
 | 
				
			||||||
            </template>
 | 
					            </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <template #validityDate="{ data }"> {{ data.validityStartDate }} ~ {{ data.validityEndDate }} </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <template #action="{ data }">
 | 
					            <template #action="{ data }">
 | 
				
			||||||
                <el-button @click.prevent="showMembers(data)" link type="primary">成员</el-button>
 | 
					                <el-button @click.prevent="showMembers(data)" link type="primary">成员</el-button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -40,12 +42,26 @@
 | 
				
			|||||||
                <el-form-item prop="name" label="团队名" required>
 | 
					                <el-form-item prop="name" label="团队名" required>
 | 
				
			||||||
                    <el-input :disabled="addTeamDialog.form.id" v-model="addTeamDialog.form.name" auto-complete="off"></el-input>
 | 
					                    <el-input :disabled="addTeamDialog.form.id" v-model="addTeamDialog.form.name" auto-complete="off"></el-input>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <el-form-item prop="validityDate" label="生效时间" required>
 | 
				
			||||||
 | 
					                    <el-date-picker
 | 
				
			||||||
 | 
					                        v-model="addTeamDialog.form.validityDate"
 | 
				
			||||||
 | 
					                        type="datetimerange"
 | 
				
			||||||
 | 
					                        start-placeholder="生效开始时间"
 | 
				
			||||||
 | 
					                        end-placeholder="生效结束时间"
 | 
				
			||||||
 | 
					                        format="YYYY-MM-DD HH:mm:ss"
 | 
				
			||||||
 | 
					                        value-format="YYYY-MM-DD HH:mm:ss"
 | 
				
			||||||
 | 
					                        date-format="YYYY-MM-DD"
 | 
				
			||||||
 | 
					                        time-format="HH:mm:ss"
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-form-item label="备注">
 | 
					                <el-form-item label="备注">
 | 
				
			||||||
                    <el-input v-model="addTeamDialog.form.remark" auto-complete="off"></el-input>
 | 
					                    <el-input v-model="addTeamDialog.form.remark" auto-complete="off"></el-input>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <el-form-item prop="tag" label="标签">
 | 
					                <el-form-item prop="tag" label="标签">
 | 
				
			||||||
                    <TagTreeCheck v-model="state.addTeamDialog.form.codePaths" :tag-type="0" />
 | 
					                    <TagTreeCheck height="calc(100vh - 390px)" v-model="state.addTeamDialog.form.codePaths" :tag-type="0" />
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
            </el-form>
 | 
					            </el-form>
 | 
				
			||||||
            <template #footer>
 | 
					            <template #footer>
 | 
				
			||||||
@@ -101,6 +117,7 @@ import AccountSelectFormItem from '@/views/system/account/components/AccountSele
 | 
				
			|||||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
 | 
					import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
 | 
				
			||||||
import TagTreeCheck from '../component/TagTreeCheck.vue';
 | 
					import TagTreeCheck from '../component/TagTreeCheck.vue';
 | 
				
			||||||
import TagCodePath from '../component/TagCodePath.vue';
 | 
					import TagCodePath from '../component/TagCodePath.vue';
 | 
				
			||||||
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const teamForm: any = ref(null);
 | 
					const teamForm: any = ref(null);
 | 
				
			||||||
const pageTableRef: Ref<any> = ref(null);
 | 
					const pageTableRef: Ref<any> = ref(null);
 | 
				
			||||||
@@ -114,13 +131,21 @@ const teamFormRules = {
 | 
				
			|||||||
            trigger: ['change', 'blur'],
 | 
					            trigger: ['change', 'blur'],
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
					    validityDate: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            required: true,
 | 
				
			||||||
 | 
					            message: '请选择生效时间',
 | 
				
			||||||
 | 
					            trigger: ['change', 'blur'],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const searchItems = [SearchItem.input('name', '团队名称')];
 | 
					const searchItems = [SearchItem.input('name', '团队名称')];
 | 
				
			||||||
const columns = [
 | 
					const columns = [
 | 
				
			||||||
    TableColumn.new('name', '团队名称'),
 | 
					    TableColumn.new('name', '团队名称'),
 | 
				
			||||||
    TableColumn.new('remark', '备注'),
 | 
					 | 
				
			||||||
    TableColumn.new('tags', '分配标签').isSlot().setAddWidth(40),
 | 
					    TableColumn.new('tags', '分配标签').isSlot().setAddWidth(40),
 | 
				
			||||||
 | 
					    TableColumn.new('validityDate', '有效期').isSlot('validityDate').setMinWidth(310),
 | 
				
			||||||
 | 
					    TableColumn.new('remark', '备注'),
 | 
				
			||||||
    TableColumn.new('creator', '创建者'),
 | 
					    TableColumn.new('creator', '创建者'),
 | 
				
			||||||
    TableColumn.new('createTime', '创建时间').isTime(),
 | 
					    TableColumn.new('createTime', '创建时间').isTime(),
 | 
				
			||||||
    TableColumn.new('modifier', '修改者'),
 | 
					    TableColumn.new('modifier', '修改者'),
 | 
				
			||||||
@@ -132,7 +157,7 @@ const state = reactive({
 | 
				
			|||||||
    currentEditPermissions: false,
 | 
					    currentEditPermissions: false,
 | 
				
			||||||
    addTeamDialog: {
 | 
					    addTeamDialog: {
 | 
				
			||||||
        visible: false,
 | 
					        visible: false,
 | 
				
			||||||
        form: { id: 0, name: '', remark: '', codePaths: [] },
 | 
					        form: { id: 0, name: '', validityDate: ['', ''], validityStartDate: '', validityEndDate: '', remark: '', codePaths: [] },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    query: {
 | 
					    query: {
 | 
				
			||||||
        pageNum: 1,
 | 
					        pageNum: 1,
 | 
				
			||||||
@@ -182,24 +207,33 @@ const showSaveTeamDialog = async (data: any) => {
 | 
				
			|||||||
    if (data) {
 | 
					    if (data) {
 | 
				
			||||||
        state.addTeamDialog.form.id = data.id;
 | 
					        state.addTeamDialog.form.id = data.id;
 | 
				
			||||||
        state.addTeamDialog.form.name = data.name;
 | 
					        state.addTeamDialog.form.name = data.name;
 | 
				
			||||||
 | 
					        state.addTeamDialog.form.validityDate = [data.validityStartDate, data.validityEndDate];
 | 
				
			||||||
        state.addTeamDialog.form.remark = data.remark;
 | 
					        state.addTeamDialog.form.remark = data.remark;
 | 
				
			||||||
        state.addTeamDialog.form.codePaths = data.tags?.map((tag: any) => tag.codePath);
 | 
					        state.addTeamDialog.form.codePaths = data.tags?.map((tag: any) => tag.codePath);
 | 
				
			||||||
        // state.addTeamDialog.form.tags = await tagApi.getRelateTagIds.request({ relateType: TagTreeRelateTypeEnum.Team.value, relateId: data.id });
 | 
					    } else {
 | 
				
			||||||
 | 
					        let end = new Date();
 | 
				
			||||||
 | 
					        end.setFullYear(end.getFullYear() + 10);
 | 
				
			||||||
 | 
					        state.addTeamDialog.form.validityDate = [formatDate(new Date()), formatDate(end)];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    state.addTeamDialog.visible = true;
 | 
					    state.addTeamDialog.visible = true;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const saveTeam = async () => {
 | 
					const saveTeam = async () => {
 | 
				
			||||||
    teamForm.value.validate(async (valid: any) => {
 | 
					    try {
 | 
				
			||||||
        if (valid) {
 | 
					        await teamForm.value.validate();
 | 
				
			||||||
            const form = state.addTeamDialog.form;
 | 
					    } catch (e: any) {
 | 
				
			||||||
            await tagApi.saveTeam.request(form);
 | 
					        ElMessage.error('请正确填写信息');
 | 
				
			||||||
            ElMessage.success('保存成功');
 | 
					        return false;
 | 
				
			||||||
            search();
 | 
					    }
 | 
				
			||||||
            cancelSaveTeam();
 | 
					
 | 
				
			||||||
        }
 | 
					    const form = state.addTeamDialog.form;
 | 
				
			||||||
    });
 | 
					    form.validityStartDate = form.validityDate[0];
 | 
				
			||||||
 | 
					    form.validityEndDate = form.validityDate[1];
 | 
				
			||||||
 | 
					    await tagApi.saveTeam.request(form);
 | 
				
			||||||
 | 
					    ElMessage.success('保存成功');
 | 
				
			||||||
 | 
					    search();
 | 
				
			||||||
 | 
					    cancelSaveTeam();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cancelSaveTeam = () => {
 | 
					const cancelSaveTeam = () => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,7 +42,7 @@
 | 
				
			|||||||
                <el-table-column property="creator" label="分配账号" width="125"></el-table-column>
 | 
					                <el-table-column property="creator" label="分配账号" width="125"></el-table-column>
 | 
				
			||||||
                <el-table-column property="createTime" label="分配时间">
 | 
					                <el-table-column property="createTime" label="分配时间">
 | 
				
			||||||
                    <template #default="scope">
 | 
					                    <template #default="scope">
 | 
				
			||||||
                        {{ dateFormat(scope.row.createTime) }}
 | 
					                        {{ formatDate(scope.row.createTime) }}
 | 
				
			||||||
                    </template>
 | 
					                    </template>
 | 
				
			||||||
                </el-table-column>
 | 
					                </el-table-column>
 | 
				
			||||||
            </el-table>
 | 
					            </el-table>
 | 
				
			||||||
@@ -60,7 +60,7 @@ import AccountEdit from './AccountEdit.vue';
 | 
				
			|||||||
import { AccountStatusEnum } from '../enums';
 | 
					import { AccountStatusEnum } from '../enums';
 | 
				
			||||||
import { accountApi } from '../api';
 | 
					import { accountApi } from '../api';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import PageTable from '@/components/pagetable/PageTable.vue';
 | 
					import PageTable from '@/components/pagetable/PageTable.vue';
 | 
				
			||||||
import { TableColumn } from '@/components/pagetable';
 | 
					import { TableColumn } from '@/components/pagetable';
 | 
				
			||||||
import { hasPerms } from '@/components/auth/auth';
 | 
					import { hasPerms } from '@/components/auth/auth';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -89,9 +89,9 @@
 | 
				
			|||||||
                                </el-descriptions-item>
 | 
					                                </el-descriptions-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                <el-descriptions-item label="创建者">{{ currentResource.creator }}</el-descriptions-item>
 | 
					                                <el-descriptions-item label="创建者">{{ currentResource.creator }}</el-descriptions-item>
 | 
				
			||||||
                                <el-descriptions-item label="创建时间">{{ dateFormat(currentResource.createTime) }} </el-descriptions-item>
 | 
					                                <el-descriptions-item label="创建时间">{{ formatDate(currentResource.createTime) }} </el-descriptions-item>
 | 
				
			||||||
                                <el-descriptions-item label="修改者">{{ currentResource.modifier }}</el-descriptions-item>
 | 
					                                <el-descriptions-item label="修改者">{{ currentResource.modifier }}</el-descriptions-item>
 | 
				
			||||||
                                <el-descriptions-item label="更新时间">{{ dateFormat(currentResource.updateTime) }} </el-descriptions-item>
 | 
					                                <el-descriptions-item label="更新时间">{{ formatDate(currentResource.updateTime) }} </el-descriptions-item>
 | 
				
			||||||
                            </el-descriptions>
 | 
					                            </el-descriptions>
 | 
				
			||||||
                        </el-tab-pane>
 | 
					                        </el-tab-pane>
 | 
				
			||||||
                    </el-tabs>
 | 
					                    </el-tabs>
 | 
				
			||||||
@@ -119,7 +119,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			|||||||
import ResourceEdit from './ResourceEdit.vue';
 | 
					import ResourceEdit from './ResourceEdit.vue';
 | 
				
			||||||
import { ResourceTypeEnum } from '../enums';
 | 
					import { ResourceTypeEnum } from '../enums';
 | 
				
			||||||
import { resourceApi } from '../api';
 | 
					import { resourceApi } from '../api';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
 | 
					import EnumTag from '@/components/enumtag/EnumTag.vue';
 | 
				
			||||||
import { Contextmenu, ContextmenuItem } from '@/components/contextmenu';
 | 
					import { Contextmenu, ContextmenuItem } from '@/components/contextmenu';
 | 
				
			||||||
import { Splitpanes, Pane } from 'splitpanes';
 | 
					import { Splitpanes, Pane } from 'splitpanes';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@
 | 
				
			|||||||
                                        {{ data.creator }}
 | 
					                                        {{ data.creator }}
 | 
				
			||||||
                                    </el-descriptions-item>
 | 
					                                    </el-descriptions-item>
 | 
				
			||||||
                                    <el-descriptions-item label="分配时间">
 | 
					                                    <el-descriptions-item label="分配时间">
 | 
				
			||||||
                                        {{ dateFormat(data.createTime) }}
 | 
					                                        {{ formatDate(data.createTime) }}
 | 
				
			||||||
                                    </el-descriptions-item>
 | 
					                                    </el-descriptions-item>
 | 
				
			||||||
                                </el-descriptions>
 | 
					                                </el-descriptions>
 | 
				
			||||||
                            </template>
 | 
					                            </template>
 | 
				
			||||||
@@ -35,7 +35,7 @@
 | 
				
			|||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { toRefs, reactive, watch } from 'vue';
 | 
					import { toRefs, reactive, watch } from 'vue';
 | 
				
			||||||
import { ResourceTypeEnum } from '../enums';
 | 
					import { ResourceTypeEnum } from '../enums';
 | 
				
			||||||
import { dateFormat } from '@/common/utils/date';
 | 
					import { formatDate } from '@/common/utils/format';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps({
 | 
					const props = defineProps({
 | 
				
			||||||
    visible: {
 | 
					    visible: {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,9 +11,11 @@ server:
 | 
				
			|||||||
    cert-file: ./default.pem
 | 
					    cert-file: ./default.pem
 | 
				
			||||||
jwt:
 | 
					jwt:
 | 
				
			||||||
  # jwt key,不设置默认使用随机字符串
 | 
					  # jwt key,不设置默认使用随机字符串
 | 
				
			||||||
  key: 
 | 
					  key: 333333000000
 | 
				
			||||||
  # 过期时间单位分钟
 | 
					  # accessToken过期时间单位分钟
 | 
				
			||||||
  expire-time: 1440
 | 
					  expire-time: 720
 | 
				
			||||||
 | 
					  # refreshToken过期时间单位分钟
 | 
				
			||||||
 | 
					  refresh-token-expire-time: 4320
 | 
				
			||||||
# 资源密码aes加密key
 | 
					# 资源密码aes加密key
 | 
				
			||||||
aes:
 | 
					aes:
 | 
				
			||||||
  key: 1111111111111111
 | 
					  key: 1111111111111111
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ require (
 | 
				
			|||||||
	github.com/pquerna/otp v1.4.0
 | 
						github.com/pquerna/otp v1.4.0
 | 
				
			||||||
	github.com/redis/go-redis/v9 v9.5.1
 | 
						github.com/redis/go-redis/v9 v9.5.1
 | 
				
			||||||
	github.com/robfig/cron/v3 v3.0.1 // 定时任务
 | 
						github.com/robfig/cron/v3 v3.0.1 // 定时任务
 | 
				
			||||||
	github.com/sijms/go-ora/v2 v2.8.16
 | 
						github.com/sijms/go-ora/v2 v2.8.17
 | 
				
			||||||
	github.com/stretchr/testify v1.9.0
 | 
						github.com/stretchr/testify v1.9.0
 | 
				
			||||||
	github.com/veops/go-ansiterm v0.0.5
 | 
						github.com/veops/go-ansiterm v0.0.5
 | 
				
			||||||
	go.mongodb.org/mongo-driver v1.15.0 // mongo
 | 
						go.mongodb.org/mongo-driver v1.15.0 // mongo
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,6 @@ package api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"mayfly-go/internal/auth/api/form"
 | 
						"mayfly-go/internal/auth/api/form"
 | 
				
			||||||
	"mayfly-go/internal/auth/config"
 | 
						"mayfly-go/internal/auth/config"
 | 
				
			||||||
@@ -71,11 +70,12 @@ func (a *AccountLogin) Login(rc *req.Ctx) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type OtpVerifyInfo struct {
 | 
					type OtpVerifyInfo struct {
 | 
				
			||||||
	AccountId   uint64
 | 
						AccountId    uint64
 | 
				
			||||||
	Username    string
 | 
						Username     string
 | 
				
			||||||
	OptStatus   int
 | 
						OptStatus    int
 | 
				
			||||||
	AccessToken string
 | 
						AccessToken  string
 | 
				
			||||||
	OtpSecret   string
 | 
						RefreshToken string
 | 
				
			||||||
 | 
						OtpSecret    string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// OTP双因素校验
 | 
					// OTP双因素校验
 | 
				
			||||||
@@ -84,10 +84,9 @@ func (a *AccountLogin) OtpVerify(rc *req.Ctx) {
 | 
				
			|||||||
	req.BindJsonAndValid(rc, otpVerify)
 | 
						req.BindJsonAndValid(rc, otpVerify)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tokenKey := fmt.Sprintf("otp:token:%s", otpVerify.OtpToken)
 | 
						tokenKey := fmt.Sprintf("otp:token:%s", otpVerify.OtpToken)
 | 
				
			||||||
	otpInfoJson := cache.GetStr(tokenKey)
 | 
					 | 
				
			||||||
	biz.NotEmpty(otpInfoJson, "otpToken错误或失效, 请重新登陆获取")
 | 
					 | 
				
			||||||
	otpInfo := new(OtpVerifyInfo)
 | 
						otpInfo := new(OtpVerifyInfo)
 | 
				
			||||||
	json.Unmarshal([]byte(otpInfoJson), otpInfo)
 | 
						ok := cache.Get(tokenKey, otpInfo)
 | 
				
			||||||
 | 
						biz.IsTrue(ok, "otpToken错误或失效, 请重新登陆获取")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	failCountKey := fmt.Sprintf("account:otp:failcount:%d", otpInfo.AccountId)
 | 
						failCountKey := fmt.Sprintf("account:otp:failcount:%d", otpInfo.AccountId)
 | 
				
			||||||
	failCount := cache.GetInt(failCountKey)
 | 
						failCount := cache.GetInt(failCountKey)
 | 
				
			||||||
@@ -116,7 +115,20 @@ func (a *AccountLogin) OtpVerify(rc *req.Ctx) {
 | 
				
			|||||||
	go saveLogin(la, getIpAndRegion(rc))
 | 
						go saveLogin(la, getIpAndRegion(rc))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cache.Del(tokenKey)
 | 
						cache.Del(tokenKey)
 | 
				
			||||||
	rc.ResData = accessToken
 | 
						rc.ResData = collx.Kvs("token", accessToken, "refresh_token", otpInfo.RefreshToken)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *AccountLogin) RefreshToken(rc *req.Ctx) {
 | 
				
			||||||
 | 
						refreshToken := rc.Query("refresh_token")
 | 
				
			||||||
 | 
						biz.NotEmpty(refreshToken, "refresh_token不能为空")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						accountId, username, err := req.ParseToken(refreshToken)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(errorx.PermissionErr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						token, refreshToken, err := req.CreateToken(accountId, username)
 | 
				
			||||||
 | 
						biz.ErrIsNil(err)
 | 
				
			||||||
 | 
						rc.ResData = collx.Kvs("token", token, "refresh_token", refreshToken)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *AccountLogin) Logout(rc *req.Ctx) {
 | 
					func (a *AccountLogin) Logout(rc *req.Ctx) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,18 +41,19 @@ func LastLoginCheck(account *sysentity.Account, accountLoginSecurity *config.Acc
 | 
				
			|||||||
	// 默认为不校验otp
 | 
						// 默认为不校验otp
 | 
				
			||||||
	otpStatus := OtpStatusNone
 | 
						otpStatus := OtpStatusNone
 | 
				
			||||||
	// 访问系统使用的token
 | 
						// 访问系统使用的token
 | 
				
			||||||
	accessToken, err := req.CreateToken(account.Id, username)
 | 
						accessToken, refreshToken, err := req.CreateToken(account.Id, username)
 | 
				
			||||||
	biz.ErrIsNilAppendErr(err, "token创建失败: %s")
 | 
						biz.ErrIsNilAppendErr(err, "token创建失败: %s")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 若系统配置中设置开启otp双因素校验,则进行otp校验
 | 
						// 若系统配置中设置开启otp双因素校验,则进行otp校验
 | 
				
			||||||
	if accountLoginSecurity.UseOtp {
 | 
						if accountLoginSecurity.UseOtp {
 | 
				
			||||||
		otpInfo, otpurl, otpToken := useOtp(account, accountLoginSecurity.OtpIssuer, accessToken)
 | 
							otpInfo, otpurl, otpToken := useOtp(account, accountLoginSecurity.OtpIssuer, accessToken, refreshToken)
 | 
				
			||||||
		otpStatus = otpInfo.OptStatus
 | 
							otpStatus = otpInfo.OptStatus
 | 
				
			||||||
		if otpurl != "" {
 | 
							if otpurl != "" {
 | 
				
			||||||
			res["otpUrl"] = otpurl
 | 
								res["otpUrl"] = otpurl
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		accessToken = otpToken
 | 
							accessToken = otpToken
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
 | 
							res["refresh_token"] = refreshToken
 | 
				
			||||||
		// 不进行otp二次校验则直接返回accessToken
 | 
							// 不进行otp二次校验则直接返回accessToken
 | 
				
			||||||
		// 保存登录消息
 | 
							// 保存登录消息
 | 
				
			||||||
		go saveLogin(account, loginIp)
 | 
							go saveLogin(account, loginIp)
 | 
				
			||||||
@@ -64,7 +65,7 @@ func LastLoginCheck(account *sysentity.Account, accountLoginSecurity *config.Acc
 | 
				
			|||||||
	return res
 | 
						return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func useOtp(account *sysentity.Account, otpIssuer, accessToken string) (*OtpVerifyInfo, string, string) {
 | 
					func useOtp(account *sysentity.Account, otpIssuer, accessToken string, refreshToken string) (*OtpVerifyInfo, string, string) {
 | 
				
			||||||
	biz.ErrIsNil(account.OtpSecretDecrypt())
 | 
						biz.ErrIsNil(account.OtpSecretDecrypt())
 | 
				
			||||||
	otpSecret := account.OtpSecret
 | 
						otpSecret := account.OtpSecret
 | 
				
			||||||
	// 修改状态为已注册
 | 
						// 修改状态为已注册
 | 
				
			||||||
@@ -83,13 +84,14 @@ func useOtp(account *sysentity.Account, otpIssuer, accessToken string) (*OtpVeri
 | 
				
			|||||||
		otpUrl = key.URL()
 | 
							otpUrl = key.URL()
 | 
				
			||||||
		otpSecret = key.Secret()
 | 
							otpSecret = key.Secret()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// 缓存otpInfo, 只有双因素校验通过才可返回真正的accessToken
 | 
						// 缓存otpInfo, 只有双因素校验通过才可返回真正的token
 | 
				
			||||||
	otpInfo := &OtpVerifyInfo{
 | 
						otpInfo := &OtpVerifyInfo{
 | 
				
			||||||
		AccountId:   account.Id,
 | 
							AccountId:    account.Id,
 | 
				
			||||||
		Username:    account.Username,
 | 
							Username:     account.Username,
 | 
				
			||||||
		OptStatus:   otpStatus,
 | 
							OptStatus:    otpStatus,
 | 
				
			||||||
		OtpSecret:   otpSecret,
 | 
							OtpSecret:    otpSecret,
 | 
				
			||||||
		AccessToken: accessToken,
 | 
							AccessToken:  accessToken,
 | 
				
			||||||
 | 
							RefreshToken: refreshToken,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cache.SetStr(fmt.Sprintf("otp:token:%s", token), jsonx.ToStr(otpInfo), time.Minute*time.Duration(3))
 | 
						cache.SetStr(fmt.Sprintf("otp:token:%s", token), jsonx.ToStr(otpInfo), time.Minute*time.Duration(3))
 | 
				
			||||||
	return otpInfo, otpUrl, token
 | 
						return otpInfo, otpUrl, token
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,8 @@ func Init(router *gin.RouterGroup) {
 | 
				
			|||||||
		// 用户账号密码登录
 | 
							// 用户账号密码登录
 | 
				
			||||||
		req.NewPost("/accounts/login", accountLogin.Login).Log(req.NewLogSave("用户登录")).DontNeedToken(),
 | 
							req.NewPost("/accounts/login", accountLogin.Login).Log(req.NewLogSave("用户登录")).DontNeedToken(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req.NewGet("/accounts/refreshToken", accountLogin.RefreshToken).DontNeedToken(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 用户退出登录
 | 
							// 用户退出登录
 | 
				
			||||||
		req.NewPost("/accounts/logout", accountLogin.Logout),
 | 
							req.NewPost("/accounts/logout", accountLogin.Logout),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,8 +10,10 @@ type Team struct {
 | 
				
			|||||||
	model.Model
 | 
						model.Model
 | 
				
			||||||
	entity.RelateTags // 标签信息
 | 
						entity.RelateTags // 标签信息
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Name   string `json:"name"`   // 名称
 | 
						Name              string          `json:"name"`              // 名称
 | 
				
			||||||
	Remark string `json:"remark"` // 备注说明
 | 
						ValidityStartDate *model.JsonTime `json:"validityStartDate"` // 生效开始时间
 | 
				
			||||||
 | 
						ValidityEndDate   *model.JsonTime `json:"validityEndDate"`   // 生效结束时间
 | 
				
			||||||
 | 
						Remark            string          `json:"remark"`            // 备注说明
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *Team) GetRelateId() uint64 {
 | 
					func (t *Team) GetRelateId() uint64 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,13 @@
 | 
				
			|||||||
package dto
 | 
					package dto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "mayfly-go/pkg/model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type SaveTeam struct {
 | 
					type SaveTeam struct {
 | 
				
			||||||
	Id     uint64 `json:"id"`
 | 
						Id                uint64          `json:"id"`
 | 
				
			||||||
	Name   string `json:"name" binding:"required"` // 名称
 | 
						Name              string          `json:"name" binding:"required"` // 名称
 | 
				
			||||||
	Remark string `json:"remark"`                  // 备注说明
 | 
						ValidityStartDate *model.JsonTime `json:"validityStartDate"`       // 生效开始时间
 | 
				
			||||||
 | 
						ValidityEndDate   *model.JsonTime `json:"validityEndDate"`         // 生效结束时间
 | 
				
			||||||
 | 
						Remark            string          `json:"remark"`                  // 备注说明
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CodePaths []string `json:"codePaths"` // 关联标签信息
 | 
						CodePaths []string `json:"codePaths"` // 关联标签信息
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,7 +56,12 @@ func (p *teamAppImpl) GetPageList(condition *entity.TeamQuery, pageParam *model.
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *teamAppImpl) SaveTeam(ctx context.Context, saveParam *dto.SaveTeam) error {
 | 
					func (p *teamAppImpl) SaveTeam(ctx context.Context, saveParam *dto.SaveTeam) error {
 | 
				
			||||||
	team := &entity.Team{Name: saveParam.Name, Remark: saveParam.Remark}
 | 
						team := &entity.Team{
 | 
				
			||||||
 | 
							Name:              saveParam.Name,
 | 
				
			||||||
 | 
							ValidityStartDate: saveParam.ValidityStartDate,
 | 
				
			||||||
 | 
							ValidityEndDate:   saveParam.ValidityEndDate,
 | 
				
			||||||
 | 
							Remark:            saveParam.Remark,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	team.Id = saveParam.Id
 | 
						team.Id = saveParam.Id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if team.Id == 0 {
 | 
						if team.Id == 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,15 @@
 | 
				
			|||||||
package entity
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "mayfly-go/pkg/model"
 | 
					import (
 | 
				
			||||||
 | 
						"mayfly-go/pkg/model"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 团队信息
 | 
					// 团队信息
 | 
				
			||||||
type Team struct {
 | 
					type Team struct {
 | 
				
			||||||
	model.Model
 | 
						model.Model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Name   string `json:"name"`   // 名称
 | 
						Name              string          `json:"name"`              // 名称
 | 
				
			||||||
	Remark string `json:"remark"` // 备注说明
 | 
						ValidityStartDate *model.JsonTime `json:"validityStartDate"` // 生效开始时间
 | 
				
			||||||
 | 
						ValidityEndDate   *model.JsonTime `json:"validityEndDate"`   // 生效结束时间
 | 
				
			||||||
 | 
						Remark            string          `json:"remark"`            // 备注说明
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ import (
 | 
				
			|||||||
const AccountTagsKey = "mayfly:tag:account:%d"
 | 
					const AccountTagsKey = "mayfly:tag:account:%d"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func SaveAccountTagPaths(accountId uint64, tags []string) error {
 | 
					func SaveAccountTagPaths(accountId uint64, tags []string) error {
 | 
				
			||||||
	return global_cache.Set(fmt.Sprintf(AccountTagsKey, accountId), tags, 30*time.Minute)
 | 
						return global_cache.Set(fmt.Sprintf(AccountTagsKey, accountId), tags, 2*time.Minute)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetAccountTagPaths(accountId uint64) ([]string, error) {
 | 
					func GetAccountTagPaths(accountId uint64) ([]string, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"mayfly-go/internal/tag/domain/entity"
 | 
						"mayfly-go/internal/tag/domain/entity"
 | 
				
			||||||
	"mayfly-go/internal/tag/domain/repository"
 | 
						"mayfly-go/internal/tag/domain/repository"
 | 
				
			||||||
	"mayfly-go/pkg/base"
 | 
						"mayfly-go/pkg/base"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type tagTreeRelateRepoImpl struct {
 | 
					type tagTreeRelateRepoImpl struct {
 | 
				
			||||||
@@ -43,12 +44,10 @@ func (tr *tagTreeRelateRepoImpl) SelectTagPathsByAccountId(accountId uint64) []s
 | 
				
			|||||||
	sql := `
 | 
						sql := `
 | 
				
			||||||
SELECT
 | 
					SELECT
 | 
				
			||||||
	DISTINCT(t.code_path)
 | 
						DISTINCT(t.code_path)
 | 
				
			||||||
FROM
 | 
					FROM t_tag_tree_relate t1
 | 
				
			||||||
	t_tag_tree_relate t1
 | 
					JOIN t_team_member t2 ON t1.relate_id = t2.team_id
 | 
				
			||||||
JOIN t_team_member t2 ON
 | 
					JOIN t_team t3 ON t3.id = t2.team_id AND t3.validity_start_date < ? AND t3.validity_end_date > ?
 | 
				
			||||||
	t1.relate_id = t2.team_id
 | 
					JOIN t_tag_tree t ON t.id = t1.tag_id
 | 
				
			||||||
JOIN t_tag_tree t ON
 | 
					 | 
				
			||||||
	t.id = t1.tag_id
 | 
					 | 
				
			||||||
WHERE
 | 
					WHERE
 | 
				
			||||||
	t1.relate_type = ?
 | 
						t1.relate_type = ?
 | 
				
			||||||
	AND t2.account_id = ?
 | 
						AND t2.account_id = ?
 | 
				
			||||||
@@ -58,7 +57,8 @@ WHERE
 | 
				
			|||||||
ORDER BY
 | 
					ORDER BY
 | 
				
			||||||
	t.code_path
 | 
						t.code_path
 | 
				
			||||||
	`
 | 
						`
 | 
				
			||||||
	tr.SelectBySql(sql, &res, entity.TagRelateTypeTeam, accountId)
 | 
						now := time.Now()
 | 
				
			||||||
 | 
						tr.SelectBySql(sql, &res, now, now, entity.TagRelateTypeTeam, accountId)
 | 
				
			||||||
	return res
 | 
						return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import "fmt"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	AppName = "mayfly-go"
 | 
						AppName = "mayfly-go"
 | 
				
			||||||
	Version = "v1.8.3"
 | 
						Version = "v1.8.4"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetAppInfo() string {
 | 
					func GetAppInfo() string {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,8 +7,9 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Jwt struct {
 | 
					type Jwt struct {
 | 
				
			||||||
	Key        string `yaml:"key"`
 | 
						Key                    string `yaml:"key"`
 | 
				
			||||||
	ExpireTime uint64 `yaml:"expire-time"` // 过期时间,单位分钟
 | 
						ExpireTime             uint64 `yaml:"expire-time"`               // 过期时间,单位分钟
 | 
				
			||||||
 | 
						RefreshTokenExpireTime uint64 `yaml:"refresh-token-expire-time"` // 刷新token的过期时间,单位分钟
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (j *Jwt) Default() {
 | 
					func (j *Jwt) Default() {
 | 
				
			||||||
@@ -22,6 +23,10 @@ func (j *Jwt) Default() {
 | 
				
			|||||||
		j.ExpireTime = 1440
 | 
							j.ExpireTime = 1440
 | 
				
			||||||
		logx.Warnf("未配置jwt.expire-time, 默认值: %d", j.ExpireTime)
 | 
							logx.Warnf("未配置jwt.expire-time, 默认值: %d", j.ExpireTime)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if j.RefreshTokenExpireTime == 0 {
 | 
				
			||||||
 | 
							j.RefreshTokenExpireTime = j.ExpireTime * 5
 | 
				
			||||||
 | 
							logx.Warnf("未配置jwt.refresh-token-expire-time, 默认值: %d", j.RefreshTokenExpireTime)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (j *Jwt) Valid() {
 | 
					func (j *Jwt) Valid() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,10 +11,11 @@ type BizError struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	Success       BizError = NewBizCode(200, "success")
 | 
						Success            BizError = NewBizCode(200, "success")
 | 
				
			||||||
	BizErr        BizError = NewBizCode(400, "biz error")
 | 
						BizErr             BizError = NewBizCode(400, "biz error")
 | 
				
			||||||
	ServerError   BizError = NewBizCode(500, "server error")
 | 
						ServerError        BizError = NewBizCode(500, "server error")
 | 
				
			||||||
	PermissionErr BizError = NewBizCode(501, "token error")
 | 
						PermissionErr      BizError = NewBizCode(501, "token error")
 | 
				
			||||||
 | 
						AccessTokenInvalid BizError = NewBizCode(502, "access token invalid")
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 错误消息
 | 
					// 错误消息
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,7 +31,8 @@ func (j JsonTime) MarshalJSON() ([]byte, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (j *JsonTime) UnmarshalJSON(b []byte) error {
 | 
					func (j *JsonTime) UnmarshalJSON(b []byte) error {
 | 
				
			||||||
	s := strings.ReplaceAll(string(b), "\"", "")
 | 
						s := strings.ReplaceAll(string(b), "\"", "")
 | 
				
			||||||
	t, err := time.Parse(timex.DefaultDateTimeFormat, s)
 | 
						// t, err := time.Parse(timex.DefaultDateTimeFormat, s)
 | 
				
			||||||
 | 
						t, err := time.ParseInLocation(timex.DefaultDateTimeFormat, s, time.Local)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,7 +56,7 @@ func PermissionHandler(rc *Ctx) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	userId, userName, err := ParseToken(tokenStr)
 | 
						userId, userName, err := ParseToken(tokenStr)
 | 
				
			||||||
	if err != nil || userId == 0 {
 | 
						if err != nil || userId == 0 {
 | 
				
			||||||
		return errorx.PermissionErr
 | 
							return errorx.AccessTokenInvalid
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// 权限不为nil,并且permission code不为空,则校验是否有权限code
 | 
						// 权限不为nil,并且permission code不为空,则校验是否有权限code
 | 
				
			||||||
	if permission != nil && permission.Code != "" {
 | 
						if permission != nil && permission.Code != "" {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,21 +9,33 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 创建用户token
 | 
					// 创建用户token
 | 
				
			||||||
func CreateToken(userId uint64, username string) (string, error) {
 | 
					func CreateToken(userId uint64, username string) (accessToken string, refreshToken string, err error) {
 | 
				
			||||||
 | 
						jwtConf := config.Conf.Jwt
 | 
				
			||||||
 | 
						now := time.Now()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 带权限创建令牌
 | 
						// 带权限创建令牌
 | 
				
			||||||
	// 设置有效期,过期需要重新登录获取token
 | 
						// 设置有效期,过期需要重新登录获取token
 | 
				
			||||||
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
 | 
						accessJwt := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
 | 
				
			||||||
		"id":       userId,
 | 
							"id":       userId,
 | 
				
			||||||
		"username": username,
 | 
							"username": username,
 | 
				
			||||||
		"exp":      time.Now().Add(time.Minute * time.Duration(config.Conf.Jwt.ExpireTime)).Unix(),
 | 
							"exp":      now.Add(time.Minute * time.Duration(jwtConf.ExpireTime)).Unix(),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// refresh token
 | 
				
			||||||
 | 
						refreshJwt := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
 | 
				
			||||||
 | 
							"id":       userId,
 | 
				
			||||||
 | 
							"username": username,
 | 
				
			||||||
 | 
							"exp":      now.Add(time.Minute * time.Duration(jwtConf.RefreshTokenExpireTime)).Unix(),
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 使用自定义字符串加密 and get the complete encoded token as a string
 | 
						// 使用自定义字符串加密 and get the complete encoded token as a string
 | 
				
			||||||
	tokenString, err := token.SignedString([]byte(config.Conf.Jwt.Key))
 | 
						accessToken, err = accessJwt.SignedString([]byte(jwtConf.Key))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return tokenString, nil
 | 
					
 | 
				
			||||||
 | 
						refreshToken, err = refreshJwt.SignedString([]byte(jwtConf.Key))
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 解析token,并返回登录者账号信息
 | 
					// 解析token,并返回登录者账号信息
 | 
				
			||||||
 
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							@@ -951,6 +951,8 @@ DROP TABLE IF EXISTS `t_team`;
 | 
				
			|||||||
CREATE TABLE `t_team` (
 | 
					CREATE TABLE `t_team` (
 | 
				
			||||||
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
 | 
					  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
 | 
				
			||||||
  `name` varchar(36) NOT NULL COMMENT '名称',
 | 
					  `name` varchar(36) NOT NULL COMMENT '名称',
 | 
				
			||||||
 | 
					  `validity_start_date` datetime DEFAULT NULL COMMENT '生效开始时间',
 | 
				
			||||||
 | 
					  `validity_end_date` datetime DEFAULT NULL COMMENT '生效结束时间',
 | 
				
			||||||
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
 | 
					  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
 | 
				
			||||||
  `create_time` datetime NOT NULL,
 | 
					  `create_time` datetime NOT NULL,
 | 
				
			||||||
  `creator_id` bigint(20) NOT NULL,
 | 
					  `creator_id` bigint(20) NOT NULL,
 | 
				
			||||||
@@ -967,7 +969,7 @@ CREATE TABLE `t_team` (
 | 
				
			|||||||
-- Records of t_team
 | 
					-- Records of t_team
 | 
				
			||||||
-- ----------------------------
 | 
					-- ----------------------------
 | 
				
			||||||
BEGIN;
 | 
					BEGIN;
 | 
				
			||||||
INSERT INTO `t_team` VALUES (1, 'default_team', '默认团队', '2022-10-26 20:04:36', 1, 'admin', '2022-10-26 20:04:36', 1, 'admin', 0, NULL);
 | 
					INSERT INTO `t_team` VALUES (1, 'default_team', '2024-05-01 00:00:00', '2050-05-01 00:00:00', '默认团队', '2022-10-26 20:04:36', 1, 'admin', '2022-10-26 20:04:36', 1, 'admin', 0, NULL);
 | 
				
			||||||
COMMIT;
 | 
					COMMIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- ----------------------------
 | 
					-- ----------------------------
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								server/resources/script/sql/v1.8/v1.8.4.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								server/resources/script/sql/v1.8/v1.8.4.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					ALTER TABLE t_team ADD validity_start_date DATETIME NULL COMMENT '生效开始时间';
 | 
				
			||||||
 | 
					ALTER TABLE t_team ADD validity_end_date DATETIME NULL COMMENT '生效结束时间';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					UPDATE t_team SET validity_start_date = NOW(), validity_end_date = '2034-01-01 00:00:00'
 | 
				
			||||||
		Reference in New Issue
	
	Block a user