数字督察一体化平台-前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

886 lines
28 KiB

<template>
<el-dialog
:title="mode === 'add' ? '添加登记来件' : '修改登记来件'"
v-model="visibleProxy"
width="70vw"
top="2vh"
style="margin-bottom: 0"
>
<el-scrollbar max-height="76vh">
<el-form
ref="formRef"
:model="formData"
:label-width="150"
class="form-layout"
style="min-height: 66vh"
:rules="rules"
>
<!-- 投诉信息 -->
<h2>投诉信息</h2>
<div class="add-negation-container">
<!-- 来源 -->
<el-row>
<el-col :span="12">
<el-form-item label="来源" prop="sourcePath">
<el-cascader
v-model="formData.sourcePath"
:options="dict.sourceTableAndLevel"
:props="{ emitPath: true, checkStrictly: false }"
clearable
filterable
show-all-levels
style="width: 100%"
placeholder="请选择来源(一级/二级)"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="业务类别" prop="handleMethod">
<el-select
v-model="formData.businessTypeCode"
placeholder="业务类别"
clearable
>
<el-option
v-for="item in dict.businessType"
:key="item.id"
:label="item.dictLabel"
:value="item.dictValue"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 编号 + 受理时间 -->
<el-row>
<el-col :span="12">
<el-form-item label="编号" prop="originId">
<el-input
v-model="formData.originId"
:placeholder="formData.originIdSkip ? '无' : '请输入编号'"
:disabled="mode === 'edit' || formData.originIdSkip"
>
<template #append>
<el-checkbox
v-model="formData.originIdSkip"
@change="(v) => v && (formData.originId = '')"
:disabled="mode=== 'edit'"
>
</el-checkbox>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="登记/受理时间" prop="discoveryTime">
<el-date-picker
v-model="formData.discoveryTime"
type="datetime"
placeholder="请选择"
value-format="YYYY-MM-DDTHH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 来件人姓名 + 身份证号码 -->
<el-row>
<el-col :span="12">
<el-form-item label="来件人姓名" prop="responderName">
<el-input
v-model="formData.responderName"
:placeholder="formData.responderNameSkip ? '无' : '请输入来件人姓名'"
:disabled="formData.responderNameSkip"
@blur="onAutoCheckDuplicate"
>
<template #append>
<el-checkbox
v-model="formData.responderNameSkip"
@change="(v) => v && (formData.responderName = '')"
>
</el-checkbox>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<div style="display: flex; align-items: flex-start; gap: 8px;">
<el-form-item label="身份证号码" prop="responderIdCode" style="flex: 1; margin-bottom: 0;">
<el-input
v-model="formData.responderIdCode"
:placeholder="formData.responderIdCodeSkip ? '无' : '请输入身份证号码'"
:disabled="formData.responderIdCodeSkip"
@blur="onAutoCheckDuplicate"
>
<template #append>
<el-checkbox
v-model="formData.responderIdCodeSkip"
@change="(v) => v && (formData.responderIdCode = '')"
>
</el-checkbox>
</template>
</el-input>
</el-form-item>
<el-button
type="primary"
:loading="duplicateLoading"
@click="onCheckDuplicate"
>
查看
</el-button>
<span
v-if="duplicateHintVisible"
style="margin-left: 8px; font-size: 12px;"
:style="{ color: duplicateCount > 0 ? '#fa541c' : '#8c8c8c' }"
>
<template v-if="autoDuplicateLoading">查重中…</template>
<template v-else-if="duplicateCount > 0">疑似重复 {{ duplicateCount }} 条</template>
<template v-else>未发现重复</template>
</span>
</div>
</el-col>
</el-row>
<!-- 联系电话 + 被投诉二级机构 -->
<el-row>
<el-col :span="12">
<el-form-item label="联系电话" prop="responderPhone">
<el-input
v-model="formData.responderPhone"
:placeholder="formData.responderPhoneSkip ? '无' : '请输入联系电话'"
:disabled="formData.responderPhoneSkip"
@blur="onAutoCheckDuplicate"
>
<template #append>
<el-checkbox
v-model="formData.responderPhoneSkip"
@change="(v) => v && (formData.responderPhone = '')"
>
</el-checkbox>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="被投诉二级机构" prop="secondDepartId" >
<depart-tree-select
v-model="formData.secondDepartId"
:check-strictly="true"
@node-click="(row) => (formData.secondDepartName = row.shortName)"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 来件内容 -->
<el-row>
<el-col :span="24">
<el-form-item label="来件内容" prop="thingDesc">
<el-input v-model="formData.thingDesc" type="textarea" :autosize="{ minRows: 4 }" />
</el-form-item>
</el-col>
</el-row>
<!-- 涉嫌问题 + 是否重复件 -->
<el-row>
<el-col :span="12">
<el-form-item label="涉嫌问题" prop="involveProblemIdList">
<el-select v-model="formData.involveProblemIdList" multiple clearable style="width: 100%">
<el-option
v-for="item in dict.suspectProblem"
:key="item.dictValue"
:value="item.dictValue"
:label="item.dictLabel"
>
<!-- 复选框展示 -->
<el-checkbox :model-value="(formData.involveProblemIdList || []).includes(item.dictValue)">
{{ item.dictLabel }}
</el-checkbox>
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否重复件" prop="repeatt">
<el-radio-group v-model="formData.repeatt" clearable>
<el-radio v-for="item in dict.yesNo" :key="item.id" :label="item.dictValue">
{{ item.dictLabel }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<!-- 标签 -->
<el-row>
<el-col :span="18">
<el-form-item label="标签" prop="tags">
<el-checkbox-group v-model="formData.tags">
<el-checkbox v-for="item in dict.sfssTags" :key="item.id" :label="item.dictValue">
{{ item.dictLabel }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-col>
</el-row>
<!-- 办理方式 -->
<el-row>
<el-col :span="12">
<el-form-item label="办理方式" prop="handleMethod">
<el-radio-group v-model="formData.handleMethod" clearable>
<el-radio v-for="item in dict.handleMethodType" :key="item.id" :label="item.dictValue">
{{ item.dictLabel }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-form-item v-if="mode === 'add'" label="附件" prop="thingFiles">
<file-upload
v-model:files="formData.thingFiles"
tips="为便于“办理单位”更全面了解问题详情,请上传相关附件,如现场督察、数字督察等相关照片、视频及其他佐证材料。"
/>
</el-form-item>
</div>
<!-- 办理单位 -->
<el-divider v-if="mode === 'add'" />
<h2 v-if="mode === 'add'">办理单位</h2>
<div v-if="mode === 'add'" class="add-negation-container">
<el-form-item label="主办层级" prop="hostLevel" :rules="{ required: true, message: '请选择主办层级', trigger: ['blur'] }">
<el-select style="width: 280px" v-model="formData.hostLevel" @change="handleChangeHostLevel">
<el-option v-for="item in dict.hostLevel" :key="item.id" :label="item.dictLabel" :value="item.dictValue" />
</el-select>
<div class="tips mt-10">
<p>如主办层级 为 市局主办, 则由<span :data-active="formData.hostLevel === HostLevel.FIRST">督察支队</span>办理;</p>
<p>如主办层级为 二级机构主办,则由<span :data-active="formData.hostLevel === HostLevel.SECOND">督察部门</span>办理,可进一步下发;</p>
<p>如主办层级为 三级机构主办,则由<span :data-active="formData.hostLevel === HostLevel.THREE">所队</span>办理。</p>
</div>
</el-form-item>
<el-form-item label="指定办理单位" prop="departId" :rules="{ required: true, message: '请选择办理单位', trigger: ['blur'] }">
<div class="flex gap">
<div style="width: 280px">
<template v-if="formData.hostLevel === HostLevel.THREE">
<depart-tree-select v-model="formData.departId" v-loading="departLoading" />
</template>
<el-tree-select v-else :data="departs" :props="{ label: 'shortName', value: 'id' }" node-key="id" clearable filterable v-model="formData.departId" @node-click="handleSelectDepart" check-strictly style="width: 280px" v-loading="departLoading" />
</div>
<el-button type="primary" @click="handleLinkDepart" text v-if="formData.hostLevel !== HostLevel.FIRST">关联问题涉及单位</el-button>
</div>
<div class="tips mt-10">
<p>问题涉及单位 指与该问题相关的单位;</p>
<p>指定办理单位 指将问题分派给哪个单位办理。</p>
</div>
</el-form-item>
</div>
<!-- 初核时限 -->
<h2 v-if="mode === 'add'">初核时限</h2>
<p v-if="mode === 'add'" style="color: #909399; font-size: 14px; line-height: 1.6; padding-left: 200px;">
该信件的初核时限需在<span style="color: var(--el-color-danger); font-weight: 600;"> 4个工作日 </span>之内完成初核。
</p>
<!-- 办理时限 -->
<h2 v-if="mode === 'add'">办理时限</h2>
<div v-if="mode === 'add'" class="add-negation-container">
<el-form-item label="办理时限" prop="timeLimit" :rules="{ required: true, message: '请选择办理时限', trigger: ['blur'] }">
<time-limit-select :key="timeLimitKey" v-model="formData.timeLimit" v-model:maxSignDuration="formData.maxSignDuration" v-model:maxHandleDuration="formData.maxHandleDuration" v-model:maxExtensionDuration="formData.maxExtensionDuration" />
</el-form-item>
</div>
<!-- 审批流程 -->
<h2 v-if="mode === 'add'">审批流程</h2>
<div v-if="mode === 'add'" class="add-negation-container">
<el-form-item label="审批流程" prop="approvalFlow" :rules="{ required: true, message: '请选择审批流程', trigger: ['blur'] }">
<el-radio-group v-model="formData.approvalFlow" v-if="userStore.user.roleCodes.includes('admin_1')">
<el-radio v-for="item in dict.approvalFlow" :key="item.dictCode" :value="item.dictValue">{{ item.dictLabel }}{{ item.remark ? `(${item.remark})` : "" }}</el-radio>
</el-radio-group>
<el-radio-group v-model="formData.approvalFlow" v-else>
<el-radio value="2">二级审批(所队一>二级机构)</el-radio>
</el-radio-group>
<div class="tips mt-10">
<p v-if="userStore.user.roleCodes.includes('admin_1')">三级审核 问题提交办结时,需经过"所队—>二级机构—>市局"三级审核,通过后方可办结;</p>
<p>二级审核 问题提交办结时,仅需经过"所队—>二级机构"两级审核,通过后即可办结;</p>
</div>
</el-form-item>
</div>
</el-form>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="visibleProxy = false" size="large">取消</el-button>
<el-button type="primary" @click="onSubmit" size="large" :loading="loading || submitLoading">
{{ mode === 'add' ? '添加' : '修改' }}
</el-button>
</div>
</template>
</el-dialog>
<DuplicateDrawerWithDetail
v-model="duplicateDrawerVisible"
:dict="dict"
:query="{
responderIdCode: formData.responderIdCode,
responderName: formData.responderName,
responderPhone: formData.responderPhone
}"
:requestFn="maileRepeatt"
detailIdKey="negativeId"
@opened-detail="handleDuplicateRowClick"
/>
<!-- 办理详情 -->
<NegativeDialog
v-model="detailDialogVisible"
:id="detailNegativeId"
@close="detailDialogVisible = false"
/>
<!-- 局长信箱办理详情 -->
<NegativeMailboxDialog
v-model="mailboxDetailVisible"
:id="detailNegativeId"
@close="mailboxDetailVisible = false"
/>
</template>
<script setup>
import {computed, ref, watch} from "vue";
import {maileRepeatt, getComplaintCollectionDetail, updateComplaintCollection, addComplaintCollection} from "@/api/data/complaintCollection.ts";
import dayjs from "dayjs";
import feedback from "@/utils/feedback.ts";
import DuplicateDrawerWithDetail from "@/views/data/DuplicateDrawerWithDetail.vue";
import useUserStore from "@/stores/modules/user.ts";
import {secondList, listByFirstHost} from "@/api/system/depart";
import useCatchStore from "@/stores/modules/catch";
const catchStore = useCatchStore();
// 内部表单数据
const formData = ref(createEmptyForm());
function createEmptyForm() {
return {
// 来源
sourcePath: [],
sourceTable: '',
sourceTableSubOne: '',
// 编号
originId: '',
originIdSkip: false,
// 受理时间
discoveryTime: '',
// 来件人
responderName: '',
responderNameSkip: false,
responderIdCode: '',
responderIdCodeSkip: false,
responderPhone: '',
responderPhoneSkip: false,
// 被投诉机构
secondDepartId: '',
secondDepartName: '',
// 内容
thingDesc: '',
involveProblemIdList: [],
repeatt: '',
handleMethod: '',
// 标签
tags: [],
businessTypeCode: '',
businessTypeName: '',
// 附件
thingFiles: [],
// 办理信息
hostLevel: '3',
departId: '',
departName: '',
timeLimit: '',
maxSignDuration: null,
maxHandleDuration: null,
maxExtensionDuration: null,
approvalFlow: '',
// 编辑时需要的 id
id: '',
negativeId: '',
};
}
// 内部校验规则
const rules = {
sourcePath: [
{required: true, message: "请选择来源(一级/二级)", trigger: "change"},
],
discoveryTime: [{required: true, message: '请选择受理时间', trigger: 'change'}],
secondDepartId: [{required: true, message: '请选择被投诉二级机构', trigger: 'change'}],
thingDesc: [{required: true, message: '请输入来件内容', trigger: 'blur'}],
involveProblemIdList: [{required: true, message: '请选择涉嫌问题', trigger: 'change'}],
repeatt: [{required: true, message: '请选择是否重复件', trigger: 'change'}],
hostLevel: [{required: true, message: '请选择主办层级', trigger: 'change'}],
departId: [{required: true, message: '请选择办理单位', trigger: 'change'}],
timeLimit: [{required: true, message: '请选择办理时限', trigger: 'change'}],
approvalFlow: [{required: true, message: '请选择审批流程', trigger: 'change'}],
};
// 1. 用 computed 包装 storeDict
const storeDict = computed(() =>
catchStore.getDicts([
"businessType",
"suspectProblem",
"specialSupervision",
"checkStatus",
"policeType",
"timeLimit",
"approvalFlow",
"sfssSourceTable",
"sfssTags",
]) || {}
);
// 2. 本地静态字典
const localDict = {
yesNo: [
{id: 1, dictLabel: "是", dictValue: "1"},
{id: 2, dictLabel: "否", dictValue: "0"},
],
handleMethodType: [
{id: 1, dictLabel: "自办", dictValue: "0"},
{id: 2, dictLabel: "下发", dictValue: "1"},
],
};
// 3. sourceTableAndLevel 依赖 storeDict.value
const sourceTableAndLevel = computed(() => {
const list = storeDict.value?.sfssSourceTable || [];
if (!list || list.length === 0) return [];
// 一级:remark 为空
const parents = list.filter(d => !d.remark);
// 二级:remark 有值
const children = list.filter(d => d.remark);
return parents.map(p => ({
label: p.dictLabel,
value: String(p.dictValue),
children: children
.filter(c => String(c.remark) === String(p.dictValue))
.map(c => ({
label: c.dictLabel,
value: String(c.dictValue),
})),
}));
});
// 4. dict 合并
const dict = computed(() => ({
...storeDict.value,
...localDict,
sourceTableAndLevel: sourceTableAndLevel.value,
}));
const HostLevel = {
FIRST: '1',
SECOND: '2',
THREE: '3'
};
const userStore = useUserStore();
const props = defineProps({
modelValue: Boolean,
mode: { type: String, default: "add" },
id: { type: String, default: "" },
negativeId: { type: String, default: "" },
loading: { type: Boolean, default: false },
});
const emit = defineEmits(["update:modelValue", "updateSuccess"]);
const formRef = ref();
const submitLoading = ref(false);
const timeLimitKey = ref(0);
const visibleProxy = computed({
get: () => props.modelValue,
set: (v) => emit("update:modelValue", v),
});
watch(
() => visibleProxy.value,
async (v) => {
if (v) {
// 重置表单
timeLimitKey.value++
formData.value = createEmptyForm();
duplicateDrawerVisible.value = false
duplicateLoading.value = false
autoDuplicateLoading.value = false
duplicateHintVisible.value = false
duplicateCount.value = 0
duplicateCache.value = []
duplicateList.value = []
getDeparts()
if (props.mode === 'edit' && (props.id || props.negativeId)) {
try {
const res = await getComplaintCollectionDetail({ id: props.id, negativeId: props.negativeId });
const r = res?.data ?? res ?? {};
formData.value.id = r.id ?? '';
formData.value.sourceTable = r.sourceTable ?? '';
formData.value.sourceTableSubOne = r.sourceTableSubOne ?? '';
formData.value.sourcePath = [r.sourceTable, r.sourceTableSubOne].filter(Boolean);
formData.value.originId = r.originId ?? '';
formData.value.discoveryTime = r.discoveryTime ?? '';
formData.value.responderName = r.responderName ?? '';
formData.value.responderIdCode = r.responderIdCode ?? '';
formData.value.responderPhone = r.responderPhone ?? '';
formData.value.secondDepartId = r.secondDepartId ?? '';
formData.value.secondDepartName = r.secondDepartName ?? '';
formData.value.thingDesc = r.thingDesc ?? '';
formData.value.repeatt = r.repeatt ?? '';
formData.value.handleMethod = r.handleMethod ?? '';
formData.value.involveProblemIdList = splitToArray(r.involveProblemIdList ?? r.involveProblem);
formData.value.tags = splitToArray(r.tags ?? r.tag);
formData.value.businessTypeName = r.businessTypeName ?? '';
formData.value.businessTypeCode = r.businessTypeCode ?? '';
formData.value.thingFiles = normalizeThingFiles(r.thingFiles);
formData.value.hostLevel = r.hostLevel ?? '3';
formData.value.departId = r.departId ?? '';
formData.value.departName = r.departName ?? '';
formData.value.timeLimit = r.timeLimit ?? '';
formData.value.maxSignDuration = r.maxSignDuration ?? null;
formData.value.maxHandleDuration = r.maxHandleDuration ?? null;
formData.value.maxExtensionDuration = r.maxExtensionDuration ?? null;
formData.value.approvalFlow = r.approvalFlow ?? '';
formData.value.negativeId = r.negativeId ?? '';
} catch (e) {
console.error("获取详情失败", e);
feedback.notifyError("获取数据失败,请重试");
visibleProxy.value = false;
}
}
}
}
);
watch(
() => formData.value.hostLevel,
() => {
getDeparts();
}
);
const departs = ref([]);
const departLoading = ref(false);
async function getDeparts() {
if (!formData.value.hostLevel) return;
departLoading.value = true;
try {
if (formData.value.hostLevel === HostLevel.FIRST) {
departs.value = await listByFirstHost();
} else if (formData.value.hostLevel === HostLevel.SECOND) {
departs.value = await secondList();
} else {
departs.value = [];
}
} catch (e) {
console.error(e);
} finally {
departLoading.value = false;
}
}
function handleSelectDepart(row) {
formData.value.departName = row.shortName;
}
function handleLinkDepart() {
if (formData.value.hostLevel === HostLevel.SECOND) {
feedback.msgWarning("当前选择二级机构主办,指定办理单位请选择二级机构!");
return;
}
formData.value.departId = formData.value.secondDepartId;
formData.value.departName = formData.value.secondDepartName;
}
function handleChangeHostLevel(val) {
if (val === HostLevel.FIRST) {
formData.value.departId = "";
}
if (val === HostLevel.SECOND) {
formData.value.approvalFlow = "3";
}
}
watch(
() => formData.value.sourcePath,
(path) => {
if (!path || path.length === 0) return;
formData.value.sourceTable = (path && path[0]) || "";
formData.value.sourceTableSubOne = (path && path[1]) || "";
},
{ deep: true }
);
function fillFiledName() {
const code = formData.value.businessTypeCode;
const hit = dict.value?.businessType?.find(
(d) => String(d.dictValue) === String(code)
);
formData.value.businessTypeName = hit?.dictLabel || "";
}
function splitToArray(val) {
if (!val) return [];
if (Array.isArray(val)) return val;
if (typeof val === 'string') {
return val.split(/[,,]/).map(s => s.trim()).filter(Boolean);
}
return [];
}
function normalizeThingFiles(val) {
if (!val) return [];
if (Array.isArray(val)) return val.map(f => ({...f, loading: false, percent: 100}));
if (typeof val === 'string' && val.trim().startsWith('[')) {
try {
return JSON.parse(val).map(f => ({...f, loading: false, percent: 100}));
} catch {}
}
if (typeof val === 'string') {
return val.split(/[,,]/).map(s => s.trim()).filter(Boolean).map(p => ({
filePath: p,
fileName: p.split('/').pop() || p,
loading: false,
percent: 100,
}));
}
return [];
}
async function onSubmit() {
if (!formRef.value) return;
try {
await formRef.value.validate();
} catch {
feedback.msgWarning("请完善必填信息");
return;
}
fillFiledName();
// 添加模式:内部调用 API
if (props.mode === 'add') {
submitLoading.value = true;
try {
await addComplaintCollection({...formData.value});
feedback.msgSuccess("添加成功");
visibleProxy.value = false;
emit("updateSuccess");
} catch (e) {
console.error("添加失败", e);
} finally {
submitLoading.value = false;
}
return;
}
// 修改模式:内部处理 API 调用
submitLoading.value = true;
try {
await updateComplaintCollection({...formData.value});
feedback.msgSuccess("修改成功");
visibleProxy.value = false;
emit("updateSuccess");
} catch (e) {
console.error("修改失败", e);
} finally {
submitLoading.value = false;
}
}
// 查重
const duplicateDrawerVisible = ref(false)
const duplicateList = ref([])
const duplicateLoading = ref(false)
const onCheckDuplicate = async () => {
duplicateDrawerVisible.value = true
}
const duplicateCount = ref(0)
const duplicateCache = ref([])
const autoDuplicateLoading = ref(false)
const duplicateHintVisible = ref(false)
const fetchDuplicate = async () => {
const body = {
responderIdCode: formData.value.responderIdCode,
responderName: formData.value.responderName,
responderPhone: formData.value.responderPhone,
}
const res = await maileRepeatt(body)
return res?.complaintCollectionRepeatDTOS || []
}
const onAutoCheckDuplicate = async () => {
const { responderIdCode, responderName, responderPhone } = formData.value
if (!responderIdCode && !responderName && !responderPhone) {
duplicateCount.value = 0
duplicateCache.value = []
duplicateHintVisible.value = false
return
}
duplicateHintVisible.value = true
autoDuplicateLoading.value = true
try {
const list = await fetchDuplicate()
duplicateCount.value = list.length
duplicateCache.value = list
} catch (e) {
} finally {
autoDuplicateLoading.value = false
}
}
// 查重结果的详情弹窗
const detailDialogVisible = ref(false)
const mailboxDetailVisible = ref(false)
const detailNegativeId = ref("")
// 处理查重行点击事件
const handleDuplicateRowClick = (row) => {
// 复用 ComplaintCollection.vue 中的逻辑
if (row.problemSources === '局长信箱') {
mailboxDetailVisible.value = true
detailNegativeId.value = row.originId
} else {
detailDialogVisible.value = true
detailNegativeId.value = row.negativeId
}
}
</script>
<style lang="scss" scoped>
.form-layout {
padding: 0 34px;
}
.dialog-footer {
padding: 16px 24px 24px;
}
.add-negation-container {
padding: 0 60px;
}
.tips {
margin-top: 10px;
[data-active="true"] {
color: var(--danger-color);
}
}
:deep() {
.el-form-item__content {
flex-direction: column;
align-items: flex-start;
}
.block.el-radio-group {
display: block;
}
.block.el-radio-group .el-radio {
display: block;
}
}
.duplicate-drawer {
--el-drawer-padding-primary: 20px;
}
.drawer-header {
display: flex;
align-items: center;
gap: 8px;
font-size: 18px;
font-weight: 600;
color: #d46b08;
}
.drawer-body {
padding-top: 8px;
}
.drawer-alert {
margin-bottom: 16px;
}
.id-highlight {
margin-bottom: 20px;
padding: 12px 16px;
background: #fafafa;
border-left: 4px solid #faad14;
font-size: 14px;
line-height: 22px;
}
.id-highlight span {
margin-left: 8px;
font-weight: 600;
color: #d4380d;
}
.drawer-header.danger {
color: #0d73ee;
}
.drawer-header .title-group {
display: flex;
flex-direction: column;
}
.drawer-header .title {
font-size: 20px;
font-weight: 600;
line-height: 1.2;
}
.drawer-header .sub-title {
font-size: 13px;
color: #8c8c8c;
margin-top: 2px;
}
h2 {
margin: 16px 0 8px;
padding-left: 60px;
font-size: 16px;
font-weight: 600;
}
</style>