mirror of https://github.com/veypi/OneAuth.git
test: front
parent
be16fc1c5c
commit
200393c4d4
@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Music Player</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="flex items-center justify-center min-h-screen bg-gray-900">
|
||||||
|
<div class="bg-gray-800 p-6 rounded-lg shadow-lg text-white w-full max-w-md space-y-4">
|
||||||
|
<audio id="player" src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"></audio>
|
||||||
|
<button id="playButton"
|
||||||
|
class="w-full py-2 px-4 bg-green-500 hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 transition duration-300 ease-in-out transform active:scale-95 flex items-center justify-center space-x-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
|
||||||
|
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.26a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<span>Play</span>
|
||||||
|
</button>
|
||||||
|
<progress id="progressBar" value="0" max="1" class="w-full h-2 bg-gray-700 rounded-full overflow-hidden">
|
||||||
|
<div class="h-full bg-green-500 rounded-full"></div>
|
||||||
|
</progress>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const player = document.getElementById('player');
|
||||||
|
const playButton = document.getElementById('playButton');
|
||||||
|
const progressBar = document.getElementById('progressBar');
|
||||||
|
|
||||||
|
playButton.addEventListener('click', () => {
|
||||||
|
if (player.paused) {
|
||||||
|
player.play();
|
||||||
|
playButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 9v6m4-6v6m7-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg><span>Pause</span>';
|
||||||
|
} else {
|
||||||
|
player.pause();
|
||||||
|
playButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6 w-6"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.26a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/></svg><span>Play</span>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
player.addEventListener('timeupdate', () => {
|
||||||
|
console.log(player.currentTime, player.duration);
|
||||||
|
progressBar.value = player.currentTime / player.duration;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Card</title>
|
||||||
|
<script src='v.js'></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="h-6 w-40 bg-green-100 flex gap-4">
|
||||||
|
<slot name="a">slot a</slot>
|
||||||
|
</div>
|
||||||
|
<div class="h-14 w-40">
|
||||||
|
<slot>
|
||||||
|
<div ref="audio"></div>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
return {
|
||||||
|
data: {a: 2}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Index</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script type="module" src="./v.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id='app' class="flex gap-2">
|
||||||
|
<div>123</div>
|
||||||
|
<div>123</div>
|
||||||
|
<div>123</div>
|
||||||
|
<div ref="card">
|
||||||
|
<div>111</div>
|
||||||
|
<div slot="a">c1</div>
|
||||||
|
<div>333</div>
|
||||||
|
</div>
|
||||||
|
<div ref="card">
|
||||||
|
<div slot="a">c2</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let app = {a: 1}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Default</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="header flex"></div>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
console.log('default')
|
||||||
|
</script>
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<body>
|
||||||
|
<slot>></slot>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: [
|
||||||
|
"./components/**/*.{js,vue,ts}",
|
||||||
|
"./layouts/**/*.vue",
|
||||||
|
"./pages/**/*.vue",
|
||||||
|
"./plugins/**/*.{js,ts}",
|
||||||
|
"./app.vue",
|
||||||
|
"./error.vue",
|
||||||
|
"*.html",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
vprimary: '#2196f3',
|
||||||
|
vsecondary: '#ecc94b',
|
||||||
|
vaccents: '#ff9800',
|
||||||
|
verror: '#f44336',
|
||||||
|
vwaring: '#ff5722',
|
||||||
|
vinfo: '#ffc107',
|
||||||
|
vsuccess: '#53de58',
|
||||||
|
vignore: '#d1d5db',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* v.js
|
||||||
|
* Copyright (C) 2024 veypi <i@veypi.com>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const config = { childList: true, subtree: true };
|
||||||
|
|
||||||
|
const callback = function (mutationsList, observer) {
|
||||||
|
for (let mutation of mutationsList) {
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
// console.log(mutation)
|
||||||
|
// console.log('A child node has been added or removed.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const observer = new MutationObserver(callback);
|
||||||
|
|
||||||
|
var cacheUrl = {}
|
||||||
|
var pendingRequests = {};
|
||||||
|
|
||||||
|
async function fetchFileWithCache(url) {
|
||||||
|
if (cacheUrl[url]) {
|
||||||
|
return Promise.resolve(cacheUrl[url]);
|
||||||
|
}
|
||||||
|
if (pendingRequests[url]) {
|
||||||
|
return pendingRequests[url];
|
||||||
|
}
|
||||||
|
const promise = fetch(url)
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.then(txt => {
|
||||||
|
cacheUrl[url] = txt;
|
||||||
|
return txt;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching the file:', error);
|
||||||
|
delete pendingRequests[url];
|
||||||
|
throw error;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
delete pendingRequests[url];
|
||||||
|
});
|
||||||
|
pendingRequests[url] = promise;
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function extractBodyAndScript(htmlString) {
|
||||||
|
const bodyMatch = htmlString.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
||||||
|
const bodyContent = bodyMatch ? bodyMatch[1] : '';
|
||||||
|
|
||||||
|
let scriptMatches = htmlString.match(/<script[^>]*>([\s\S]*?)<\/script>/ig);
|
||||||
|
|
||||||
|
|
||||||
|
let scriptContents = []
|
||||||
|
let srcReg = /<script\s+[^>]*src=["']([^"']+)["'][^>]*>/
|
||||||
|
for (let s of scriptMatches) {
|
||||||
|
let match = srcReg.exec(s)
|
||||||
|
if (match && match.length) {
|
||||||
|
// handle src script
|
||||||
|
// console.log(match[1])
|
||||||
|
} else {
|
||||||
|
scriptContents.push(s.replace(/<script[^>]*>|<\/script>/ig, ''))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
bodyContent,
|
||||||
|
scriptContents
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @param {HTMLElement} node
|
||||||
|
* @return {Promise<void>}
|
||||||
|
* */
|
||||||
|
async function setupRef(node, data, itercount = 0) {
|
||||||
|
console.log(itercount)
|
||||||
|
if (itercount > 100) {
|
||||||
|
log.warn('infinite loop')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const ref = node.getAttribute('ref')
|
||||||
|
node.setAttribute('tag', ref)
|
||||||
|
node.removeAttribute('ref')
|
||||||
|
const txt = await fetchFileWithCache(ref + ".html")
|
||||||
|
let ast = extractBodyAndScript(txt)
|
||||||
|
|
||||||
|
let div = document.createElement('div')
|
||||||
|
div.innerHTML = ast.bodyContent
|
||||||
|
|
||||||
|
handleSlots(node, div)
|
||||||
|
//
|
||||||
|
node.innerHTML = div.innerHTML
|
||||||
|
|
||||||
|
let resp = new Function("{vnode,data}", ast.scriptContents.join('\n'))({ vnode: node, data: data })
|
||||||
|
|
||||||
|
for (let n of node.querySelectorAll("div[ref]")) {
|
||||||
|
// 确保子元素先加载完
|
||||||
|
await setupRef(n, resp?.data || {}, itercount + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSlots(origin, template) {
|
||||||
|
// handle slots
|
||||||
|
let originSnap = { '': [] }
|
||||||
|
let i = 0
|
||||||
|
let child
|
||||||
|
while (child = origin.children.item(i)) {
|
||||||
|
i++
|
||||||
|
let slotname = child.getAttribute ? child.getAttribute('slot') || '' : ''
|
||||||
|
if (slotname !== '') {
|
||||||
|
child.removeAttribute('slot')
|
||||||
|
originSnap[slotname] = child
|
||||||
|
} else {
|
||||||
|
originSnap[''].push(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.b = originSnap
|
||||||
|
let slots = template.querySelectorAll('slot')
|
||||||
|
slots.forEach((slot) => {
|
||||||
|
let slotname = slot.getAttribute('name') || ''
|
||||||
|
let snap = originSnap[slotname]
|
||||||
|
if (snap && snap.length !== 0) {
|
||||||
|
if (slotname === '') {
|
||||||
|
slot.replaceWith(...snap)
|
||||||
|
} else {
|
||||||
|
slot.replaceWith(snap)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
slot.replaceWith(...slot.childNodes)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
console.log('DOMContentLoaded');
|
||||||
|
const node = document.getElementById('app');
|
||||||
|
observer.observe(node, config);
|
||||||
|
const sync = () => {
|
||||||
|
let test = node.querySelectorAll("div[ref]")
|
||||||
|
test.forEach(n => setupRef(n, app))
|
||||||
|
}
|
||||||
|
sync()
|
||||||
|
// setInterval(sync, 10)
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* v.js
|
||||||
|
* Copyright (C) 2024 veypi <i@veypi.com>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
a: 123
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue