feat: 成果发布与报名数据优化、作品列表空结果修复、创建活动主办单位默认
- 成果发布:所属活动下拉与 GET /contests 筛选(id/resultState/creatorTenantId) - 报名列表/详情:回显 contest、creatorTenant、user;主办机构列绑定 creatorTenant - 作品列表:contestIds 为空时跳过评委 IN 查询避免 500 - 登录与用户信息返回 tenantName;创建活动默认主办单位 - 活动监管多页筛选表单补充 model Made-with: Cursor
This commit is contained in:
parent
8561f3d320
commit
1fff56d700
@ -13,6 +13,9 @@ public class QueryContestDto {
|
||||
@Schema(description = "每页条数", defaultValue = "10")
|
||||
private Long pageSize = 10L;
|
||||
|
||||
@Schema(description = "活动ID,精确筛选")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "活动名称")
|
||||
private String contestName;
|
||||
|
||||
@ -31,6 +34,9 @@ public class QueryContestDto {
|
||||
@Schema(description = "活动阶段")
|
||||
private String stage;
|
||||
|
||||
@Schema(description = "成果发布状态筛选:published/unpublished")
|
||||
private String resultState;
|
||||
|
||||
@Schema(description = "创建者租户ID")
|
||||
private Long creatorTenantId;
|
||||
|
||||
|
||||
@ -18,7 +18,9 @@ import com.competition.modules.biz.contest.mapper.ContestMapper;
|
||||
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
|
||||
import com.competition.modules.biz.contest.mapper.ContestRegistrationTeacherMapper;
|
||||
import com.competition.modules.biz.contest.service.IContestRegistrationService;
|
||||
import com.competition.modules.sys.entity.SysTenant;
|
||||
import com.competition.modules.sys.entity.SysUser;
|
||||
import com.competition.modules.sys.mapper.SysTenantMapper;
|
||||
import com.competition.modules.sys.mapper.SysUserMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -39,6 +41,7 @@ public class ContestRegistrationServiceImpl extends ServiceImpl<ContestRegistrat
|
||||
private final ContestRegistrationTeacherMapper contestRegistrationTeacherMapper;
|
||||
private final ContestMapper contestMapper;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final SysTenantMapper sysTenantMapper;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> createRegistration(CreateRegistrationDto dto, Long tenantId, Long creatorId) {
|
||||
@ -119,9 +122,66 @@ public class ContestRegistrationServiceImpl extends ServiceImpl<ContestRegistrat
|
||||
Page<BizContestRegistration> page = new Page<>(dto.getPage(), dto.getPageSize());
|
||||
Page<BizContestRegistration> result = contestRegistrationMapper.selectPage(page, wrapper);
|
||||
|
||||
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||
.map(this::registrationToMap)
|
||||
.collect(Collectors.toList());
|
||||
Set<Long> contestIds = result.getRecords().stream()
|
||||
.map(BizContestRegistration::getContestId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
Map<Long, BizContest> contestMap = new HashMap<>();
|
||||
if (!contestIds.isEmpty()) {
|
||||
contestMap = contestMapper.selectBatchIds(contestIds).stream()
|
||||
.collect(Collectors.toMap(BizContest::getId, c -> c));
|
||||
}
|
||||
|
||||
Set<Long> organizerTenantIds = new HashSet<>();
|
||||
for (BizContest c : contestMap.values()) {
|
||||
List<Integer> ct = c.getContestTenants();
|
||||
if (ct != null && !ct.isEmpty()) {
|
||||
organizerTenantIds.add(ct.get(0).longValue());
|
||||
}
|
||||
}
|
||||
Map<Long, SysTenant> organizerTenantMap = new HashMap<>();
|
||||
if (!organizerTenantIds.isEmpty()) {
|
||||
for (SysTenant t : sysTenantMapper.selectBatchIds(organizerTenantIds)) {
|
||||
if (t != null && t.getId() != null) {
|
||||
organizerTenantMap.put(t.getId(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Map<String, Object>> voList = new ArrayList<>();
|
||||
for (BizContestRegistration reg : result.getRecords()) {
|
||||
Map<String, Object> map = registrationToMap(reg);
|
||||
BizContest contest = contestMap.get(reg.getContestId());
|
||||
if (contest != null) {
|
||||
Map<String, Object> contestVo = new LinkedHashMap<>();
|
||||
contestVo.put("id", contest.getId());
|
||||
contestVo.put("contestName", contest.getContestName());
|
||||
map.put("contest", contestVo);
|
||||
List<Integer> ct = contest.getContestTenants();
|
||||
if (ct != null && !ct.isEmpty()) {
|
||||
SysTenant org = organizerTenantMap.get(ct.get(0).longValue());
|
||||
if (org != null) {
|
||||
Map<String, Object> tenantVo = new LinkedHashMap<>();
|
||||
tenantVo.put("id", org.getId());
|
||||
tenantVo.put("name", org.getName());
|
||||
tenantVo.put("code", org.getCode());
|
||||
map.put("creatorTenant", tenantVo);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reg.getUserId() != null) {
|
||||
SysUser user = sysUserMapper.selectById(reg.getUserId());
|
||||
if (user != null) {
|
||||
Map<String, Object> userVo = new LinkedHashMap<>();
|
||||
userVo.put("id", user.getId());
|
||||
userVo.put("username", user.getUsername());
|
||||
userVo.put("nickname", user.getNickname());
|
||||
userVo.put("phone", user.getPhone());
|
||||
map.put("user", userVo);
|
||||
}
|
||||
}
|
||||
voList.add(map);
|
||||
}
|
||||
|
||||
return PageResult.from(result, voList);
|
||||
}
|
||||
@ -191,6 +251,28 @@ public class ContestRegistrationServiceImpl extends ServiceImpl<ContestRegistrat
|
||||
|
||||
Map<String, Object> result = registrationToMap(registration);
|
||||
|
||||
// 活动与主办机构(前端列表/详情需要 contest、creatorTenant)
|
||||
if (registration.getContestId() != null) {
|
||||
BizContest contest = contestMapper.selectById(registration.getContestId());
|
||||
if (contest != null) {
|
||||
Map<String, Object> contestVo = new LinkedHashMap<>();
|
||||
contestVo.put("id", contest.getId());
|
||||
contestVo.put("contestName", contest.getContestName());
|
||||
result.put("contest", contestVo);
|
||||
List<Integer> ct = contest.getContestTenants();
|
||||
if (ct != null && !ct.isEmpty()) {
|
||||
SysTenant org = sysTenantMapper.selectById(ct.get(0).longValue());
|
||||
if (org != null) {
|
||||
Map<String, Object> tenantVo = new LinkedHashMap<>();
|
||||
tenantVo.put("id", org.getId());
|
||||
tenantVo.put("name", org.getName());
|
||||
tenantVo.put("code", org.getCode());
|
||||
result.put("creatorTenant", tenantVo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查询用户详情
|
||||
if (registration.getUserId() != null) {
|
||||
SysUser user = sysUserMapper.selectById(registration.getUserId());
|
||||
@ -203,6 +285,12 @@ public class ContestRegistrationServiceImpl extends ServiceImpl<ContestRegistrat
|
||||
userInfo.put("email", user.getEmail());
|
||||
userInfo.put("avatar", user.getAvatar());
|
||||
result.put("userInfo", userInfo);
|
||||
Map<String, Object> userVo = new LinkedHashMap<>();
|
||||
userVo.put("id", user.getId());
|
||||
userVo.put("username", user.getUsername());
|
||||
userVo.put("nickname", user.getNickname());
|
||||
userVo.put("phone", user.getPhone());
|
||||
result.put("user", userVo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -114,6 +114,9 @@ public class ContestServiceImpl extends ServiceImpl<ContestMapper, BizContest> i
|
||||
LambdaQueryWrapper<BizContest> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(BizContest::getValidState, 1);
|
||||
|
||||
if (dto.getId() != null) {
|
||||
wrapper.eq(BizContest::getId, dto.getId());
|
||||
}
|
||||
if (StringUtils.hasText(dto.getContestName())) {
|
||||
wrapper.like(BizContest::getContestName, dto.getContestName());
|
||||
}
|
||||
@ -129,6 +132,12 @@ public class ContestServiceImpl extends ServiceImpl<ContestMapper, BizContest> i
|
||||
if (StringUtils.hasText(dto.getContestType())) {
|
||||
wrapper.eq(BizContest::getContestType, dto.getContestType());
|
||||
}
|
||||
if (StringUtils.hasText(dto.getResultState())) {
|
||||
wrapper.eq(BizContest::getResultState, dto.getResultState());
|
||||
}
|
||||
if (dto.getCreatorTenantId() != null) {
|
||||
wrapper.apply("JSON_CONTAINS(contest_tenants, CAST({0} AS JSON))", dto.getCreatorTenantId());
|
||||
}
|
||||
|
||||
// 阶段筛选(与前端活动列表「活动阶段」一致:unpublished/finished/registering/submitting/reviewing)
|
||||
if (StringUtils.hasText(dto.getStage())) {
|
||||
|
||||
@ -403,11 +403,14 @@ public class ContestWorkServiceImpl extends ServiceImpl<ContestWorkMapper, BizCo
|
||||
.forEach(r -> ruleMap.put(r.getId(), r));
|
||||
}
|
||||
|
||||
// 批量预加载所有相关活动的评委权重
|
||||
// 批量预加载所有相关活动的评委权重(contestIds 为空时不能拼 IN (),否则 SQL 报错 500)
|
||||
List<BizContestJudge> allJudges = Collections.emptyList();
|
||||
if (!contestIds.isEmpty()) {
|
||||
LambdaQueryWrapper<BizContestJudge> allJudgesWrapper = new LambdaQueryWrapper<>();
|
||||
allJudgesWrapper.in(BizContestJudge::getContestId, contestIds);
|
||||
allJudgesWrapper.eq(BizContestJudge::getValidState, 1);
|
||||
List<BizContestJudge> allJudges = contestJudgeMapper.selectList(allJudgesWrapper);
|
||||
allJudges = contestJudgeMapper.selectList(allJudgesWrapper);
|
||||
}
|
||||
Map<Long, List<BizContestJudge>> judgesByContestId = allJudges.stream()
|
||||
.collect(Collectors.groupingBy(BizContestJudge::getContestId));
|
||||
|
||||
|
||||
@ -118,6 +118,8 @@ export interface UpdateContestForm extends Partial<CreateContestForm> {
|
||||
}
|
||||
|
||||
export interface QueryContestParams extends PaginationParams {
|
||||
/** 活动 ID,精确筛选(如成果发布「所属活动」) */
|
||||
id?: number;
|
||||
contestName?: string;
|
||||
contestState?: "unpublished" | "published";
|
||||
status?: "ongoing" | "finished";
|
||||
@ -131,6 +133,8 @@ export interface QueryContestParams extends PaginationParams {
|
||||
| "finished";
|
||||
creatorTenantId?: number;
|
||||
role?: "student" | "teacher" | "judge";
|
||||
/** 成果发布状态筛选 */
|
||||
resultState?: "published" | "unpublished";
|
||||
}
|
||||
|
||||
export interface ContestStats {
|
||||
@ -224,6 +228,8 @@ export interface ContestRegistration {
|
||||
createTime?: string;
|
||||
modifyTime?: string;
|
||||
contest?: Contest;
|
||||
/** 活动主办机构(与活动 contest_tenants 首个机构一致) */
|
||||
creatorTenant?: { id: number; name: string; code?: string };
|
||||
user?: {
|
||||
id: number;
|
||||
username: string;
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<a-form layout="inline" @finish="handleSuperSearch">
|
||||
<a-form layout="inline" :model="superSearch" @finish="handleSuperSearch">
|
||||
<a-form-item label="所属活动">
|
||||
<a-select
|
||||
v-model:value="superSearch.contestId"
|
||||
@ -124,7 +124,7 @@
|
||||
{{ record.accountNo || record.user?.username || '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'orgName'">
|
||||
{{ record.user?.tenant?.name || '-' }}
|
||||
{{ record.creatorTenant?.name || '-' }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'registrationState'">
|
||||
<a-tag :color="stateColorMap[record.registrationState] || 'default'">
|
||||
@ -234,7 +234,7 @@
|
||||
|
||||
<!-- 筛选 -->
|
||||
<div class="filter-bar">
|
||||
<a-form layout="inline" @finish="handleSearch">
|
||||
<a-form layout="inline" :model="searchParams" @finish="handleSearch">
|
||||
<a-form-item label="活动名称">
|
||||
<a-input v-model:value="searchParams.contestName" placeholder="请输入活动名称" allow-clear style="width: 200px" />
|
||||
</a-form-item>
|
||||
|
||||
@ -26,13 +26,16 @@
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<a-form layout="inline" @finish="handleSuperSearch">
|
||||
<a-form-item label="活动名称">
|
||||
<a-input
|
||||
v-model:value="superSearch.contestName"
|
||||
placeholder="请输入活动名称"
|
||||
<a-form layout="inline" :model="superSearch" @finish="handleSuperSearch">
|
||||
<a-form-item label="所属活动">
|
||||
<a-select
|
||||
v-model:value="superSearch.contestId"
|
||||
placeholder="全部活动"
|
||||
allow-clear
|
||||
style="width: 180px"
|
||||
show-search
|
||||
:filter-option="filterContestOption"
|
||||
style="width: 200px"
|
||||
:options="contestOptions"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="发布状态">
|
||||
@ -147,7 +150,7 @@
|
||||
|
||||
<!-- 筛选 -->
|
||||
<div class="filter-bar">
|
||||
<a-form layout="inline" @finish="handleSearch">
|
||||
<a-form layout="inline" :model="searchParams" @finish="handleSearch">
|
||||
<a-form-item label="活动名称">
|
||||
<a-input v-model:value="searchParams.contestName" placeholder="请输入活动名称" allow-clear style="width: 200px" />
|
||||
</a-form-item>
|
||||
@ -242,11 +245,24 @@ const superPagination = reactive({
|
||||
showTotal: (t: number) => `共 ${t} 条`,
|
||||
})
|
||||
const superSearch = reactive({
|
||||
contestName: undefined as string | undefined,
|
||||
contestId: undefined as number | undefined,
|
||||
resultState: undefined as string | undefined,
|
||||
creatorTenantId: undefined as number | undefined,
|
||||
})
|
||||
|
||||
// 活动下拉(与报名数据一致,仅已发布的活动)
|
||||
const contestOptions = ref<{ label: string; value: number }[]>([])
|
||||
const fetchContestOptions = async () => {
|
||||
try {
|
||||
const res = await contestsApi.getList({ page: 1, pageSize: 500, contestState: 'published' })
|
||||
contestOptions.value = res.list.map((c) => ({ label: c.contestName, value: c.id }))
|
||||
} catch {
|
||||
/* 静默 */
|
||||
}
|
||||
}
|
||||
const filterContestOption = (input: string, option: any) =>
|
||||
option.label?.toLowerCase().includes(input.toLowerCase())
|
||||
|
||||
// 统计(基于当前列表数据前端计算)
|
||||
const publishedCount = ref(0)
|
||||
const unpublishedCount = ref(0)
|
||||
@ -285,24 +301,14 @@ const fetchSuperList = async () => {
|
||||
const params: QueryContestParams = {
|
||||
page: superPagination.current,
|
||||
pageSize: superPagination.pageSize,
|
||||
contestName: superSearch.contestName,
|
||||
contestState: 'published', // 成果发布只看已发布的活动
|
||||
contestState: 'published',
|
||||
id: superSearch.contestId,
|
||||
creatorTenantId: superSearch.creatorTenantId,
|
||||
resultState: superSearch.resultState as 'published' | 'unpublished' | undefined,
|
||||
}
|
||||
const res = await contestsApi.getList(params)
|
||||
|
||||
// 前端过滤发布状态
|
||||
let list = res.list
|
||||
if (superSearch.resultState) {
|
||||
list = list.filter((c) =>
|
||||
superSearch.resultState === 'published'
|
||||
? c.resultState === 'published'
|
||||
: c.resultState !== 'published'
|
||||
)
|
||||
}
|
||||
|
||||
superDataSource.value = list
|
||||
superPagination.total = superSearch.resultState ? list.length : res.total
|
||||
superDataSource.value = res.list
|
||||
superPagination.total = res.total
|
||||
} catch {
|
||||
message.error('获取成果列表失败')
|
||||
} finally {
|
||||
@ -323,7 +329,7 @@ const handleSuperSearch = () => {
|
||||
}
|
||||
|
||||
const handleSuperReset = () => {
|
||||
superSearch.contestName = undefined
|
||||
superSearch.contestId = undefined
|
||||
superSearch.resultState = undefined
|
||||
superSearch.creatorTenantId = undefined
|
||||
activeFilter.value = ''
|
||||
@ -431,6 +437,7 @@ const handleViewDetail = (record: Contest) => { router.push(`/${tenantCode}/cont
|
||||
onMounted(() => {
|
||||
if (isSuperAdmin.value) {
|
||||
fetchTenants()
|
||||
fetchContestOptions()
|
||||
fetchSuperStats()
|
||||
fetchSuperList()
|
||||
} else {
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<a-form layout="inline" @finish="handleSuperSearch">
|
||||
<a-form layout="inline" :model="superSearch" @finish="handleSuperSearch">
|
||||
<a-form-item label="所属活动">
|
||||
<a-select
|
||||
v-model:value="superSearch.contestId"
|
||||
@ -212,7 +212,7 @@
|
||||
|
||||
<!-- 筛选 -->
|
||||
<div class="filter-bar">
|
||||
<a-form layout="inline" @finish="handleSearch">
|
||||
<a-form layout="inline" :model="searchParams" @finish="handleSearch">
|
||||
<a-form-item label="活动名称">
|
||||
<a-input v-model:value="searchParams.contestName" placeholder="请输入活动名称" allow-clear style="width: 200px" />
|
||||
</a-form-item>
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<a-form layout="inline" @finish="handleSuperSearch">
|
||||
<a-form layout="inline" :model="superSearch" @finish="handleSuperSearch">
|
||||
<a-form-item label="所属活动">
|
||||
<a-select
|
||||
v-model:value="superSearch.contestId"
|
||||
@ -147,7 +147,7 @@
|
||||
|
||||
<!-- 筛选 -->
|
||||
<div class="filter-bar">
|
||||
<a-form layout="inline" @finish="handleSearch">
|
||||
<a-form layout="inline" :model="searchParams" @finish="handleSearch">
|
||||
<a-form-item label="活动名称">
|
||||
<a-input v-model:value="searchParams.contestName" placeholder="请输入活动名称" allow-clear style="width: 200px" />
|
||||
</a-form-item>
|
||||
|
||||
27
pnpm-lock.yaml
generated
27
pnpm-lock.yaml
generated
@ -40,6 +40,9 @@ importers:
|
||||
dayjs:
|
||||
specifier: ^1.11.10
|
||||
version: 1.11.19
|
||||
dompurify:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
echarts:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
@ -71,6 +74,9 @@ importers:
|
||||
'@types/crypto-js':
|
||||
specifier: ^4.2.2
|
||||
version: 4.2.2
|
||||
'@types/dompurify':
|
||||
specifier: ^3.2.0
|
||||
version: 3.2.0
|
||||
'@types/multer':
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
@ -593,6 +599,10 @@ packages:
|
||||
'@types/crypto-js@4.2.2':
|
||||
resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==}
|
||||
|
||||
'@types/dompurify@3.2.0':
|
||||
resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==}
|
||||
deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.
|
||||
|
||||
'@types/estree@1.0.8':
|
||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||
|
||||
@ -632,6 +642,9 @@ packages:
|
||||
'@types/serve-static@1.15.10':
|
||||
resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@7.18.0':
|
||||
resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==}
|
||||
engines: {node: ^18.18.0 || >=20.0.0}
|
||||
@ -1153,6 +1166,9 @@ packages:
|
||||
dom7@3.0.0:
|
||||
resolution: {integrity: sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==}
|
||||
|
||||
dompurify@3.3.3:
|
||||
resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==}
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -2685,6 +2701,10 @@ snapshots:
|
||||
|
||||
'@types/crypto-js@4.2.2': {}
|
||||
|
||||
'@types/dompurify@3.2.0':
|
||||
dependencies:
|
||||
dompurify: 3.3.3
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@types/event-emitter@0.3.5': {}
|
||||
@ -2734,6 +2754,9 @@ snapshots:
|
||||
'@types/node': 20.19.25
|
||||
'@types/send': 0.17.6
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
@ -3366,6 +3389,10 @@ snapshots:
|
||||
dependencies:
|
||||
ssr-window: 3.0.0
|
||||
|
||||
dompurify@3.3.3:
|
||||
optionalDependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
|
||||
Loading…
Reference in New Issue
Block a user