feat(hall-audit): improve time conflict detection
- Use PUT method for audit API to match backend - Detect internal conflicts among selected records only when multiple selected - Differentiate conflict origin with tags (本次批次 / 已审批) - Format time with dayjs for backend checkTimeConflict API - Fix rotate-loading icon class and layout styles
This commit is contained in:
1 parent
d2781d4470
commit
8675ef4581
2 files changed
+453
-73
No files matched your search
@@ -1,84 +1,39 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { HallReservationVO, HallReservationForm, HallReservationQuery } from '@/api/hq/hallReservation/types';
|
||||
import { HallReservationVO, HallReservationQuery } from '../hallReservation/types';
|
||||
import { HallReservationAuditForm, TimeCheckParams, TimeCheckResult } from './types';
|
||||
import { TableDataInfo, R } from '@/api/types';
|
||||
|
||||
/**
|
||||
* 查询报告厅预约列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listHallReservation = (query?: HallReservationQuery): AxiosPromise<HallReservationVO[]> => {
|
||||
return request({
|
||||
// 查询报告厅预约列表
|
||||
export function listHallReservation(query: HallReservationQuery) {
|
||||
return request<TableDataInfo<HallReservationVO>>({
|
||||
url: '/hq/hallReservation/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询报告厅预约详细
|
||||
* @param id
|
||||
*/
|
||||
export const getHallReservation = (id: string | number): AxiosPromise<HallReservationVO> => {
|
||||
return request({
|
||||
// 获取报告厅预约详细信息
|
||||
export function getHallReservation(id: string | number) {
|
||||
return request<R<HallReservationVO>>({
|
||||
url: '/hq/hallReservation/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增报告厅预约
|
||||
* @param data
|
||||
*/
|
||||
export const addHallReservation = (data: HallReservationForm) => {
|
||||
return request({
|
||||
url: '/hq/hallReservation',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改报告厅预约
|
||||
* @param data
|
||||
*/
|
||||
export const updateHallReservation = (data: HallReservationForm) => {
|
||||
return request({
|
||||
url: '/hq/hallReservation',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除报告厅预约
|
||||
* @param id
|
||||
*/
|
||||
export const delHallReservation = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/hq/hallReservation/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 审核报告厅预约(批准/拒绝)
|
||||
* @param data
|
||||
*/
|
||||
export interface HallReservationAuditForm {
|
||||
/** 预约记录ID列表 */
|
||||
ids: Array<string | number>;
|
||||
/** 审核状态:1-批准,2-拒绝 */
|
||||
status: string;
|
||||
/** 审核备注 */
|
||||
auditRemark?: string;
|
||||
}
|
||||
|
||||
export const auditHallReservation = (data: HallReservationAuditForm) => {
|
||||
return request({
|
||||
// 审核报告厅预约
|
||||
export function auditHallReservation(data: HallReservationAuditForm) {
|
||||
return request<R<any>>({
|
||||
url: '/hq/hallReservation/audit',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// 检查报告厅预约时间是否存在冲突
|
||||
export function checkTimeConflict(data: TimeCheckParams) {
|
||||
return request<R<TimeCheckResult>>({
|
||||
url: '/hq/hallReservation/checkTimeConflict',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
@@ -268,8 +268,7 @@
|
||||
<div class="reservation-details">
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">申请人:</span>
|
||||
<span class="detail-value">{{ record.applicantName }}</span>
|
||||
<span class="detail-phone">({{ record.applicantPhone || '手机号暂无' }})</span>
|
||||
<span class="detail-value"> {{ record.applicantName }}({{ record.applicantPhone || '手机号暂无' }}) </span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">预约时间:</span>
|
||||
@@ -281,6 +280,46 @@
|
||||
<span class="detail-label">预约用途:</span>
|
||||
<span class="detail-value purpose-content">{{ record.reservationPurpose }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">审核建议:</span>
|
||||
<span class="detail-value audit-suggestion-content" :class="getConflictStatusClass(record)">
|
||||
<template v-if="getConflictCheckStatus(record).isLoading">
|
||||
<el-icon class="rotate-loading"><Loading /></el-icon> 正在检查时间冲突...
|
||||
</template>
|
||||
<template v-else-if="getConflictCheckStatus(record).hasError">
|
||||
<el-icon class="text-warning"><Warning /></el-icon> 冲突检查失败,请手动核实
|
||||
</template>
|
||||
<template v-else-if="hasTimeConflict(record)">
|
||||
<el-icon class="text-danger"><Warning /></el-icon> 发现时间冲突,建议拒绝
|
||||
<div class="conflict-details" v-if="getConflictReservations(record).length > 0">
|
||||
<small>冲突详情:</small>
|
||||
<ul class="conflict-list">
|
||||
<li v-for="conflict in getConflictReservations(record)" :key="conflict.id">
|
||||
<el-tag type="warning" size="small" v-if="conflict.internal">本次批次</el-tag>
|
||||
<el-tag type="info" size="small" v-else>已审批</el-tag>
|
||||
{{ conflict.applicantName }} ({{ parseTime(conflict.startTime, '{h}:{i}') }}-{{
|
||||
parseTime(conflict.endTime, '{h}:{i}')
|
||||
}})
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="getConflictCheckStatus(record).isChecked">
|
||||
<el-icon class="text-success"><Check /></el-icon> 无时间冲突,可以批准
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-icon class="text-info"><InfoFilled /></el-icon> 未检查时间冲突
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="manualCheckConflict(record)"
|
||||
:loading="getConflictCheckStatus(record).isLoading"
|
||||
>
|
||||
立即检查
|
||||
</el-button>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -297,6 +336,22 @@
|
||||
{{ auditForm.status === '1' ? '批准' : '拒绝' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- 批准时显示冲突检查统计 -->
|
||||
<div v-if="auditForm.status === '1'" class="conflict-summary">
|
||||
<span class="audit-label">冲突检查统计:</span>
|
||||
<div class="summary-stats">
|
||||
<el-tag type="info" size="small"> 总计:{{ selectedRecords.length }} 个预约 </el-tag>
|
||||
<el-tag type="success" size="small"> 无冲突:{{ getNoConflictCount() }} 个 </el-tag>
|
||||
<el-tag type="danger" size="small" v-if="getConflictCount() > 0"> 有冲突:{{ getConflictCount() }} 个 </el-tag>
|
||||
<el-tag type="warning" size="small" v-if="getUncheckCount() > 0"> 未检查:{{ getUncheckCount() }} 个 </el-tag>
|
||||
</div>
|
||||
<div v-if="getConflictCount() > 0" class="conflict-warning-text">
|
||||
<el-icon><Warning /></el-icon>
|
||||
建议先处理时间冲突的预约,或选择拒绝有冲突的申请
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="audit-remark-section">
|
||||
<span class="audit-label">审核备注:</span>
|
||||
<el-input
|
||||
@@ -323,7 +378,8 @@
|
||||
</template>
|
||||
|
||||
<script setup name="HallAudit" lang="ts">
|
||||
import { listHallReservation, getHallReservation, auditHallReservation, HallReservationAuditForm } from '@/api/hq/hallAudit';
|
||||
import { listHallReservation, getHallReservation, auditHallReservation, HallReservationAuditForm, checkTimeConflict } from '@/api/hq/hallAudit';
|
||||
import dayjs from 'dayjs';
|
||||
import { listHallInfo } from '@/api/hq/hallInfo';
|
||||
import { HallReservationVO, HallReservationQuery, HallReservationForm } from '@/api/hq/hallReservation/types';
|
||||
import { HallInfoVO } from '@/api/hq/hallInfo/types';
|
||||
@@ -388,6 +444,10 @@ const auditForm = ref({
|
||||
const selectedRecords = ref<HallReservationVO[]>([]);
|
||||
const auditLoading = ref(false);
|
||||
|
||||
// 审核建议相关状态
|
||||
const conflictCheckResults = reactive(new Map<string | number, any>());
|
||||
const conflictCheckLoading = reactive(new Set<string | number>());
|
||||
|
||||
const queryParams = ref<HallReservationQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
@@ -683,13 +743,22 @@ const handleReject = async (row?: HallReservationVO) => {
|
||||
};
|
||||
|
||||
/** 打开审核对话框 */
|
||||
const openAuditDialog = (status: string, ids: Array<string | number>, records: HallReservationVO[], title: string) => {
|
||||
const openAuditDialog = async (status: string, ids: Array<string | number>, records: HallReservationVO[], title: string) => {
|
||||
auditForm.value.status = status;
|
||||
auditForm.value.auditRemark = '';
|
||||
auditForm.value.ids = ids;
|
||||
selectedRecords.value = records;
|
||||
auditDialog.title = title;
|
||||
auditDialog.visible = true;
|
||||
|
||||
// 在打开审核对话框时统一检查时间冲突(无论批准/拒绝)
|
||||
// 清除选中记录旧的缓存(尤其是 internal 冲突信息)
|
||||
records.forEach((rec) => conflictCheckResults.delete(rec.id));
|
||||
|
||||
if (records.length > 0) {
|
||||
console.log('审核对话框打开,开始检查时间冲突...');
|
||||
await batchCheckTimeConflict(records);
|
||||
}
|
||||
};
|
||||
|
||||
/** 确认审核操作 */
|
||||
@@ -756,6 +825,242 @@ const handleExport = () => {
|
||||
);
|
||||
};
|
||||
|
||||
/** 检查单个记录的时间冲突 */
|
||||
const checkRecordTimeConflict = async (record: HallReservationVO) => {
|
||||
const recordId = record.id;
|
||||
|
||||
// 如果已经在检查中,跳过
|
||||
if (conflictCheckLoading.has(recordId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果已有缓存结果,直接返回
|
||||
if (conflictCheckResults.has(recordId)) {
|
||||
return conflictCheckResults.get(recordId);
|
||||
}
|
||||
|
||||
try {
|
||||
conflictCheckLoading.add(recordId);
|
||||
|
||||
const res = await checkTimeConflict({
|
||||
hallId: record.hallId,
|
||||
startTime: dayjs(record.startTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
endTime: dayjs(record.endTime).format('YYYY-MM-DD HH:mm:ss'),
|
||||
excludeId: record.id
|
||||
});
|
||||
|
||||
const result = {
|
||||
hasConflict: res.data.hasConflict,
|
||||
conflictList: (res.data.conflictList || []).map((item: any) => ({ ...item, internal: false })),
|
||||
conflictMessage: res.data.conflictMessage || '',
|
||||
checkTime: new Date().getTime()
|
||||
};
|
||||
|
||||
// 缓存结果
|
||||
conflictCheckResults.set(recordId, result);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('检查时间冲突失败:', error);
|
||||
const errorResult = {
|
||||
hasConflict: false,
|
||||
conflictList: [],
|
||||
conflictMessage: '冲突检查失败',
|
||||
checkTime: new Date().getTime(),
|
||||
error: true
|
||||
};
|
||||
conflictCheckResults.set(recordId, errorResult);
|
||||
return errorResult;
|
||||
} finally {
|
||||
conflictCheckLoading.delete(recordId);
|
||||
}
|
||||
};
|
||||
|
||||
/** 批量检查选中记录的时间冲突 */
|
||||
const batchCheckTimeConflict = async (records: HallReservationVO[]) => {
|
||||
console.log('开始批量检查时间冲突,记录数:', records.length);
|
||||
|
||||
const promises = records.map((record) => checkRecordTimeConflict(record));
|
||||
await Promise.all(promises);
|
||||
|
||||
// 前端额外校验勾选记录之间的时间交叉冲突(同一报告厅)
|
||||
addInternalConflictInfo(records);
|
||||
|
||||
console.log('批量时间冲突检查完成');
|
||||
};
|
||||
|
||||
/** 判断两个时间段是否重叠 */
|
||||
const isTimeOverlap = (startA: any, endA: any, startB: any, endB: any) => {
|
||||
const sA = dayjs(startA);
|
||||
const eA = dayjs(endA);
|
||||
const sB = dayjs(startB);
|
||||
const eB = dayjs(endB);
|
||||
return sA.isBefore(eB) && sB.isBefore(eA); // 开始 < 另一结束 且 另一开始 < 结束
|
||||
};
|
||||
|
||||
/** 在选中记录之间标记内部冲突 */
|
||||
const addInternalConflictInfo = (records: HallReservationVO[]) => {
|
||||
// 若不足两条记录无需内部冲突检测
|
||||
if (records.length < 2) return;
|
||||
|
||||
const hallGroups: Record<string, HallReservationVO[]> = {};
|
||||
records.forEach((rec) => {
|
||||
const key = `${rec.hallId || ''}`;
|
||||
hallGroups[key] = hallGroups[key] || [];
|
||||
hallGroups[key].push(rec);
|
||||
});
|
||||
|
||||
Object.values(hallGroups).forEach((list) => {
|
||||
// 对同一报告厅的申请做两两比较
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
for (let j = i + 1; j < list.length; j++) {
|
||||
const r1 = list[i];
|
||||
const r2 = list[j];
|
||||
if (isTimeOverlap(r1.startTime, r1.endTime, r2.startTime, r2.endTime)) {
|
||||
// r1 冲突信息
|
||||
const res1 = conflictCheckResults.get(r1.id) || {
|
||||
hasConflict: false,
|
||||
conflictList: [],
|
||||
conflictMessage: '',
|
||||
checkTime: new Date().getTime()
|
||||
};
|
||||
res1.hasConflict = true;
|
||||
res1.conflictList.push({ ...r2, internal: true });
|
||||
conflictCheckResults.set(r1.id, res1);
|
||||
|
||||
// r2 冲突信息
|
||||
const res2 = conflictCheckResults.get(r2.id) || {
|
||||
hasConflict: false,
|
||||
conflictList: [],
|
||||
conflictMessage: '',
|
||||
checkTime: new Date().getTime()
|
||||
};
|
||||
res2.hasConflict = true;
|
||||
res2.conflictList.push({ ...r1, internal: true });
|
||||
conflictCheckResults.set(r2.id, res2);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 获取记录的冲突检查结果 */
|
||||
const getConflictResult = (record: HallReservationVO) => {
|
||||
return (
|
||||
conflictCheckResults.get(record.id) || {
|
||||
hasConflict: false,
|
||||
conflictList: [],
|
||||
conflictMessage: '未检查',
|
||||
checkTime: 0
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/** 检查是否存在时间冲突(同步方法) */
|
||||
const hasTimeConflict = (record: HallReservationVO): boolean => {
|
||||
const result = getConflictResult(record);
|
||||
return result.hasConflict;
|
||||
};
|
||||
|
||||
/** 获取时间冲突的预约列表(同步方法) */
|
||||
const getConflictReservations = (record: HallReservationVO) => {
|
||||
const result = getConflictResult(record);
|
||||
return result.conflictList || [];
|
||||
};
|
||||
|
||||
/** 获取冲突检查状态 */
|
||||
const getConflictCheckStatus = (record: HallReservationVO) => {
|
||||
const isLoading = conflictCheckLoading.has(record.id);
|
||||
const result = getConflictResult(record);
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
isChecked: result.checkTime > 0,
|
||||
hasError: result.error || false,
|
||||
result
|
||||
};
|
||||
};
|
||||
|
||||
/** 获取冲突状态对应的CSS类 */
|
||||
const getConflictStatusClass = (record: HallReservationVO) => {
|
||||
const status = getConflictCheckStatus(record);
|
||||
|
||||
if (status.isLoading) {
|
||||
return 'checking-conflict';
|
||||
}
|
||||
|
||||
if (status.hasError) {
|
||||
return 'check-error';
|
||||
}
|
||||
|
||||
if (status.isChecked) {
|
||||
return hasTimeConflict(record) ? 'has-conflict' : 'no-conflict';
|
||||
}
|
||||
|
||||
return 'not-checked';
|
||||
};
|
||||
|
||||
/** 获取审核建议文本 */
|
||||
const getAuditSuggestionText = (record: HallReservationVO) => {
|
||||
const status = getConflictCheckStatus(record);
|
||||
|
||||
if (status.isLoading) {
|
||||
return '正在检查时间冲突...';
|
||||
}
|
||||
|
||||
if (status.hasError) {
|
||||
return '冲突检查失败,请手动核实';
|
||||
}
|
||||
|
||||
if (status.isChecked) {
|
||||
return hasTimeConflict(record) ? '发现时间冲突,建议拒绝' : '无时间冲突,可以批准';
|
||||
}
|
||||
|
||||
return '未检查时间冲突';
|
||||
};
|
||||
|
||||
/** 手动触发时间冲突检查 */
|
||||
const manualCheckConflict = async (record: HallReservationVO) => {
|
||||
// 清除缓存,强制重新检查
|
||||
conflictCheckResults.delete(record.id);
|
||||
await checkRecordTimeConflict(record);
|
||||
};
|
||||
|
||||
/** 获取无冲突的预约数量 */
|
||||
const getNoConflictCount = () => {
|
||||
return selectedRecords.value.filter((record) => {
|
||||
const status = getConflictCheckStatus(record);
|
||||
return status.isChecked && !hasTimeConflict(record);
|
||||
}).length;
|
||||
};
|
||||
|
||||
/** 获取有冲突的预约数量 */
|
||||
const getConflictCount = () => {
|
||||
return selectedRecords.value.filter((record) => {
|
||||
const status = getConflictCheckStatus(record);
|
||||
return status.isChecked && hasTimeConflict(record);
|
||||
}).length;
|
||||
};
|
||||
|
||||
/** 获取未检查的预约数量 */
|
||||
const getUncheckCount = () => {
|
||||
return selectedRecords.value.filter((record) => {
|
||||
const status = getConflictCheckStatus(record);
|
||||
return !status.isChecked && !status.isLoading;
|
||||
}).length;
|
||||
};
|
||||
|
||||
/** 获取预约状态文本 */
|
||||
const getStatusText = (status: string) => {
|
||||
const statusMap: { [key: string]: string } = {
|
||||
'0': '审核中',
|
||||
'1': '已批准',
|
||||
'2': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || status;
|
||||
};
|
||||
|
||||
/** 页面加载完成后的初始化 */
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getHallList();
|
||||
@@ -1057,7 +1362,7 @@ onMounted(() => {
|
||||
.hall-id {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
background: #f0f2f5;
|
||||
/* background: #f0f2f5; */
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@@ -1192,6 +1497,126 @@ onMounted(() => {
|
||||
font-size: 14px;
|
||||
resize: none !important;
|
||||
}
|
||||
|
||||
/* 审核建议样式 - 增强版 */
|
||||
.audit-suggestion-content {
|
||||
line-height: 1.6;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.audit-suggestion-content .el-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* 冲突详情在新行展示 */
|
||||
.audit-suggestion-content .conflict-details {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
/* 不同状态的颜色 */
|
||||
.audit-suggestion-content.checking-conflict {
|
||||
color: #409eff; /* 检查中 - 蓝色 */
|
||||
}
|
||||
|
||||
.audit-suggestion-content.check-error {
|
||||
color: #e6a23c; /* 检查错误 - 橙色 */
|
||||
}
|
||||
|
||||
.audit-suggestion-content.has-conflict {
|
||||
color: #f56c6c; /* 有冲突 - 红色 */
|
||||
}
|
||||
|
||||
.audit-suggestion-content.no-conflict {
|
||||
color: #67c23a; /* 无冲突 - 绿色 */
|
||||
}
|
||||
|
||||
.audit-suggestion-content.not-checked {
|
||||
color: #909399; /* 未检查 - 灰色 */
|
||||
}
|
||||
|
||||
/* 冲突详情样式 */
|
||||
.conflict-details {
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
background-color: #fef2f2;
|
||||
border-left: 3px solid #f56c6c;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.conflict-list {
|
||||
margin: 4px 0 0 0;
|
||||
padding-left: 16px;
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.conflict-list li {
|
||||
margin: 2px 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 文本颜色工具类 */
|
||||
.text-success {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.text-info {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.rotate-loading {
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 冲突检查统计样式 */
|
||||
.conflict-summary {
|
||||
margin: 0px 0;
|
||||
padding: 0px;
|
||||
/* background-color: #f9fafe; */
|
||||
border-radius: 6px;
|
||||
/* border: 1px solid #e4e7ed; */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap; /* 超出时自动换行 */
|
||||
}
|
||||
|
||||
.summary-stats {
|
||||
display: inline-flex; /* 与标签同行 */
|
||||
gap: 8px;
|
||||
margin-top: 0; /* 移除顶部间距,保持同一行 */
|
||||
flex-wrap: wrap; /* 达到最大宽度时自身也可换行 */
|
||||
}
|
||||
|
||||
.conflict-warning-text {
|
||||
margin-top: 8px;
|
||||
color: #e6a23c;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
|
||||
Reference in new issue
Block a user