Compare commits
3 Commits
5bcd94a3d0
...
e4702b877f
Author | SHA1 | Date | |
---|---|---|---|
|
e4702b877f | ||
|
26eff2fcb3 | ||
|
3f80854bbc |
@ -22,7 +22,8 @@
|
|||||||
"three": "^0.172.0",
|
"three": "^0.172.0",
|
||||||
"vue": "^3.3.0",
|
"vue": "^3.3.0",
|
||||||
"vue-i18n": "^9.1.9",
|
"vue-i18n": "^9.1.9",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.0",
|
||||||
|
"register-service-worker": "^1.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rushstack/eslint-patch": "^1.6.1",
|
"@rushstack/eslint-patch": "^1.6.1",
|
||||||
@ -44,6 +45,8 @@
|
|||||||
"sass-loader": "^13.3.3",
|
"sass-loader": "^13.3.3",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.4",
|
||||||
"vite": "4.1.4",
|
"vite": "4.1.4",
|
||||||
"vue-tsc": "^1.0.24"
|
"vite-plugin-pwa": "^0.17.5",
|
||||||
|
"vue-tsc": "^1.0.24",
|
||||||
|
"workbox-window": "^7.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
src/main.ts
19
src/main.ts
@ -3,6 +3,25 @@ import App from './App.vue'
|
|||||||
import 'ant-design-vue/dist/reset.css';
|
import 'ant-design-vue/dist/reset.css';
|
||||||
import Antd from 'ant-design-vue';
|
import Antd from 'ant-design-vue';
|
||||||
import router from './router/router';
|
import router from './router/router';
|
||||||
|
import { registerSW } from 'virtual:pwa-register'
|
||||||
|
import { isPwa, showPwaInstallPrompt } from './utils/pwa';
|
||||||
|
|
||||||
|
const updateSW = registerSW({
|
||||||
|
onNeedRefresh() {
|
||||||
|
// 当发现新版本时,可以在这里提示用户
|
||||||
|
console.log('发现新版本')
|
||||||
|
},
|
||||||
|
onOfflineReady() {
|
||||||
|
// 当离线功能准备就绪时
|
||||||
|
console.log('应用已经可以离线使用')
|
||||||
|
if (isPwa()) {
|
||||||
|
console.log('PWA 模式')
|
||||||
|
} else {
|
||||||
|
// showPwaInstallPrompt()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
app.use(Antd);
|
app.use(Antd);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
@ -2,23 +2,108 @@
|
|||||||
<div>
|
<div>
|
||||||
<video
|
<video
|
||||||
id="video"
|
id="video"
|
||||||
src="/static/test.mp4"
|
:src="mediaSource"
|
||||||
style="display: none"
|
style="display: none"
|
||||||
loop
|
loop
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
></video>
|
></video>
|
||||||
<canvas id="canvas"></canvas>
|
<canvas id="canvas"></canvas>
|
||||||
<button @click="handleClick">播放</button>
|
<div class="play-button" v-show="!isPlaying" @click="handleClick">
|
||||||
|
<i class="play-icon"></i>
|
||||||
|
</div>
|
||||||
|
<div class="upload-container">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
ref="fileInput"
|
||||||
|
@change="handleFileUpload"
|
||||||
|
accept="image/*,video/*"
|
||||||
|
style="display: none"
|
||||||
|
/>
|
||||||
|
<button class="upload-btn" @click="triggerFileUpload">
|
||||||
|
上传图片/视频
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { onMounted, onUnmounted } from "vue";
|
import { onMounted, onUnmounted, ref } from "vue";
|
||||||
|
|
||||||
|
const isPlaying = ref(false);
|
||||||
|
const fileInput = ref<HTMLInputElement | null>(null);
|
||||||
|
const mediaSource = ref("/static/test.mp4");
|
||||||
|
const isVideo = ref(true);
|
||||||
|
const texture = ref<THREE.VideoTexture | THREE.Texture | null>(null);
|
||||||
|
|
||||||
|
const triggerFileUpload = () => {
|
||||||
|
fileInput.value?.click();
|
||||||
|
};
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
const video = document.getElementById("video") as HTMLVideoElement;
|
if (isVideo.value) {
|
||||||
video.play();
|
const video = document.getElementById("video") as HTMLVideoElement;
|
||||||
|
video.play();
|
||||||
|
isPlaying.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createImageTexture = (url: string): Promise<THREE.Texture> => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const loader = new THREE.TextureLoader();
|
||||||
|
loader.load(url, (texture) => {
|
||||||
|
texture.minFilter = THREE.LinearFilter;
|
||||||
|
texture.magFilter = THREE.LinearFilter;
|
||||||
|
resolve(texture);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileUpload = async (event: Event) => {
|
||||||
|
const input = event.target as HTMLInputElement;
|
||||||
|
const file = input.files?.[0];
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
const url = URL.createObjectURL(file);
|
||||||
|
const video = document.getElementById("video") as HTMLVideoElement;
|
||||||
|
|
||||||
|
// 先暂停当前播放
|
||||||
|
video.pause();
|
||||||
|
isPlaying.value = false;
|
||||||
|
|
||||||
|
if (file.type.startsWith("video/")) {
|
||||||
|
isVideo.value = true;
|
||||||
|
// 更新视频源
|
||||||
|
mediaSource.value = url;
|
||||||
|
|
||||||
|
// 如果是视频,等待加载后再播放
|
||||||
|
video.addEventListener(
|
||||||
|
"loadeddata",
|
||||||
|
async () => {
|
||||||
|
try {
|
||||||
|
texture.value = new THREE.VideoTexture(video);
|
||||||
|
texture.value.minFilter = THREE.LinearFilter;
|
||||||
|
texture.value.magFilter = THREE.LinearFilter;
|
||||||
|
texture.value.format = THREE.RGBFormat;
|
||||||
|
await video.play();
|
||||||
|
isPlaying.value = true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("视频播放失败:", err);
|
||||||
|
isPlaying.value = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
} else if (file.type.startsWith("image/")) {
|
||||||
|
isVideo.value = false;
|
||||||
|
mediaSource.value = "";
|
||||||
|
// 创建图片纹理
|
||||||
|
texture.value = await createImageTexture(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置文件输入,允许重复上传相同文件
|
||||||
|
input.value = "";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 创建场景和相机
|
// 创建场景和相机
|
||||||
@ -36,21 +121,30 @@ onMounted(async () => {
|
|||||||
canvas: document.querySelector("#canvas"),
|
canvas: document.querySelector("#canvas"),
|
||||||
});
|
});
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
|
|
||||||
// 获取视频元素
|
// 获取视频元素
|
||||||
const video = document.getElementById("video") as HTMLVideoElement;
|
const video = document.getElementById("video") as HTMLVideoElement;
|
||||||
|
|
||||||
// 创建视频纹理
|
// 创建初始视频纹理
|
||||||
const videoTexture = new THREE.VideoTexture(video);
|
texture.value = new THREE.VideoTexture(video);
|
||||||
videoTexture.minFilter = THREE.LinearFilter;
|
texture.value.minFilter = THREE.LinearFilter;
|
||||||
videoTexture.magFilter = THREE.LinearFilter;
|
texture.value.magFilter = THREE.LinearFilter;
|
||||||
videoTexture.format = THREE.RGBFormat;
|
texture.value.format = THREE.RGBFormat;
|
||||||
|
|
||||||
// 播放视频
|
// 等待视频加载完成后播放
|
||||||
try {
|
video.addEventListener(
|
||||||
await video.play();
|
"loadeddata",
|
||||||
} catch (err) {
|
async () => {
|
||||||
console.error("视频播放失败:", err);
|
try {
|
||||||
}
|
await video.play();
|
||||||
|
isPlaying.value = true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("视频播放失败:", err);
|
||||||
|
isPlaying.value = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
|
||||||
// 创建平面几何体
|
// 创建平面几何体
|
||||||
const geometry = new THREE.PlaneGeometry(10, 10, 200, 200);
|
const geometry = new THREE.PlaneGeometry(10, 10, 200, 200);
|
||||||
@ -76,7 +170,7 @@ onMounted(async () => {
|
|||||||
vec3 pos = position;
|
vec3 pos = position;
|
||||||
|
|
||||||
// 基于位置和时间生成随机值
|
// 基于位置和时间生成随机值
|
||||||
float rand = random(pos.xy) * 2.0 - 1.0;
|
float rand = random(pos.xy) * 1.01 - 0.01;
|
||||||
vRandom = rand;
|
vRandom = rand;
|
||||||
|
|
||||||
// 计算打散效果
|
// 计算打散效果
|
||||||
@ -114,29 +208,49 @@ onMounted(async () => {
|
|||||||
varying float vRandom;
|
varying float vRandom;
|
||||||
uniform float time;
|
uniform float time;
|
||||||
|
|
||||||
|
// 颜色调整辅助函数
|
||||||
|
vec3 adjustColor(vec3 color) {
|
||||||
|
// 增加对比度
|
||||||
|
vec3 contrast = (color - 0.5) * 1.4 + 0.5;
|
||||||
|
|
||||||
|
// 计算亮度
|
||||||
|
float luminance = dot(contrast, vec3(0.299, 0.587, 0.114));
|
||||||
|
|
||||||
|
// 增加饱和度
|
||||||
|
vec3 saturated = mix(vec3(luminance), contrast, 1.3);
|
||||||
|
|
||||||
|
// 稍微压暗高光
|
||||||
|
saturated = pow(saturated, vec3(1.1));
|
||||||
|
|
||||||
|
return saturated;
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// 减小马赛克大小
|
// 马赛克大小
|
||||||
float mosaicSize = 1000.0;
|
float mosaicSize = 1000.0;
|
||||||
vec2 mosaicUv = floor(vUv * mosaicSize) / mosaicSize;
|
vec2 mosaicUv = floor(vUv * mosaicSize) / mosaicSize;
|
||||||
|
|
||||||
// 减小抖动效果
|
// 减小抖动效果
|
||||||
float jitter = sin(time * 2.0 + vRandom * 6.28) * 0.000001;
|
float jitter = sin(time * 2.0 + vRandom * 1.28) * 0.001;
|
||||||
mosaicUv += vec2(jitter);
|
mosaicUv += vec2(jitter);
|
||||||
|
|
||||||
vec4 texColor = texture2D(mainTexture, mosaicUv);
|
vec4 texColor = texture2D(mainTexture, mosaicUv);
|
||||||
|
|
||||||
|
// 调整颜色
|
||||||
|
vec3 adjustedColor = adjustColor(texColor.rgb);
|
||||||
|
|
||||||
// 调整扭曲和淡出效果
|
// 调整扭曲和淡出效果
|
||||||
float distortionFactor = 1.0 - vDist * 0.05;
|
float distortionFactor = 1.0 - vDist * 0.005;
|
||||||
float alpha = 0.9 + 0.1 * sin(time * 2.0 + vRandom * 6.28);
|
float alpha = 0.99 + 0.01 * sin(time * 2.0 + vRandom * 6.28);
|
||||||
|
|
||||||
// 边缘淡出效果
|
// 边缘淡出效果
|
||||||
float edgeFade = 1.0 - length(gl_PointCoord - vec2(0.5)) * 2.0;
|
float edgeFade = 1.0 - length(gl_PointCoord - vec2(0.5)) * 2.0;
|
||||||
edgeFade = smoothstep(0.0, 0.5, edgeFade);
|
edgeFade = smoothstep(0.0, 0.5, edgeFade);
|
||||||
|
|
||||||
gl_FragColor = vec4(texColor.rgb * distortionFactor, texColor.a * alpha * edgeFade);
|
gl_FragColor = vec4(adjustedColor, texColor.a * alpha * edgeFade);
|
||||||
}`,
|
}`,
|
||||||
uniforms: {
|
uniforms: {
|
||||||
mainTexture: { value: videoTexture },
|
mainTexture: { value: texture.value },
|
||||||
mouse: { value: new THREE.Vector2(0, 0) },
|
mouse: { value: new THREE.Vector2(0, 0) },
|
||||||
radius: { value: 0.5 },
|
radius: { value: 0.5 },
|
||||||
time: { value: 0 },
|
time: { value: 0 },
|
||||||
@ -166,8 +280,11 @@ onMounted(async () => {
|
|||||||
function animate() {
|
function animate() {
|
||||||
requestAnimationFrame(animate);
|
requestAnimationFrame(animate);
|
||||||
|
|
||||||
if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
if (texture.value) {
|
||||||
videoTexture.needsUpdate = true;
|
if (isVideo.value && video.readyState === video.HAVE_ENOUGH_DATA) {
|
||||||
|
(texture.value as THREE.VideoTexture).needsUpdate = true;
|
||||||
|
}
|
||||||
|
material.uniforms.mainTexture.value = texture.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
material.uniforms.time.value += 0.016;
|
material.uniforms.time.value += 0.016;
|
||||||
@ -185,6 +302,16 @@ onMounted(async () => {
|
|||||||
|
|
||||||
renderer.setSize(width, height);
|
renderer.setSize(width, height);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 监听视频暂停事件
|
||||||
|
video.addEventListener("pause", () => {
|
||||||
|
isPlaying.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听视频播放事件
|
||||||
|
video.addEventListener("play", () => {
|
||||||
|
isPlaying.value = true;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 组件卸载时清理
|
// 组件卸载时清理
|
||||||
@ -193,6 +320,17 @@ onUnmounted(() => {
|
|||||||
video.pause();
|
video.pause();
|
||||||
video.src = "";
|
video.src = "";
|
||||||
video.load();
|
video.load();
|
||||||
|
|
||||||
|
// 清理创建的对象URL
|
||||||
|
if (mediaSource.value.startsWith("blob:")) {
|
||||||
|
URL.revokeObjectURL(mediaSource.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理纹理
|
||||||
|
if (texture.value) {
|
||||||
|
texture.value.dispose();
|
||||||
|
texture.value = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -202,4 +340,58 @@ canvas {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.play-button {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-button:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
transform: translate(-50%, -50%) scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-icon {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 20px 0 20px 30px;
|
||||||
|
border-color: transparent transparent transparent #ffffff;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-container {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-btn {
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-btn:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -2,13 +2,43 @@ import { createRouter, createWebHashHistory } from "vue-router";
|
|||||||
import Index from "../pages/file/index.vue";
|
import Index from "../pages/file/index.vue";
|
||||||
import Voice from "../pages/voice/webrtcVoice.vue";
|
import Voice from "../pages/voice/webrtcVoice.vue";
|
||||||
import PointShader from "../pages/pointShader/pointShader.vue";
|
import PointShader from "../pages/pointShader/pointShader.vue";
|
||||||
|
import { disablePwaPrompt, enablePwaPrompt } from '../utils/pwa';
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: [
|
routes: [
|
||||||
{ path: "/", component: Index },
|
{
|
||||||
{ path: "/voice", component: Voice },
|
path: "/",
|
||||||
{ path: "/pointShader", component: PointShader },
|
component: Index,
|
||||||
|
meta: {
|
||||||
|
showPwaPrompt: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/voice",
|
||||||
|
component: Voice,
|
||||||
|
meta: {
|
||||||
|
showPwaPrompt: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/pointShader",
|
||||||
|
component: PointShader,
|
||||||
|
meta: {
|
||||||
|
showPwaPrompt: false
|
||||||
|
}
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 路由守卫控制 PWA 提示
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
if (to.meta.showPwaPrompt) {
|
||||||
|
enablePwaPrompt();
|
||||||
|
} else {
|
||||||
|
disablePwaPrompt();
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
11
src/types/virtual-pwa-register.d.ts
vendored
Normal file
11
src/types/virtual-pwa-register.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
declare module 'virtual:pwa-register' {
|
||||||
|
export interface RegisterSWOptions {
|
||||||
|
immediate?: boolean
|
||||||
|
onNeedRefresh?: () => void
|
||||||
|
onOfflineReady?: () => void
|
||||||
|
onRegistered?: (registration: ServiceWorkerRegistration | undefined) => void
|
||||||
|
onRegisterError?: (error: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerSW(options?: RegisterSWOptions): (reloadPage?: boolean) => Promise<void>
|
||||||
|
}
|
46
src/utils/pwa.ts
Normal file
46
src/utils/pwa.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// PWA 安装状态
|
||||||
|
let deferredPrompt: any = null;
|
||||||
|
|
||||||
|
// 控制是否显示安装提示
|
||||||
|
let showInstallPrompt = true;
|
||||||
|
|
||||||
|
// 监听 beforeinstallprompt 事件
|
||||||
|
window.addEventListener('beforeinstallprompt', (e) => {
|
||||||
|
if (!showInstallPrompt) {
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deferredPrompt = e;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 检查是否为 PWA 模式
|
||||||
|
export const isPwa = () => {
|
||||||
|
return window.matchMedia('(display-mode: standalone)').matches ||
|
||||||
|
(window.navigator as any).standalone ||
|
||||||
|
document.referrer.includes('android-app://');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 禁用 PWA 安装提示
|
||||||
|
export const disablePwaPrompt = () => {
|
||||||
|
showInstallPrompt = false;
|
||||||
|
if (deferredPrompt) {
|
||||||
|
deferredPrompt = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 启用 PWA 安装提示
|
||||||
|
export const enablePwaPrompt = () => {
|
||||||
|
showInstallPrompt = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 手动触发 PWA 安装提示
|
||||||
|
export const showPwaInstallPrompt = async () => {
|
||||||
|
if (!deferredPrompt) {
|
||||||
|
console.log('No PWA installation prompt available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deferredPrompt.prompt();
|
||||||
|
const { outcome } = await deferredPrompt.userChoice;
|
||||||
|
deferredPrompt = null;
|
||||||
|
return outcome;
|
||||||
|
};
|
BIN
static/icon256.png
Normal file
BIN
static/icon256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
static/icon64.png
Normal file
BIN
static/icon64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -1,13 +1,43 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import basicSsl from '@vitejs/plugin-basic-ssl'
|
import basicSsl from '@vitejs/plugin-basic-ssl'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
build: {
|
build: {
|
||||||
// 开发阶段启用源码映射:https://uniapp.dcloud.net.cn/tutorial/migration-to-vue3.html#需主动开启-sourcemap
|
// 开发阶段启用源码映射:https://uniapp.dcloud.net.cn/tutorial/migration-to-vue3.html#需主动开启-sourcemap
|
||||||
sourcemap: process.env.NODE_ENV === 'development',
|
sourcemap: process.env.NODE_ENV === 'development',
|
||||||
},
|
},
|
||||||
plugins: [vue(), basicSsl()],
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
basicSsl(),
|
||||||
|
VitePWA({
|
||||||
|
registerType: 'autoUpdate',
|
||||||
|
manifest: {
|
||||||
|
name: 'P2P Explorer',
|
||||||
|
short_name: 'P2P Explorer',
|
||||||
|
description: 'P2P文件传输与浏览工具',
|
||||||
|
theme_color: '#f5f5f5',//米色
|
||||||
|
background_color: '#f5f5f5',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: '/static/icon64.png',
|
||||||
|
sizes: '64x64',
|
||||||
|
type: 'image/png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/static/icon256.png',
|
||||||
|
sizes: '256x256',
|
||||||
|
type: 'image/png'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
workbox: {
|
||||||
|
globPatterns: ['**/*.{js,css,html,ico,png,svg,webp}'],
|
||||||
|
runtimeCaching: []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user