mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-04-20 05:02:50 +08:00
feat: ✨ 查询过滤器保存
页面定时刷新 WebSocket断线自动重连
This commit is contained in:
parent
6922a863ec
commit
779d8e511a
@ -11,4 +11,4 @@
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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')
|
||||
}
|
||||
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.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')
|
||||
}
|
||||
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))
|
||||
this.ws.send('1')
|
||||
}
|
||||
}
|
||||
this.ws.onclose = () => {
|
||||
setTimeout(this.connectWebSocket, 5000) // 5秒后重试
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 通知消息
|
||||
*/
|
||||
|
@ -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: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user