mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-04-22 22:22:51 +08:00
parent
e00c30c961
commit
4733adede5
@ -30,6 +30,18 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
|
|||||||
set => Rpo.DbContextOptions.EnableCascadeSave = value;
|
set => Rpo.DbContextOptions.EnableCascadeSave = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导出实体
|
||||||
|
/// </summary>
|
||||||
|
protected async Task<IActionResult> ExportAsync<TQuery, TExport>( //
|
||||||
|
Func<QueryReq<TQuery>, ISelectGrouping<TEntity, TEntity>> selector, QueryReq<TQuery> query, string fileName
|
||||||
|
, Expression<Func<ISelectGroupingAggregate<TEntity, TEntity>, object>> listExp)
|
||||||
|
where TQuery : DataAbstraction, new()
|
||||||
|
{
|
||||||
|
var list = await selector(query).Take(Numbers.MAX_LIMIT_EXPORT).ToListAsync(listExp).ConfigureAwait(false);
|
||||||
|
return await GetExportFileStreamAsync<TExport>(fileName, list).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 导出实体
|
/// 导出实体
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -48,26 +60,7 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
|
|||||||
? await select.ToListAsync().ConfigureAwait(false)
|
? await select.ToListAsync().ConfigureAwait(false)
|
||||||
: await select.ToListAsync(listExp).ConfigureAwait(false);
|
: await select.ToListAsync(listExp).ConfigureAwait(false);
|
||||||
|
|
||||||
var listTyped = list.Adapt<List<TExport>>();
|
return await GetExportFileStreamAsync<TExport>(fileName, list).ConfigureAwait(false);
|
||||||
var stream = new MemoryStream();
|
|
||||||
var writer = new StreamWriter(stream);
|
|
||||||
var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
|
|
||||||
csv.WriteHeader<TExport>();
|
|
||||||
await csv.NextRecordAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
foreach (var item in listTyped) {
|
|
||||||
csv.WriteRecord(item);
|
|
||||||
await csv.NextRecordAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
await csv.FlushAsync().ConfigureAwait(false);
|
|
||||||
_ = stream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
App.HttpContext.Response.Headers.ContentDisposition
|
|
||||||
= new ContentDispositionHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_ATTACHMENT) {
|
|
||||||
FileNameStar = $"{fileName}_{DateTime.Now:yyyy.MM.dd-HH.mm.ss}.csv"
|
|
||||||
}.ToString();
|
|
||||||
return new FileStreamResult(stream, Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_OCTET_STREAM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -117,6 +110,31 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private static async Task<IActionResult> GetExportFileStreamAsync<TExport>(string fileName, object list)
|
||||||
|
{
|
||||||
|
var listTyped = list.Adapt<List<TExport>>();
|
||||||
|
var stream = new MemoryStream();
|
||||||
|
var writer = new StreamWriter(stream);
|
||||||
|
var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
|
||||||
|
csv.WriteHeader<TExport>();
|
||||||
|
await csv.NextRecordAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
foreach (var item in listTyped) {
|
||||||
|
csv.WriteRecord(item);
|
||||||
|
await csv.NextRecordAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
await csv.FlushAsync().ConfigureAwait(false);
|
||||||
|
_ = stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
App.HttpContext.Response.Headers.ContentDisposition
|
||||||
|
= new ContentDispositionHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_ATTACHMENT) {
|
||||||
|
FileNameStar = $"{fileName}_{DateTime.Now:yyyy.MM.dd-HH.mm.ss}.csv"
|
||||||
|
}.ToString();
|
||||||
|
|
||||||
|
return new FileStreamResult(stream, Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_OCTET_STREAM);
|
||||||
|
}
|
||||||
|
|
||||||
private IUpdate<TEntity> BuildUpdate( //
|
private IUpdate<TEntity> BuildUpdate( //
|
||||||
TEntity entity //
|
TEntity entity //
|
||||||
, IEnumerable<string> includeFields //
|
, IEnumerable<string> includeFields //
|
||||||
|
@ -19,6 +19,8 @@ public record CreateLoginLogReq : Sys_LoginLog, IRegister
|
|||||||
{
|
{
|
||||||
var body = s.Detail.ResponseBody.ToObject<RestfulInfo<LoginRsp>>();
|
var body = s.Detail.ResponseBody.ToObject<RestfulInfo<LoginRsp>>();
|
||||||
ContextUserToken userToken = null;
|
ContextUserToken userToken = null;
|
||||||
|
|
||||||
|
// ReSharper disable once InvertIf
|
||||||
if (body.Data?.AccessToken != null) {
|
if (body.Data?.AccessToken != null) {
|
||||||
try {
|
try {
|
||||||
userToken = ContextUserToken.Create(body.Data.AccessToken);
|
userToken = ContextUserToken.Create(body.Data.AccessToken);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"ace-builds": "^1.35.2",
|
"ace-builds": "^1.35.4",
|
||||||
"aieditor": "^1.0.13",
|
"aieditor": "^1.0.13",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
@ -18,7 +18,7 @@
|
|||||||
"cropperjs": "^1.6.2",
|
"cropperjs": "^1.6.2",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
"element-plus": "^2.7.7",
|
"element-plus": "^2.7.8",
|
||||||
"json-bigint": "^1.0.0",
|
"json-bigint": "^1.0.0",
|
||||||
"json5-to-table": "^0.1.8",
|
"json5-to-table": "^0.1.8",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"qrcodejs2": "^0.0.2",
|
"qrcodejs2": "^0.0.2",
|
||||||
"sortablejs": "^1.15.2",
|
"sortablejs": "^1.15.2",
|
||||||
"vkbeautify": "^0.99.3",
|
"vkbeautify": "^0.99.3",
|
||||||
"vue": "^3.4.31",
|
"vue": "^3.4.34",
|
||||||
"vue-i18n": "^9.13.1",
|
"vue-i18n": "^9.13.1",
|
||||||
"vue-router": "^4.4.0",
|
"vue-router": "^4.4.0",
|
||||||
"vue3-ace-editor": "^2.2.4",
|
"vue3-ace-editor": "^2.2.4",
|
||||||
@ -37,12 +37,12 @@
|
|||||||
"vuex": "^4.1.0"
|
"vuex": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.0.5",
|
"@vitejs/plugin-vue": "^5.1.1",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-organize-attributes": "^1.0.0",
|
"prettier-plugin-organize-attributes": "^1.0.0",
|
||||||
"sass": "^1.77.8",
|
"sass": "^1.77.8",
|
||||||
"terser": "^5.31.3",
|
"terser": "^5.31.3",
|
||||||
"vite": "^5.3.4"
|
"vite": "^5.3.5"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"> 1%",
|
"> 1%",
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
<template>
|
|
||||||
<p>{{ ip ?? '-' }}</p>
|
|
||||||
<p style="overflow: hidden">{{ region ?? '-' }}</p>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import http from '@/utils/request'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
emits: [],
|
|
||||||
props: ['ip'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
region: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {},
|
|
||||||
created() {
|
|
||||||
if (this.ip) {
|
|
||||||
this.region = '...'
|
|
||||||
http.get(`http://ip.line92.xyz/?ip=${this.ip}`).then((x) => {
|
|
||||||
this.region = x.region
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {},
|
|
||||||
computed: {},
|
|
||||||
methods: {},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style scoped></style>
|
|
@ -2,7 +2,7 @@
|
|||||||
<form @keyup.enter="search" @submit.prevent="search" class="right-panel-search">
|
<form @keyup.enter="search" @submit.prevent="search" class="right-panel-search">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-if="hasDate"
|
v-if="hasDate"
|
||||||
v-model="form.dy.createdTime"
|
v-model="form.dy[this.dateField]"
|
||||||
:end-placeholder="$t('结束日期')"
|
:end-placeholder="$t('结束日期')"
|
||||||
:format="dateFormat"
|
:format="dateFormat"
|
||||||
:range-separator="$t('至')"
|
:range-separator="$t('至')"
|
||||||
@ -83,6 +83,7 @@ import vkbeautify from 'vkbeautify/index'
|
|||||||
export default {
|
export default {
|
||||||
emits: ['search', 'reset'],
|
emits: ['search', 'reset'],
|
||||||
props: {
|
props: {
|
||||||
|
dateField: { type: String, default: 'createdTime' },
|
||||||
hasDate: { type: Boolean, default: true },
|
hasDate: { type: Boolean, default: true },
|
||||||
dateType: { type: String, default: 'daterange' },
|
dateType: { type: String, default: 'daterange' },
|
||||||
dateFormat: { type: String, default: 'YYYY-MM-DD' },
|
dateFormat: { type: String, default: 'YYYY-MM-DD' },
|
||||||
@ -114,8 +115,8 @@ export default {
|
|||||||
text: this.$t('后退一日'),
|
text: this.$t('后退一日'),
|
||||||
value: () => {
|
value: () => {
|
||||||
try {
|
try {
|
||||||
const start = new Date(new Date(this.form.dy.createdTime[0]) - 3600 * 1000 * 24)
|
const start = new Date(new Date(this.form.dy[this.dateField][0]) - 3600 * 1000 * 24)
|
||||||
const end = new Date(new Date(this.form.dy.createdTime[1]) - 3600 * 1000 * 24)
|
const end = new Date(new Date(this.form.dy[this.dateField][1]) - 3600 * 1000 * 24)
|
||||||
return [start, end]
|
return [start, end]
|
||||||
} catch {}
|
} catch {}
|
||||||
},
|
},
|
||||||
@ -356,8 +357,8 @@ export default {
|
|||||||
text: this.$t('后退一时'),
|
text: this.$t('后退一时'),
|
||||||
value: () => {
|
value: () => {
|
||||||
try {
|
try {
|
||||||
const start = new Date(new Date(this.form.dy.createdTime[0]) - 3600 * 1000)
|
const start = new Date(new Date(this.form.dy[this.dateField][0]) - 3600 * 1000)
|
||||||
const end = new Date(new Date(this.form.dy.createdTime[1]) - 3600 * 1000)
|
const end = new Date(new Date(this.form.dy[this.dateField][1]) - 3600 * 1000)
|
||||||
return [start, end]
|
return [start, end]
|
||||||
} catch {}
|
} catch {}
|
||||||
},
|
},
|
||||||
|
@ -24,7 +24,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="sc-statistic-content">
|
<div class="sc-statistic-content">
|
||||||
<span v-if="prefix" class="sc-statistic-content-prefix">{{ prefix }}</span>
|
<span v-if="prefix" class="sc-statistic-content-prefix">{{ prefix }}</span>
|
||||||
<span class="sc-statistic-content-value">{{ cmtValue }}</span>
|
<span class="sc-statistic-content-value">
|
||||||
|
<slot v-if="$slots.content" name="content"></slot>
|
||||||
|
<template v-else>
|
||||||
|
{{ cmtValue }}
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
<span v-if="suffix" class="sc-statistic-content-suffix">{{ suffix }}</span>
|
<span v-if="suffix" class="sc-statistic-content-suffix">{{ suffix }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="description || $slots.default" class="sc-statistic-description">
|
<div v-if="description || $slots.default" class="sc-statistic-description">
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
label: 'name',
|
label: 'name',
|
||||||
}"
|
}"
|
||||||
@node-click="click"
|
@node-click="click"
|
||||||
|
default-expand-all
|
||||||
node-key="id"
|
node-key="id"
|
||||||
ref="dic">
|
ref="dic">
|
||||||
<template #default="{ _, data }">
|
<template #default="{ _, data }">
|
||||||
|
@ -121,7 +121,7 @@ export default {
|
|||||||
this.apis = []
|
this.apis = []
|
||||||
const ips = data.data.rows?.map((x) => x.createdClientIp) ?? []
|
const ips = data.data.rows?.map((x) => x.createdClientIp) ?? []
|
||||||
const res = await Promise.all([
|
const res = await Promise.all([
|
||||||
ips && ips.length > 0 ? http.get(`http://ip.line92.xyz/?ip=${ips.join('&ip=')}`) : new Promise((x) => x({ data: [] })),
|
ips && ips.length > 0 ? http.get(`https://ip.tools92.top/?ip=${ips.join('&ip=')}`) : new Promise((x) => x({ data: [] })),
|
||||||
])
|
])
|
||||||
this.ips = res[0]
|
this.ips = res[0]
|
||||||
},
|
},
|
||||||
|
@ -239,7 +239,7 @@ export default {
|
|||||||
})
|
})
|
||||||
: new Promise((x) => x({ data: [] })),
|
: new Promise((x) => x({ data: [] })),
|
||||||
|
|
||||||
ips && ips.length > 0 ? http.get(`http://ip.line92.xyz/?ip=${ips.join('&ip=')}`) : new Promise((x) => x({ data: [] })),
|
ips && ips.length > 0 ? http.get(`https://ip.tools92.top/?ip=${ips.join('&ip=')}`) : new Promise((x) => x({ data: [] })),
|
||||||
])
|
])
|
||||||
this.owners = res[0].data
|
this.owners = res[0].data
|
||||||
this.apis = res[1].data
|
this.apis = res[1].data
|
||||||
|
Loading…
x
Reference in New Issue
Block a user