新增: 全局对话框服务 AppDialog,Promise 风格 appAlert/appConfirm/appPrompt 替代原生弹窗

This commit is contained in:
lxy
2026-08-25 12:30:12 +08:00
parent 0907a104cf
commit 8eef4606c5
9 changed files with 253 additions and 25 deletions
+5 -4
View File
@@ -5,6 +5,7 @@
import { ref, computed, watch } from 'vue'
import type { LibItem, Slide } from '../../core/types'
import { store, resolveBg } from '../../core/store'
import { appPrompt, appConfirm } from '../../core/dialog'
import ElementView from '../editor/ElementView.vue'
const props = defineProps<{ visible: boolean }>()
@@ -91,9 +92,9 @@ function onOpen(id: string) {
}
}
function onRename(id: string) {
async function onRename(id: string) {
const cur = store.getLibrary().find(x => x.id === id)
const name = prompt('重命名', cur ? cur.name : '')
const name = await appPrompt('重命名', { defaultValue: cur ? cur.name : '' })
if (name != null && name.trim()) {
store.renameInLibrary(id, name.trim())
bump()
@@ -106,8 +107,8 @@ function onDuplicate(id: string) {
bump()
}
function onDelete(id: string) {
if (confirm('删除这份演示?此操作不可撤销')) {
async function onDelete(id: string) {
if (await appConfirm('删除这份演示?', '此操作不可撤销', { danger: true, okText: '删除' })) {
store.deleteFromLibrary(id)
toast('已删除')
bump()
+25 -13
View File
@@ -4,6 +4,7 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { store, resolveBg } from '../../core/store'
import { appConfirm } from '../../core/dialog'
import type { PageTemplate } from '../../core/types'
import ElementView from '../editor/ElementView.vue'
@@ -65,8 +66,8 @@ function onPick(tpl: PageTemplate) {
}
/* ---------- 删除自存模板 ---------- */
function onDelete(tpl: PageTemplate) {
if (confirm('删除模板「' + tpl.name + '」')) {
async function onDelete(tpl: PageTemplate) {
if (await appConfirm('删除模板', '「' + tpl.name + '」删除后不可恢复', { danger: true, okText: '删除' })) {
store.deleteTemplate(tpl.id)
bump()
emit('toast', '已删除')
@@ -162,20 +163,24 @@ function onDelete(tpl: PageTemplate) {
.tpl-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1em;
gap: 12px;
}
/* 卡片宽 ≈ (720 - 48 padding - 2×12 gap) / 3 ≈ 212px → scale = 212/1280 ≈ 0.166 */
.tpl-card {
--tpl-scale: 0.166;
cursor: pointer;
border-radius: 8px;
overflow: hidden;
background: #fff;
border: 2px solid rgba(100, 116, 139, .2);
transition: border-color .15s, transform .15s;
border: 1px solid var(--ui-border, #e2e8f0);
box-shadow: 0 1px 3px rgba(15, 23, 42, .06);
transition: border-color .15s, transform .15s, box-shadow .15s;
}
.tpl-card:hover {
border-color: var(--primary, #4f46e5);
border-color: var(--ui-primary, #4f46e5);
box-shadow: 0 4px 12px rgba(15, 23, 42, .12);
transform: translateY(-2px);
}
@@ -192,7 +197,7 @@ function onDelete(tpl: PageTemplate) {
left: 0;
width: 1280px;
height: 720px;
transform: scale(0.094); /* 约 120px 宽 */
transform: scale(var(--tpl-scale));
transform-origin: top left;
pointer-events: none;
}
@@ -201,25 +206,29 @@ function onDelete(tpl: PageTemplate) {
display: flex;
align-items: center;
gap: .4em;
padding: .5em .6em;
padding: .45em .6em;
font-size: 13px;
}
.tpl-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tpl-badge {
font-size: 11px;
padding: 1px 6px;
border-radius: 3px;
border-radius: 4px;
background: rgba(100, 116, 139, .15);
color: var(--muted, #64748b);
color: var(--ui-muted, #64748b);
flex: none;
}
.tpl-badge.user {
background: rgba(79, 70, 229, .12);
color: var(--primary, #4f46e5);
background: var(--ui-primary-soft, #eef2ff);
color: var(--ui-primary, #4f46e5);
}
.tpl-del {
@@ -227,10 +236,13 @@ function onDelete(tpl: PageTemplate) {
opacity: .5;
cursor: pointer;
padding: 2px 4px;
border: none;
background: transparent;
flex: none;
}
.tpl-del:hover {
opacity: 1;
color: #e11d48;
color: var(--ui-danger, #e11d48);
}
</style>