feat(P4): precompute移植+app.js sources双向回填+回归1003/1003+验收体积/文件/data.json
Deploy to GitHub Pages / deploy (push) Failing after 16s
Regression / regression (push) Failing after 15m2s

This commit is contained in:
aurora-admin
2026-09-12 14:18:41 +08:00
parent 16f365a045
commit c65a69c34e
401 changed files with 22932 additions and 140 deletions
+93
View File
@@ -0,0 +1,93 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aurora Admin · Popconfirm (H5)</title>
<link rel="stylesheet" href="../.design_library/aurora-admin/colors_and_type.css">
<link rel="stylesheet" href="./Popconfirm.css">
<style>
.demo { padding: 24px; background: var(--au-color-page-bg); font-family: var(--font-family); }
h1 { font-size: 20px; margin: 0 0 4px; color: var(--au-color-text-title); }
.sub { color: var(--au-color-text-secondary); margin: 0 0 24px; font-size: 12px; }
.demo-block { background: var(--au-color-card-bg); border-radius: 8px; padding: 32px; margin-bottom: 20px; }
.demo-block > .hint { color: var(--au-color-text-secondary); font-size: 12px; margin: 0 0 16px; }
.aa-btn { height: 32px; padding: 0 16px; border-radius: 4px; font-size: 14px; cursor: pointer; border: 1px solid var(--au-color-border); background: var(--au-color-card-bg); color: var(--au-color-text-body); }
.aa-btn.danger { border-color: var(--au-color-error); color: var(--au-color-error); }
.aa-btn.danger:hover { background: var(--au-color-error); color: var(--au-color-text-inverse); }
.log { margin-top: 16px; font-size: 13px; color: var(--au-color-text-secondary); }
</style>
</head>
<body>
<div class="demo">
<h1>Aurora Admin · Popconfirm 组件(纯 H5)</h1>
<p class="sub">对齐 Popconfirm 规范:气泡确认、图标+描述+确认/取消,点击外部关闭</p>
<div class="demo-block">
<p class="hint">删除确认(底部弹出)</p>
<span class="aa-popconfirm" data-popconfirm>
<button class="aa-btn danger">删除</button>
<div class="aa-popconfirm-pop placement-bottom" hidden>
<div class="aa-popconfirm-inner">
<span class="aa-popconfirm-icon">!</span>
<span class="aa-popconfirm-title">确定要删除该条记录吗?此操作不可撤销。</span>
</div>
<div class="aa-popconfirm-actions">
<button class="aa-popconfirm-btn" data-act="cancel">取消</button>
<button class="aa-popconfirm-btn primary" data-act="ok">确定</button>
</div>
</div>
</span>
<div class="log" id="log">结果:—</div>
</div>
<div class="demo-block">
<p class="hint">顶部弹出</p>
<span class="aa-popconfirm" data-popconfirm>
<button class="aa-btn">提交</button>
<div class="aa-popconfirm-pop placement-top" hidden>
<div class="aa-popconfirm-inner">
<span class="aa-popconfirm-icon">!</span>
<span class="aa-popconfirm-title">确定要提交审批吗?</span>
</div>
<div class="aa-popconfirm-actions">
<button class="aa-popconfirm-btn" data-act="cancel">取消</button>
<button class="aa-popconfirm-btn primary" data-act="ok">确定</button>
</div>
</div>
</span>
</div>
</div>
<script>
document.querySelectorAll('[data-popconfirm]').forEach(root => {
const trigger = root.querySelector('button');
const pop = root.querySelector('.aa-popconfirm-pop');
trigger.addEventListener('click', (e) => {
e.stopPropagation();
const open = pop.hidden;
closeAll();
pop.hidden = !open;
});
pop.querySelector('[data-act="cancel"]').addEventListener('click', (e) => {
e.stopPropagation(); pop.hidden = true;
});
pop.querySelector('[data-act="ok"]').addEventListener('click', (e) => {
e.stopPropagation(); pop.hidden = true;
const log = document.getElementById('log');
if (log) log.textContent = '结果:已确认(ok)';
});
// 点击气泡本身不关闭
pop.addEventListener('click', (e) => e.stopPropagation());
});
function closeAll() {
document.querySelectorAll('.aa-popconfirm-pop').forEach(p => p.hidden = true);
}
document.addEventListener('click', closeAll);
</script>
</body>
</html>
+82
View File
@@ -0,0 +1,82 @@
import React, { useRef, useState, useEffect } from 'react';
import './Popconfirm.css';
/**
* Aurora Admin Popconfirm(React)
* 对齐 组件2.txt Popconfirm 规范:气泡确认、图标+描述+确认/取消,点击外部关闭
*
* props:
* - title : 确认描述文本
* - okText / cancelText
* - placement : 'bottom' | 'top'
* - visible / defaultVisible : 受控打开
* - onConfirm() / onCancel()
* - children : 触发元素(如按钮)
*/
export default function Popconfirm({
title,
okText = '确定',
cancelText = '取消',
placement = 'bottom',
visible,
defaultVisible = false,
onConfirm,
onCancel,
children
}) {
const rootRef = useRef(null);
const [innerOpen, setInnerOpen] = useState(defaultVisible);
const open = visible !== undefined ? visible : innerOpen;
const [style, setStyle] = useState({});
const syncPos = () => {
if (!rootRef.current) return;
const r = rootRef.current.getBoundingClientRect();
setStyle(placement === 'top'
? { left: r.left, bottom: window.innerHeight - r.top }
: { left: r.left, top: r.bottom + 8 });
};
const setOpen = (v) => {
if (visible === undefined) setInnerOpen(v);
if (v) { syncPos(); document.addEventListener('click', onDocClick, true); window.addEventListener('resize', syncPos); }
else { document.removeEventListener('click', onDocClick, true); window.removeEventListener('resize', syncPos); }
};
const onDocClick = (e) => {
if (rootRef.current && rootRef.current.contains(e.target)) return;
setOpen(false);
};
useEffect(() => {
if (open) { syncPos(); document.addEventListener('click', onDocClick, true); window.addEventListener('resize', syncPos); }
return () => { document.removeEventListener('click', onDocClick, true); window.removeEventListener('resize', syncPos); };
}, [open]);
const confirm = () => { onConfirm && onConfirm(); setOpen(false); };
const cancel = () => { onCancel && onCancel(); setOpen(false); };
return (
<span className="aa-popconfirm" ref={rootRef}>
<span onClick={() => setOpen(!open)}>{children}</span>
{open && (
<div className={`aa-popconfirm-pop placement-${placement}`} style={style}>
<div className="aa-popconfirm-inner">
<span className="aa-popconfirm-icon">!</span>
<span className="aa-popconfirm-title">{title}</span>
</div>
<div className="aa-popconfirm-actions">
<button className="aa-popconfirm-btn" onClick={cancel}>{cancelText}</button>
<button className="aa-popconfirm-btn primary" onClick={confirm}>{okText}</button>
</div>
</div>
)}
</span>
);
}
/*
用法示例:
import Popconfirm from './Popconfirm';
<Popconfirm title="确定要删除吗?" onConfirm={handleDelete}>
<button>删除</button>
</Popconfirm>
*/
+79
View File
@@ -0,0 +1,79 @@
<template>
<span class="aa-popconfirm" ref="root">
<span @click="toggle">
<slot />
</span>
<div
v-if="open"
class="aa-popconfirm-pop"
:class="'placement-' + placement"
>
<div class="aa-popconfirm-inner">
<span class="aa-popconfirm-icon">!</span>
<span class="aa-popconfirm-title">{{ title }}</span>
</div>
<div class="aa-popconfirm-actions">
<button class="aa-popconfirm-btn" @click="cancel">{{ cancelText }}</button>
<button class="aa-popconfirm-btn primary" @click="confirm">{{ okText }}</button>
</div>
</div>
</span>
</template>
<script>
export default {
name: 'AaPopconfirm',
props: {
value: { type: Boolean, default: undefined }, // 受控打开
title: { type: String, required: true },
okText: { type: String, default: '确定' },
cancelText: { type: String, default: '取消' },
placement: { type: String, default: 'bottom' } // bottom | top
},
data() { return { innerOpen: false }; },
computed: {
open() { return this.value !== undefined ? this.value : this.innerOpen; }
},
watch: {
value(v) { this.bindOutside(v); }
},
methods: {
setOpen(v) {
if (this.value !== undefined) this.$emit('input', v);
else this.innerOpen = v;
this.bindOutside(v);
},
bindOutside(v) {
if (v) document.addEventListener('click', this.onDocClick, true);
else document.removeEventListener('click', this.onDocClick, true);
},
toggle() { this.setOpen(!this.open); },
confirm() { this.$emit('confirm'); this.setOpen(false); },
cancel() { this.$emit('cancel'); this.setOpen(false); },
onDocClick(e) {
if (this.$refs.root && this.$refs.root.contains(e.target)) return;
this.setOpen(false);
}
},
beforeDestroy() { document.removeEventListener('click', this.onDocClick, true); }
};
</script>
<!-- 样式对齐 组件2.txt Popconfirm 规范 -->
<style>
.aa-popconfirm { position: relative; display: inline-block; }
.aa-popconfirm-pop { position: absolute; z-index: 100; min-width: 220px; background: #fff; border: 1px solid #E8ECF1;
border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); padding: 12px 14px; }
.aa-popconfirm-pop.placement-bottom { top: calc(100% + 8px); left: 0; }
.aa-popconfirm-pop.placement-top { bottom: calc(100% + 8px); left: 0; }
.aa-popconfirm-inner { display: flex; gap: 8px; }
.aa-popconfirm-icon { flex-shrink: 0; width: 16px; height: 16px; border-radius: 50%; background: #8C5A00; color: #fff;
display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; margin-top: 2px; }
.aa-popconfirm-title { font-size: 14px; color: #262626; line-height: 1.5; }
.aa-popconfirm-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
.aa-popconfirm-btn { height: 28px; padding: 0 12px; border-radius: 4px; font-size: 13px; cursor: pointer;
border: 1px solid #E8ECF1; background: #fff; color: #262626; transition: border-color .15s, color .15s; }
.aa-popconfirm-btn:hover { color: #2F54EB; border-color: #2F54EB; }
.aa-popconfirm-btn.primary { background: #2F54EB; border-color: #2F54EB; color: #fff; }
.aa-popconfirm-btn.primary:hover { background: #1D39C4; border-color: #1D39C4; color: #fff; }
</style>
+87
View File
@@ -0,0 +1,87 @@
<template>
<span class="aa-popconfirm" ref="root">
<span @click="toggle">
<slot />
</span>
<teleport to="body">
<div
v-if="open"
class="aa-popconfirm-pop"
:class="'placement-' + placement"
:style="popStyle"
>
<div class="aa-popconfirm-inner">
<span class="aa-popconfirm-icon">!</span>
<span class="aa-popconfirm-title">{{ title }}</span>
</div>
<div class="aa-popconfirm-actions">
<button class="aa-popconfirm-btn" @click="cancel">{{ cancelText }}</button>
<button class="aa-popconfirm-btn primary" @click="confirm">{{ okText }}</button>
</div>
</div>
</teleport>
</span>
</template>
<script setup>
import { ref, computed, watch, onBeforeUnmount } from 'vue';
const props = defineProps({
modelValue: { type: Boolean, default: undefined }, // 受控打开
title: { type: String, required: true },
okText: { type: String, default: '确定' },
cancelText: { type: String, default: '取消' },
placement: { type: String, default: 'bottom' } // bottom | top
});
const emit = defineEmits(['update:modelValue', 'confirm', 'cancel']);
const root = ref(null);
const innerOpen = ref(false);
const open = computed(() => props.modelValue !== undefined ? props.modelValue : innerOpen.value);
const popStyle = ref({});
function syncPosition() {
if (!root.value || !open.value) return;
const r = root.value.getBoundingClientRect();
popStyle.value = props.placement === 'top'
? { left: r.left + 'px', bottom: (window.innerHeight - r.top) + 'px' }
: { left: r.left + 'px', top: r.bottom + 8 + 'px' };
}
function setOpen(v) {
if (props.modelValue !== undefined) emit('update:modelValue', v);
else innerOpen.value = v;
if (v) { syncPosition(); document.addEventListener('click', onDocClick, true); window.addEventListener('resize', syncPosition); }
else { document.removeEventListener('click', onDocClick, true); window.removeEventListener('resize', syncPosition); }
}
function toggle() { setOpen(!open.value); }
function confirm() { emit('confirm'); setOpen(false); }
function cancel() { emit('cancel'); setOpen(false); }
function onDocClick(e) {
if (root.value && root.value.contains(e.target)) return;
setOpen(false);
}
watch(() => props.modelValue, (v) => {
if (v) { syncPosition(); document.addEventListener('click', onDocClick, true); window.addEventListener('resize', syncPosition); }
else { document.removeEventListener('click', onDocClick, true); window.removeEventListener('resize', syncPosition); }
});
onBeforeUnmount(() => { document.removeEventListener('click', onDocClick, true); window.removeEventListener('resize', syncPosition); });
</script>
<!-- 样式对齐 组件2.txt Popconfirm 规范 -->
<style scoped>
.aa-popconfirm { position: relative; display: inline-block; }
.aa-popconfirm-pop { position: fixed; z-index: 100; min-width: 220px; background: #fff; border: 1px solid #E8ECF1;
border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); padding: 12px 14px; }
.aa-popconfirm-inner { display: flex; gap: 8px; }
.aa-popconfirm-icon { flex-shrink: 0; width: 16px; height: 16px; border-radius: 50%; background: #8C5A00; color: #fff;
display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; margin-top: 2px; }
.aa-popconfirm-title { font-size: 14px; color: #262626; line-height: 1.5; }
.aa-popconfirm-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
.aa-popconfirm-btn { height: 28px; padding: 0 12px; border-radius: 4px; font-size: 13px; cursor: pointer;
border: 1px solid #E8ECF1; background: #fff; color: #262626; transition: border-color .15s, color .15s; }
.aa-popconfirm-btn:hover { color: #2F54EB; border-color: #2F54EB; }
.aa-popconfirm-btn.primary { background: #2F54EB; border-color: #2F54EB; color: #fff; }
.aa-popconfirm-btn.primary:hover { background: #1D39C4; border-color: #1D39C4; color: #fff; }
</style>