mirror of
				https://github.com/nsnail/NetAdmin.git
				synced 2025-10-31 03:19:26 +08:00 
			
		
		
		
	| @@ -25,7 +25,6 @@ public record QueryConfigRsp : Sys_Config | ||||
|     public override bool UserRegisterConfirm { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_Config.UserRegisterDept" /> | ||||
|     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     public new virtual QueryDeptRsp UserRegisterDept { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_Config.UserRegisterDeptId" /> | ||||
| @@ -33,7 +32,6 @@ public record QueryConfigRsp : Sys_Config | ||||
|     public override long UserRegisterDeptId { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_Config.UserRegisterRole" /> | ||||
|     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     public new virtual QueryRoleRsp UserRegisterRole { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_Config.UserRegisterRoleId" /> | ||||
|   | ||||
| @@ -48,7 +48,6 @@ public record QueryLoginLogRsp : Sys_LoginLog | ||||
|     public override string LoginUserName { get; protected init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_LoginLog.Owner" /> | ||||
|     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     public new virtual QueryUserRsp Owner { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_LoginLog.RequestBody" /> | ||||
|   | ||||
| @@ -17,7 +17,6 @@ public record QueryRequestLogRsp : Sys_RequestLog | ||||
|     public new virtual string CreatedClientIp => base.CreatedClientIp?.ToIpV4(); | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_RequestLog.Api" /> | ||||
|     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     public new virtual QueryApiRsp Api { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_RequestLog.ApiPathCrc32" /> | ||||
| @@ -29,7 +28,6 @@ public record QueryRequestLogRsp : Sys_RequestLog | ||||
|     public override DateTime CreatedTime { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_RequestLog.Detail" /> | ||||
|     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     public new virtual QueryRequestLogDetailRsp Detail { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_RequestLog.Duration" /> | ||||
| @@ -45,7 +43,6 @@ public record QueryRequestLogRsp : Sys_RequestLog | ||||
|     public override int HttpStatusCode { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_RequestLog.Owner" /> | ||||
|     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     public new virtual QueryUserRsp Owner { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="IFieldOwner.OwnerId" /> | ||||
|   | ||||
| @@ -24,7 +24,6 @@ public record QuerySiteMsgRsp : Sys_SiteMsg | ||||
|     public override string CreatedUserName { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_SiteMsg.Depts" /> | ||||
|     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     public new virtual IEnumerable<QueryDeptRsp> Depts { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="EntityBase{T}.Id" /> | ||||
| @@ -45,7 +44,6 @@ public record QuerySiteMsgRsp : Sys_SiteMsg | ||||
|     public QuerySiteMsgFlagRsp MyFlags { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_SiteMsg.Roles" /> | ||||
|     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     public new virtual IEnumerable<QueryRoleRsp> Roles { get; init; } | ||||
|  | ||||
|     /// <summary> | ||||
| @@ -62,7 +60,6 @@ public record QuerySiteMsgRsp : Sys_SiteMsg | ||||
|     public override string Title { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="Sys_SiteMsg.Users" /> | ||||
|     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] | ||||
|     public new virtual IEnumerable<QueryUserRsp> Users { get; init; } | ||||
|  | ||||
|     /// <inheritdoc cref="IFieldVersion.Version" /> | ||||
|   | ||||
| @@ -207,7 +207,9 @@ public sealed class UserService( | ||||
|                            .ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         return dbUser == null ? throw new NetAdminInvalidOperationException(Ln.用户名或密码错误) : LoginInternal(dbUser); | ||||
|         return dbUser == null | ||||
|             ? throw new NetAdminInvalidOperationException(Ln.用户名或密码错误) | ||||
|             : await LoginInternalAsync(dbUser).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc /> | ||||
| @@ -221,7 +223,9 @@ public sealed class UserService( | ||||
|         } | ||||
|  | ||||
|         var dbUser = await Rpo.Where(a => a.Mobile == req.DestDevice).ToOneAsync().ConfigureAwait(false); | ||||
|         return dbUser == null ? throw new NetAdminInvalidOperationException(Ln.用户不存在) : LoginInternal(dbUser); | ||||
|         return dbUser == null | ||||
|             ? throw new NetAdminInvalidOperationException(Ln.用户不存在) | ||||
|             : await LoginInternalAsync(dbUser).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc /> | ||||
| @@ -229,7 +233,7 @@ public sealed class UserService( | ||||
|     { | ||||
|         var dbUser = await Rpo.Where(a => a.Id == userId).ToOneAsync().ConfigureAwait(false); | ||||
|  | ||||
|         return LoginInternal(dbUser); | ||||
|         return await LoginInternalAsync(dbUser).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc /> | ||||
| @@ -464,14 +468,15 @@ public sealed class UserService( | ||||
|         return dept.Count != 1 ? throw new NetAdminInvalidOperationException(Ln.部门不存在) : roles; | ||||
|     } | ||||
|  | ||||
|     private LoginRsp LoginInternal(Sys_User dbUser) | ||||
|     private async Task<LoginRsp> LoginInternalAsync(Sys_User dbUser) | ||||
|     { | ||||
|         if (!dbUser.Enabled) { | ||||
|             throw new NetAdminInvalidOperationException(Ln.请联系管理员激活账号); | ||||
|         } | ||||
|  | ||||
|         _ = UpdateAsync(dbUser with { LastLoginTime = DateTime.Now }, [nameof(Sys_User.LastLoginTime)] | ||||
| ,                                                                     ignoreVersion: true); | ||||
|         _ = await UpdateAsync(dbUser with { LastLoginTime = DateTime.Now }, [nameof(Sys_User.LastLoginTime)] | ||||
| ,                                                                           ignoreVersion: true) | ||||
|             .ConfigureAwait(false); | ||||
|  | ||||
|         var tokenPayload | ||||
|             = new Dictionary<string, object> { { nameof(ContextUserToken), dbUser.Adapt<ContextUserToken>() } }; | ||||
|   | ||||
| @@ -76,15 +76,59 @@ | ||||
|         <el-badge :hidden="vue.query.dynamicFilter.filters.length === 0" :value="vue.query.dynamicFilter.filters.length"> | ||||
|             <el-button-group> | ||||
|                 <el-button @click="search" icon="el-icon-search" type="primary">{{ $t('查询') }}</el-button> | ||||
|                 <el-popover :title="$t('已应用的查询条件')" placement="bottom-end" trigger="hover" width="40rem"> | ||||
|                 <el-popover :title="$t('已应用的查询条件')" :width="popWidth" placement="bottom-end" trigger="hover"> | ||||
|                     <template #reference> | ||||
|                         <el-button @click="reset" icon="el-icon-refresh-left">{{ $t('重置') }}</el-button> | ||||
|                     </template> | ||||
|                     <v-ace-editor | ||||
|                         :theme="this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK ? 'github_dark' : 'github'" | ||||
|                         :value="vkbeautify().json(vue.query, 2)" | ||||
|                         v-model:value="aceEditorValue" | ||||
|                         :theme="$TOOL.data.get('APP_SET_DARK') || $CONFIG.APP_SET_DARK ? 'github_dark' : 'github'" | ||||
|                         lang="json" | ||||
|                         style="height: 20rem; width: 100%" /> | ||||
|                     <p class="mt-4 flex gap05 items-center" style="justify-content: right"> | ||||
|                         <span class="text-right" style="width: 5rem">{{ $t('全局') }}</span> | ||||
|                         <el-select v-model="this.aceEditorValue" :key="selectFilterKey" :teleported="false" style="flex-grow: 1"> | ||||
|                             <el-option | ||||
|                                 v-for="(item, i) in $TOOL.data.get('APP_SET_QUERY_FILTERS') || []" | ||||
|                                 :key="i" | ||||
|                                 :label="item.name" | ||||
|                                 :value="item.value" /> | ||||
|                         </el-select> | ||||
|                         <el-button-group> | ||||
|                             <el-button @click="saveFilter(true)" plain type="primary">{{ $t('保存') }}</el-button> | ||||
|                             <el-button @click="delFilter(true)" plain>{{ $t('删除') }}</el-button> | ||||
|                         </el-button-group> | ||||
|                     </p> | ||||
|                     <p class="mt-4 flex gap05 items-center" style="justify-content: right"> | ||||
|                         <span class="text-right" style="width: 5rem">{{ $t('本页') }}</span> | ||||
|                         <el-select v-model="this.aceEditorValue" :key="selectFilterKey" :teleported="false" style="flex-grow: 1"> | ||||
|                             <el-option | ||||
|                                 v-for="(item, i) in $TOOL.data.get('APP_SET_QUERY_FILTERS_' + this.queryApi) || []" | ||||
|                                 :key="i" | ||||
|                                 :label="item.name" | ||||
|                                 :value="item.value" /> | ||||
|                         </el-select> | ||||
|                         <el-button-group> | ||||
|                             <el-button @click="saveFilter(false)" plain type="primary">{{ $t('保存') }}</el-button> | ||||
|                             <el-button @click="delFilter(false)" plain>{{ $t('删除') }}</el-button> | ||||
|                         </el-button-group> | ||||
|                     </p> | ||||
|                     <p class="mt-4 text-right"> | ||||
|                         <el-button @click="jsonFormat">{{ $t('JSON 格式化') }}</el-button> | ||||
|                         <el-dropdown :teleported="false"> | ||||
|                             <el-button @click="reSearch" type="primary">{{ $t('重新查询') }}</el-button> | ||||
|                             <template #dropdown> | ||||
|                                 <el-dropdown-menu> | ||||
|                                     <el-dropdown-item @click="reSearch(5)">5s</el-dropdown-item> | ||||
|                                     <el-dropdown-item @click="reSearch(10)">10s</el-dropdown-item> | ||||
|                                     <el-dropdown-item @click="reSearch(15)">15s</el-dropdown-item> | ||||
|                                     <el-dropdown-item @click="reSearch(30)">30s</el-dropdown-item> | ||||
|                                     <el-dropdown-item @click="reSearch(60)">60s</el-dropdown-item> | ||||
|                                     <el-dropdown-item @click="reSearch(120)">120s</el-dropdown-item> | ||||
|                                 </el-dropdown-menu> | ||||
|                             </template> | ||||
|                         </el-dropdown> | ||||
|                     </p> | ||||
|                 </el-popover> | ||||
|             </el-button-group> | ||||
|         </el-badge> | ||||
| @@ -96,7 +140,7 @@ import tool from '@/utils/tool' | ||||
| import vkbeautify from 'vkbeautify/index' | ||||
|  | ||||
| export default { | ||||
|     emits: ['search', 'reset'], | ||||
|     emits: ['search', 'reset', 'reSearch'], | ||||
|     props: { | ||||
|         dateField: { type: String, default: 'createdTime' }, | ||||
|         hasDate: { type: Boolean, default: true }, | ||||
| @@ -108,6 +152,11 @@ export default { | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             autoResearchTimer: null, | ||||
|             queryApi: null, | ||||
|             popWidth: '40rem', | ||||
|             selectFilterKey: 0, | ||||
|             aceEditorValue: null, | ||||
|             selectInputKey: null, | ||||
|             dateShortCuts: [ | ||||
|                 { | ||||
| @@ -356,8 +405,23 @@ export default { | ||||
|             }, | ||||
|         } | ||||
|     }, | ||||
|     mounted() {}, | ||||
|     mounted() { | ||||
|         this.queryApi = this.vue.$refs.table.queryApi.url.toUpperCase() | ||||
|     }, | ||||
|     watch: { | ||||
|         'vue.query': { | ||||
|             immediate: true, | ||||
|             deep: true, | ||||
|             handler: function (o, n) { | ||||
|                 this.aceEditorValue = this.vkbeautify.json(n, 2) | ||||
|             }, | ||||
|         }, | ||||
|     }, | ||||
|     async created() { | ||||
|         if (document.body.clientWidth < 1000) { | ||||
|             this.popWidth = '100%' | ||||
|         } | ||||
|         this.aceEditorValue = this.vkbeautify.json(this.vue.query, 2) | ||||
|         this.selectInputKey = this.controls.find((x) => x.type === 'select-input')?.field[1][0].key | ||||
|         if (this.dateType === 'datetimerange') { | ||||
|             this.dateShortCuts.unshift( | ||||
| @@ -405,8 +469,70 @@ export default { | ||||
|         tool() { | ||||
|             return tool | ||||
|         }, | ||||
|         vkbeautify() { | ||||
|             return vkbeautify | ||||
|         }, | ||||
|     }, | ||||
|     methods: { | ||||
|         jsonFormat() { | ||||
|             try { | ||||
|                 this.aceEditorValue = vkbeautify.json(this.aceEditorValue, 2) | ||||
|             } catch { | ||||
|                 this.$message.error(this.$t('格式错误')) | ||||
|             } | ||||
|         }, | ||||
|         async reSearch(sec) { | ||||
|             const newParam = JSON.parse(this.aceEditorValue) | ||||
|             this.vue.$refs.table.tableParams = newParam | ||||
|             this.vue.$refs.table.upData() | ||||
|             await this.$nextTick() | ||||
|             this.vue.$refs.table.tableParams = this.vue.query | ||||
|             this.$emit('reSearch', newParam) | ||||
|  | ||||
|             if (typeof sec !== 'number') return | ||||
|             const timerEl = document.getElementsByClassName('autoResearchTimer')[0] | ||||
|             if (!timerEl) { | ||||
|                 this.$message({ | ||||
|                     showClose: true, | ||||
|                     onClose: () => clearInterval(this.autoResearchTimer), | ||||
|                     type: 'warning', | ||||
|                     customClass: 'autoResearchTimer', | ||||
|                     message: this.$t('{s} 秒后刷新...', { s: sec }), | ||||
|                     duration: 0, | ||||
|                 }) | ||||
|                 this.autoResearchTimer = setInterval(() => { | ||||
|                     const el = document.getElementsByClassName('autoResearchTimer')[0].getElementsByClassName('el-message__content')[0] | ||||
|                     let num = parseInt(/(\d+)/.exec(el.innerHTML)[0]) | ||||
|                     if (num === 1) { | ||||
|                         this.reSearch() | ||||
|                         num = sec + 1 | ||||
|                     } | ||||
|                     el.innerHTML = el.innerHTML.replace(/\d+/, (num - 1).toString()) | ||||
|                 }, 1000) | ||||
|             } | ||||
|         }, | ||||
|         async delFilter(isGlobal) { | ||||
|             const key = isGlobal ? 'APP_SET_QUERY_FILTERS' : 'APP_SET_QUERY_FILTERS_' + this.queryApi | ||||
|             let filters = this.$TOOL.data.get(key) || [] | ||||
|             filters = filters.filter((x) => x.value !== this.aceEditorValue) | ||||
|             await this.$TOOL.data.set(key, filters) | ||||
|             this.$message.success(this.$t('删除成功')) | ||||
|             this.selectFilterKey = Math.random() | ||||
|         }, | ||||
|         async saveFilter(isGlobal) { | ||||
|             const key = isGlobal ? 'APP_SET_QUERY_FILTERS' : 'APP_SET_QUERY_FILTERS_' + this.queryApi | ||||
|             try { | ||||
|                 const filterName = await this.$prompt('设置一个过滤器名称', '保存查询条件', { | ||||
|                     inputPattern: /\S/, | ||||
|                     inputErrorMessage: '名称不能为空', | ||||
|                 }) | ||||
|                 let filters = this.$TOOL.data.get(key) || [] | ||||
|                 filters = filters.filter((x) => x.name !== filterName.value) | ||||
|                 filters.push({ name: filterName.value, value: this.aceEditorValue }) | ||||
|                 await this.$TOOL.data.set(key, filters) | ||||
|                 this.$message.success(this.$t('保存成功')) | ||||
|             } catch {} | ||||
|         }, | ||||
|         trimSpaces(key) { | ||||
|             this.form[key][this.selectInputKey] = this.form[key][this.selectInputKey].replace(/^\s*(.*?)\s*$/g, '$1') | ||||
|         }, | ||||
| @@ -415,9 +541,6 @@ export default { | ||||
|                 delete this.form[item.field[0]][field.key] | ||||
|             } | ||||
|         }, | ||||
|         vkbeautify() { | ||||
|             return vkbeautify | ||||
|         }, | ||||
|         search() { | ||||
|             const parentQuery = this.clearParentQuery() | ||||
|             Object.assign(parentQuery, this.form.root || {}) | ||||
|   | ||||
| @@ -6,24 +6,36 @@ import config from '@/config' | ||||
|  | ||||
| export default { | ||||
|     data() { | ||||
|         return {} | ||||
|         return { | ||||
|             ws: null, | ||||
|         } | ||||
|     }, | ||||
|     async created() { | ||||
|         const ws = new WebSocket(`ws://${config.API_URL.replace('http://', '')}/ws/version`) | ||||
|         ws.onopen = () => { | ||||
|             ws.send('1') | ||||
|         this.connectWebSocket() | ||||
|     }, | ||||
|     methods: { | ||||
|         connectWebSocket() { | ||||
|             this.ws = new WebSocket( | ||||
|                 import.meta.env.MODE === 'production' | ||||
|                     ? `wss://${config.API_URL.replace('https://', '')}/ws/version` | ||||
|                     : `ws://${window.location.host}/ws/version`, | ||||
|             ) | ||||
|             this.ws.onopen = () => { | ||||
|                 this.ws.send('1') | ||||
|             } | ||||
|         ws.onmessage = async (res) => { | ||||
|             this.ws.onmessage = async (res) => { | ||||
|                 if (res.data !== this.$TOOL.data.get('APP_VERSION')) { | ||||
|                     await this.$TOOL.data.set('APP_VERSION', res.data) | ||||
|                     this.showTip(res.data.slice(0, res.data.indexOf('+'))) | ||||
|                 } else { | ||||
|                     await new Promise((x) => setTimeout(x, 10000)) | ||||
|                 ws.send('1') | ||||
|                     this.ws.send('1') | ||||
|                 } | ||||
|             } | ||||
|             this.ws.onclose = () => { | ||||
|                 setTimeout(this.connectWebSocket, 5000) // 5秒后重试 | ||||
|             } | ||||
|         }, | ||||
|     methods: { | ||||
|         /** | ||||
|          * 通知消息 | ||||
|          */ | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
|  | ||||
| <template> | ||||
|     <div class="sc-dialog" ref="scDialog"> | ||||
|         <el-dialog v-bind="$attrs" v-model="dialogVisible" :fullscreen="isFullscreen" :show-close="false" ref="dialog"> | ||||
|         <el-dialog v-bind="$attrs" v-model="dialogVisible" :fullscreen="isFullscreen" :show-close="false" draggable ref="dialog"> | ||||
|             <template #header> | ||||
|                 <slot name="header"> | ||||
|                     <span class="el-dialog__title">{{ title }}</span> | ||||
|   | ||||
| @@ -24,6 +24,12 @@ | ||||
|                 </el-icon> | ||||
|                 刷新 | ||||
|             </li> | ||||
|             <li @click="scheduledRefresh()"> | ||||
|                 <el-icon> | ||||
|                     <el-icon-alarm-clock /> | ||||
|                 </el-icon> | ||||
|                 定时刷新 | ||||
|             </li> | ||||
|             <hr /> | ||||
|             <li :class="contextMenuItem.meta.affix ? 'disabled' : ''" @click="closeTabs()"> | ||||
|                 <el-icon> | ||||
| @@ -61,6 +67,7 @@ export default { | ||||
|     name: 'tags', | ||||
|     data() { | ||||
|         return { | ||||
|             refreshTimer: null, | ||||
|             contextMenuVisible: false, | ||||
|             contextMenuItem: null, | ||||
|             left: 0, | ||||
| @@ -69,7 +76,9 @@ export default { | ||||
|             tipDisplayed: false, | ||||
|         } | ||||
|     }, | ||||
|     props: {}, | ||||
|     props: { | ||||
|         vue: { type: Object }, | ||||
|     }, | ||||
|     watch: { | ||||
|         $route(e) { | ||||
|             this.addViewTags(e) | ||||
| @@ -188,27 +197,38 @@ export default { | ||||
|             this.contextMenuItem = null | ||||
|             this.contextMenuVisible = false | ||||
|         }, | ||||
|         async scheduledRefresh() { | ||||
|             this.closeMenu() | ||||
|             try { | ||||
|                 const sleep = await this.$prompt('刷新时间间隔(秒)', '定时刷新', { | ||||
|                     inputPattern: /^[1-9]\d*$/, | ||||
|                     inputErrorMessage: '时间必须为数字', | ||||
|                     inputValue: '10', | ||||
|                 }) | ||||
|                 const sleepSecs = parseInt(sleep.value) | ||||
|                 this.$message({ | ||||
|                     showClose: true, | ||||
|                     onClose: () => clearInterval(this.refreshTimer), | ||||
|                     type: 'warning', | ||||
|                     customClass: 'refreshTimer', | ||||
|                     message: this.$t('{s} 秒后刷新...', { s: sleepSecs }), | ||||
|                     duration: 0, | ||||
|                 }) | ||||
|                 this.refreshTimer = setInterval(() => { | ||||
|                     const el = document.getElementsByClassName('refreshTimer')[0].getElementsByClassName('el-message__content')[0] | ||||
|                     let num = parseInt(/(\d+)/.exec(el.innerHTML)[0]) | ||||
|                     if (num === 1) { | ||||
|                         this.vue.routerViewKey = Math.random() | ||||
|                         num = sleepSecs + 1 | ||||
|                     } | ||||
|                     el.innerHTML = el.innerHTML.replace(/\d+/, (num - 1).toString()) | ||||
|                 }, 1000) | ||||
|             } catch {} | ||||
|         }, | ||||
|         //TAB 刷新 | ||||
|         refreshTab() { | ||||
|             this.contextMenuVisible = false | ||||
|             const nowTag = this.contextMenuItem | ||||
|             //判断是否当前路由,否的话跳转 | ||||
|             if (this.$route.fullPath !== nowTag.fullPath) { | ||||
|                 this.$router.push({ | ||||
|                     path: nowTag.fullPath, | ||||
|                     query: nowTag.query, | ||||
|                 }) | ||||
|             } | ||||
|  | ||||
|             this.$store.commit('refreshIframe', nowTag) | ||||
|             setTimeout(() => { | ||||
|                 this.$store.commit('removeKeepLive', nowTag.name) | ||||
|                 this.$store.commit('setRouteShow', false) | ||||
|                 this.$nextTick(() => { | ||||
|                     this.$store.commit('pushKeepLive', nowTag.name) | ||||
|                     this.$store.commit('setRouteShow', true) | ||||
|                 }) | ||||
|             }, 0) | ||||
|             this.closeMenu() | ||||
|             this.vue.routerViewKey = Math.random() | ||||
|         }, | ||||
|         //TAB 关闭 | ||||
|         closeTabs() { | ||||
|   | ||||
| @@ -46,9 +46,9 @@ | ||||
|             </div> | ||||
|             <Side-m v-if="ismobile"></Side-m> | ||||
|             <div class="aminui-body el-container"> | ||||
|                 <Tags v-if="!ismobile && layoutTags"></Tags> | ||||
|                 <Tags v-if="!ismobile && layoutTags" :vue="this"></Tags> | ||||
|                 <div class="adminui-main" id="adminui-main"> | ||||
|                     <router-view v-slot="{ Component }"> | ||||
|                     <router-view v-slot="{ Component }" :key="routerViewKey"> | ||||
|                         <keep-alive> | ||||
|                             <component v-if="$store.state.keepAlive.routeShow" :is="Component" :key="$route.fullPath" /> | ||||
|                         </keep-alive> | ||||
| @@ -93,9 +93,9 @@ | ||||
|             </div> | ||||
|             <Side-m v-if="ismobile"></Side-m> | ||||
|             <div class="aminui-body el-container"> | ||||
|                 <Tags v-if="!ismobile && layoutTags"></Tags> | ||||
|                 <Tags v-if="!ismobile && layoutTags" :vue="this"></Tags> | ||||
|                 <div class="adminui-main" id="adminui-main"> | ||||
|                     <router-view v-slot="{ Component }"> | ||||
|                     <router-view v-slot="{ Component }" :key="routerViewKey"> | ||||
|                         <keep-alive> | ||||
|                             <component v-if="$store.state.keepAlive.routeShow" :is="Component" :key="$route.fullPath" /> | ||||
|                         </keep-alive> | ||||
| @@ -135,9 +135,9 @@ | ||||
|         </header> | ||||
|         <section class="aminui-wrapper"> | ||||
|             <div class="aminui-body el-container"> | ||||
|                 <Tags v-if="!ismobile && layoutTags"></Tags> | ||||
|                 <Tags v-if="!ismobile && layoutTags" :vue="this"></Tags> | ||||
|                 <div class="adminui-main" id="adminui-main"> | ||||
|                     <router-view v-slot="{ Component }"> | ||||
|                     <router-view v-slot="{ Component }" :key="routerViewKey"> | ||||
|                         <keep-alive> | ||||
|                             <component v-if="$store.state.keepAlive.routeShow" :is="Component" :key="$route.fullPath" /> | ||||
|                         </keep-alive> | ||||
| @@ -195,9 +195,9 @@ | ||||
|                 <Topbar> | ||||
|                     <userbar></userbar> | ||||
|                 </Topbar> | ||||
|                 <Tags v-if="!ismobile && layoutTags"></Tags> | ||||
|                 <Tags v-if="!ismobile && layoutTags" :vue="this"></Tags> | ||||
|                 <div class="adminui-main" id="adminui-main"> | ||||
|                     <router-view v-slot="{ Component }"> | ||||
|                     <router-view v-slot="{ Component }" :key="routerViewKey"> | ||||
|                         <keep-alive> | ||||
|                             <component v-if="$store.state.keepAlive.routeShow" :is="Component" :key="$route.fullPath" /> | ||||
|                         </keep-alive> | ||||
| @@ -239,6 +239,7 @@ export default { | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             routerViewKey: 0, | ||||
|             menu: [], | ||||
|             nextMenu: [], | ||||
|             pmenu: {}, | ||||
|   | ||||
| @@ -436,7 +436,6 @@ export default { | ||||
|     周四: 'Thursday', | ||||
|     周五: 'Friday', | ||||
|     周六: 'Saturday', | ||||
|     JSON格式化: 'JSON formatting', | ||||
|     确定: 'Confirm', | ||||
|     无权限或找不到页面: 'No permission or page not found', | ||||
|     '当前页面无权限访问或者打开了一个不存在的链接,请检查当前账户权限和链接的可访问性。': | ||||
| @@ -467,6 +466,7 @@ export default { | ||||
|     用户列表: 'User list', | ||||
|     是: 'Yes', | ||||
|     否: 'No', | ||||
|     资源复用: 'Resource reuse', | ||||
|     手机: 'Mobile', | ||||
|     '您已退出登录或无权限访问当前资源,请重新登录后再操作': | ||||
|         'You have logged out or do not have permission to access the current resource, please log in again before operating', | ||||
| @@ -497,4 +497,10 @@ export default { | ||||
|     请输入操作符: 'Please enter operator', | ||||
|     查询字段: 'Query field', | ||||
|     最后登录: 'Last login', | ||||
|     '{s} 秒后刷新...': '{s} seconds to refresh...', | ||||
|     重新查询: 'Re-query', | ||||
|     全局: 'Global', | ||||
|     本页: 'Page', | ||||
|     'JSON 格式化': 'JSON formatting', | ||||
|     格式错误: 'Format error', | ||||
| } | ||||
| @@ -435,7 +435,6 @@ export default { | ||||
|     周四: '周四', | ||||
|     周五: '周五', | ||||
|     周六: '周六', | ||||
|     JSON格式化: 'JSON格式化', | ||||
|     确定: '确定', | ||||
|     无权限或找不到页面: '无权限或找不到页面', | ||||
|     '当前页面无权限访问或者打开了一个不存在的链接,请检查当前账户权限和链接的可访问性。': | ||||
| @@ -465,6 +464,7 @@ export default { | ||||
|     用户列表: '用户列表', | ||||
|     是: '是', | ||||
|     否: '否', | ||||
|     资源复用: '资源复用', | ||||
|     手机: '手机', | ||||
|     '您已退出登录或无权限访问当前资源,请重新登录后再操作': '您已退出登录或无权限访问当前资源,请重新登录后再操作', | ||||
|     访问受限: '访问受限', | ||||
| @@ -494,4 +494,10 @@ export default { | ||||
|     请输入操作符: '请输入操作符', | ||||
|     查询字段: '查询字段', | ||||
|     最后登录: '最后登录', | ||||
|     '{s} 秒后刷新...': '{s} 秒后刷新...', | ||||
|     重新查询: '重新查询', | ||||
|     全局: '全局', | ||||
|     本页: '本页', | ||||
|     'JSON 格式化': 'JSON 格式化', | ||||
|     格式错误: '格式错误', | ||||
| } | ||||
| @@ -513,6 +513,10 @@ textarea { | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .text-right { | ||||
|     text-align: right; | ||||
| } | ||||
|  | ||||
| .flex { | ||||
|     display: flex; | ||||
| } | ||||
|   | ||||
| @@ -93,6 +93,7 @@ | ||||
|                     'nextExecTime', | ||||
|                     'enabled', | ||||
|                     'createdTime', | ||||
|                     'lastDuration', | ||||
|                 ]" | ||||
|                 :default-sort="{ prop: 'lastExecTime', order: 'descending' }" | ||||
|                 :export-api="$API.sys_job.export" | ||||
|   | ||||
| @@ -80,7 +80,9 @@ | ||||
|                                 :theme="this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK ? 'github_dark' : 'github'" | ||||
|                                 lang="json" | ||||
|                                 style="height: 30rem; width: 100%" /> | ||||
|                             <el-button @click="form.dashboardLayout = jsonFormat(form.dashboardLayout)" type="text">{{ $t('JSON格式化') }}</el-button> | ||||
|                             <el-button @click="form.dashboardLayout = jsonFormat(form.dashboardLayout)" type="text">{{ | ||||
|                                 $t('JSON 格式化') | ||||
|                             }}</el-button> | ||||
|                         </el-form-item> | ||||
|                     </el-form> | ||||
|                 </el-tab-pane> | ||||
|   | ||||
| @@ -69,7 +69,7 @@ | ||||
|         </el-header> | ||||
|         <el-main class="nopadding"> | ||||
|             <sc-table | ||||
|                 :context-menus="['id', 'userName', 'mobile', 'email', 'enabled', 'createdTime']" | ||||
|                 :context-menus="['id', 'userName', 'mobile', 'email', 'enabled', 'createdTime', 'lastLoginTime']" | ||||
|                 :context-opers="['view', 'edit']" | ||||
|                 :default-sort="{ prop: 'createdTime', order: 'descending' }" | ||||
|                 :export-api="$API.sys_user.export" | ||||
|   | ||||
| @@ -26,6 +26,11 @@ export default defineConfig({ | ||||
|                 changeOrigin: true, | ||||
|                 rewrite: (p) => p.replace(/^\/api/, ''), | ||||
|             }, | ||||
|             '/ws': { | ||||
|                 target: 'ws://localhost:5010/ws', | ||||
|                 ws: true, | ||||
|                 rewrite: (p) => p.replace(/^\/ws/, ''), | ||||
|             }, | ||||
|         }, | ||||
|     }, | ||||
|     css: { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 GitHub
						GitHub