384 lines
8.3 KiB
HTML
384 lines
8.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh">
|
|
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<title>Dialog Final</title>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
|
|
|
|
<style>
|
|
.mask {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
background: rgba(0, 0, 0, 0.35);
|
|
z-index: 999;
|
|
display: none;
|
|
}
|
|
|
|
.win-dialog {
|
|
position: fixed;
|
|
background: #fff;
|
|
box-shadow: 0 12px 40px rgba(0, 0, 0, .25);
|
|
display: none;
|
|
overflow: hidden;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.win-header {
|
|
height: 40px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
border-bottom: 1px solid #eee;
|
|
cursor: move;
|
|
background: #fafafa;
|
|
}
|
|
|
|
.win-title {
|
|
padding-left: 10px;
|
|
}
|
|
|
|
.win-btn {
|
|
display: flex;
|
|
height: 100%;
|
|
}
|
|
|
|
.btn-item {
|
|
padding: 0 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.btn-item:hover {
|
|
background: #ddd;
|
|
}
|
|
|
|
.win-body {
|
|
height: calc(100% - 40px);
|
|
padding: 10px;
|
|
}
|
|
|
|
.resizer {
|
|
position: absolute;
|
|
}
|
|
|
|
.br {
|
|
right: 0;
|
|
bottom: 0;
|
|
width: 12px;
|
|
height: 12px;
|
|
cursor: nwse-resize;
|
|
}
|
|
|
|
.r {
|
|
right: 0;
|
|
top: 0;
|
|
width: 5px;
|
|
height: 100%;
|
|
cursor: ew-resize;
|
|
}
|
|
|
|
.b {
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 5px;
|
|
cursor: ns-resize;
|
|
}
|
|
|
|
.l {
|
|
left: 0;
|
|
top: 0;
|
|
width: 5px;
|
|
height: 100%;
|
|
cursor: ew-resize;
|
|
}
|
|
|
|
.t {
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 5px;
|
|
cursor: ns-resize;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<template id="file-template">
|
|
<div class="layout">
|
|
<div class="left">分类树</div>
|
|
<div class="middle">列表</div>
|
|
<div class="right">预览</div>
|
|
</div>
|
|
</template>
|
|
|
|
<button onclick="handleShowDialog()">
|
|
打开窗口
|
|
</button>
|
|
|
|
<script>
|
|
function handleShowDialog() {
|
|
Dialog.open({
|
|
title: 'Test Window',
|
|
template: '#file-template',
|
|
width: '80%',
|
|
height: '80%'
|
|
})
|
|
}
|
|
|
|
// Dialog
|
|
const Dialog = (() => {
|
|
let $mask = $('<div class="mask"></div>').appendTo('body');
|
|
let $el = null;
|
|
let isMax = false;
|
|
let old = {};
|
|
let opt = null;
|
|
|
|
// ====== 工具函数 ======
|
|
function parseSize(val, max) {
|
|
if (typeof val === 'number') return val;
|
|
|
|
if (typeof val === 'string') {
|
|
if (val.includes('%')) return max * parseFloat(val) / 100;
|
|
if (val.includes('vw')) return window.innerWidth * parseFloat(val) / 100;
|
|
if (val.includes('vh')) return window.innerHeight * parseFloat(val) / 100;
|
|
return parseFloat(val);
|
|
}
|
|
|
|
return max;
|
|
}
|
|
|
|
// ====== 核心方法 ======
|
|
function applySize() {
|
|
const maxW = window.innerWidth;
|
|
const maxH = window.innerHeight;
|
|
|
|
let w = parseSize(opt.width || 600, maxW);
|
|
let h = parseSize(opt.height || 400, maxH);
|
|
|
|
w = Math.min(w, maxW);
|
|
h = Math.min(h, maxH);
|
|
|
|
$el.css({
|
|
width: w,
|
|
height: h,
|
|
left: (maxW - w) / 2,
|
|
top: (maxH - h) / 2
|
|
});
|
|
}
|
|
|
|
function bindResize() {
|
|
$(window).on('resize.dialog', () => {
|
|
if (isMax) {
|
|
$el.css({
|
|
width: window.innerWidth,
|
|
height: window.innerHeight
|
|
});
|
|
return;
|
|
}
|
|
|
|
const w = opt.width;
|
|
const h = opt.height;
|
|
|
|
if (
|
|
(typeof w === 'string' && (w.includes('%') || w.includes('vw'))) ||
|
|
(typeof h === 'string' && (h.includes('%') || h.includes('vh')))
|
|
) {
|
|
applySize();
|
|
}
|
|
});
|
|
}
|
|
|
|
function bindEvents() {
|
|
const self = {
|
|
get el() { return $el; }
|
|
};
|
|
|
|
$el.find('.close').on('click', close);
|
|
|
|
// drag
|
|
let move = false, ox = 0, oy = 0;
|
|
|
|
$el.find('.win-header').on('mousedown', function (e) {
|
|
if (isMax) return;
|
|
|
|
move = true;
|
|
const pos = $el.position();
|
|
ox = e.clientX - pos.left;
|
|
oy = e.clientY - pos.top;
|
|
|
|
$(document).on('mousemove.drag', function (e) {
|
|
if (!move) return;
|
|
|
|
$el.css({
|
|
left: e.clientX - ox,
|
|
top: e.clientY - oy
|
|
});
|
|
});
|
|
|
|
$(document).on('mouseup.drag', function () {
|
|
move = false;
|
|
$(document).off('.drag');
|
|
});
|
|
});
|
|
|
|
// resize
|
|
const MIN_W = 300;
|
|
const MIN_H = 200;
|
|
|
|
$el.find('.resizer').on('mousedown', function (e) {
|
|
if (isMax) return;
|
|
|
|
let type = $(this).attr('class').split(' ')[1];
|
|
|
|
const startX = e.clientX;
|
|
const startY = e.clientY;
|
|
|
|
const startW = $el.width();
|
|
const startH = $el.height();
|
|
const startL = parseFloat($el.css('left'));
|
|
const startT = parseFloat($el.css('top'));
|
|
|
|
const MAX_W = window.innerWidth;
|
|
const MAX_H = window.innerHeight;
|
|
|
|
function onMove(e) {
|
|
if (e.buttons !== 1) return;
|
|
|
|
let dx = e.clientX - startX;
|
|
let dy = e.clientY - startY;
|
|
|
|
let newW = startW;
|
|
let newH = startH;
|
|
let newL = startL;
|
|
let newT = startT;
|
|
|
|
if (type === 'br') { newW += dx; newH += dy; }
|
|
if (type === 'r') newW += dx;
|
|
if (type === 'b') newH += dy;
|
|
if (type === 'l') { newW -= dx; newL += dx; }
|
|
if (type === 't') { newH -= dy; newT += dy; }
|
|
|
|
if (newW < MIN_W) newW = MIN_W;
|
|
if (newH < MIN_H) newH = MIN_H;
|
|
|
|
if (newW > MAX_W) newW = MAX_W;
|
|
if (newH > MAX_H) newH = MAX_H;
|
|
|
|
$el.css({
|
|
width: newW,
|
|
height: newH,
|
|
left: newL,
|
|
top: newT
|
|
});
|
|
}
|
|
|
|
function onUp() {
|
|
$(document).off('.resize');
|
|
}
|
|
|
|
$(document)
|
|
.on('mousemove.resize', onMove)
|
|
.on('mouseup.resize', onUp);
|
|
});
|
|
|
|
// max
|
|
$el.find('.max').on('click', function () {
|
|
if (!isMax) {
|
|
old = {
|
|
width: $el.width(),
|
|
height: $el.height(),
|
|
left: $el.position().left,
|
|
top: $el.position().top
|
|
};
|
|
|
|
$el.css({
|
|
left: 0,
|
|
top: 0,
|
|
width: window.innerWidth,
|
|
height: window.innerHeight
|
|
});
|
|
|
|
isMax = true;
|
|
} else {
|
|
$el.css(old);
|
|
isMax = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
function destroy() {
|
|
if ($el) {
|
|
$el.remove();
|
|
$el = null;
|
|
}
|
|
isMax = false;
|
|
$mask.off().hide();
|
|
$(window).off('resize.dialog');
|
|
}
|
|
|
|
// ====== API ======
|
|
function open(o) {
|
|
destroy();
|
|
opt = o;
|
|
|
|
$el = $(`
|
|
<div class="win-dialog">
|
|
<div class="win-header">
|
|
<div class="win-title"></div>
|
|
<div class="win-btn">
|
|
<div class="btn-item max">MAX</div>
|
|
<div class="btn-item close">X</div>
|
|
</div>
|
|
</div>
|
|
<div class="win-body"></div>
|
|
</div>
|
|
`).appendTo('body');
|
|
|
|
$el.find('.win-title').text(opt.title || 'Dialog');
|
|
const $body = $el.find('.win-body');
|
|
|
|
if (opt.template) {
|
|
const tpl = document.querySelector(opt.template);
|
|
const node = tpl.content.cloneNode(true);
|
|
$body.empty().append(node);
|
|
}
|
|
else if (opt.content instanceof HTMLElement) {
|
|
$body.empty().append(opt.content);
|
|
}
|
|
else {
|
|
$body.html(opt.content || '');
|
|
}
|
|
applySize();
|
|
|
|
$mask.fadeIn(120);
|
|
$el.fadeIn(180);
|
|
|
|
bindEvents();
|
|
bindResize();
|
|
}
|
|
|
|
function close() {
|
|
$el?.fadeOut(120);
|
|
$mask.fadeOut(120);
|
|
|
|
setTimeout(() => destroy(), 120);
|
|
}
|
|
|
|
// ====== 单例导出 ======
|
|
return {
|
|
open,
|
|
close
|
|
};
|
|
})();
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html> |