基调是简单快速:hover 颜色 0.15s 过渡(全局规则,只过渡颜色不碰布局)、 对话框入场淡入加 6px 上移、tab 下划线改成共享的一条并在切换时滑动、tab 内容 out-in 淡切、配置折叠箭头居右随展开旋转 90 度、订阅下拉 0.12s 出入场。 不引 animate.css:这些效果各自十几行 CSS,引库只多不少。 配置展开和拨开链式代理时,若详情底部长出窗口则平滑滚进视野;tab 切换 不触发,来回切时页面跟着跳只会晕。二次确认对话框点遮罩可关闭,内边距 挪进内层,否则点在 dialog 自己的 padding 上也会被当成遮罩。新增入站的 表单不做遮罩关闭:误点会丢掉填了一半的内容。对话框标题改为「新增路由」 「编辑路由」。select 换自绘箭头,原生箭头贴边且位置调不了。
99 lines
2.3 KiB
Vue
99 lines
2.3 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* 危险动作的二次确认。
|
|
*
|
|
* 原生 <dialog>,焦点捕获、Esc 关闭、背景遮罩都是浏览器自带的(同 InboundForm)。
|
|
* 不用 window.confirm 是因为它的样式游离在页面之外,按钮文案也固定是「确定」,
|
|
* 对不上具体要做的动作。打开时焦点落在「取消」上,回车不会误删。
|
|
*/
|
|
import { ref, watch } from 'vue'
|
|
|
|
const props = defineProps<{
|
|
open: boolean
|
|
message: string
|
|
/** 确认按钮上的动词,比如「删除」 */
|
|
action: string
|
|
}>()
|
|
|
|
const emit = defineEmits<{ close: []; confirm: [] }>()
|
|
|
|
const dialog = ref<HTMLDialogElement | null>(null)
|
|
watch(
|
|
() => props.open,
|
|
(open) => {
|
|
if (open) dialog.value?.showModal()
|
|
else dialog.value?.close()
|
|
},
|
|
)
|
|
|
|
/** 点在遮罩上就关闭。内容区的点击 target 是 .body 里的元素,到不了 dialog 本身。 */
|
|
function onBackdrop(event: MouseEvent) {
|
|
if (event.target === dialog.value) emit('close')
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<dialog
|
|
ref="dialog"
|
|
class="dlg"
|
|
@click="onBackdrop"
|
|
@close="emit('close')"
|
|
@cancel.prevent="emit('close')"
|
|
>
|
|
<div class="body">
|
|
<p class="msg">{{ message }}</p>
|
|
<footer class="acts">
|
|
<button type="button" class="btn" @click="emit('close')">取消</button>
|
|
<button type="button" class="btn go" @click="emit('confirm')">{{ action }}</button>
|
|
</footer>
|
|
</div>
|
|
</dialog>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.dlg {
|
|
width: min(400px, calc(100vw - 2 * var(--s-5)));
|
|
/* 内边距放在 .body 上:dialog 自己的内边距区域点了会被当成遮罩点击 */
|
|
padding: 0;
|
|
border: 1px solid var(--c-border-strong);
|
|
border-radius: var(--r-lg);
|
|
background: var(--c-surface);
|
|
color: var(--c-text);
|
|
}
|
|
|
|
.body {
|
|
padding: var(--s-5);
|
|
}
|
|
|
|
.dlg::backdrop {
|
|
background: rgb(0 0 0 / 0.45);
|
|
}
|
|
|
|
.msg {
|
|
line-height: var(--line);
|
|
}
|
|
|
|
.acts {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: var(--s-2);
|
|
margin-top: var(--s-5);
|
|
}
|
|
|
|
.btn {
|
|
padding: var(--s-2) var(--s-4);
|
|
border: 1px solid var(--c-border-strong);
|
|
border-radius: var(--r-sm);
|
|
background: var(--c-surface);
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* 确认键用危险色实底:这个对话框只出现在删除这类不可逆动作前 */
|
|
.go {
|
|
border-color: transparent;
|
|
background: var(--c-danger);
|
|
color: var(--c-bg);
|
|
font-weight: 500;
|
|
}
|
|
</style>
|