更新.gitignore配置,添加ContextMenu组件类型声明,构建发布dist目录

This commit is contained in:
2025-12-24 11:03:12 +08:00
parent 5cb696ac88
commit cf0275358f
22 changed files with 512 additions and 323 deletions

1
.gitignore vendored
View File

@ -12,7 +12,6 @@ node_modules
dist-ssr dist-ssr
coverage coverage
*.local *.local
/cypress/videos/ /cypress/videos/
/cypress/screenshots/ /cypress/screenshots/

1
components.d.ts vendored
View File

@ -9,6 +9,7 @@ export {}
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
Aplayer: typeof import('./src/components/aplayer.vue')['default'] Aplayer: typeof import('./src/components/aplayer.vue')['default']
ContextMenu: typeof import('./src/components/contextMenu.vue')['default']
DAside: typeof import('vue-devui/layout/index.es.js')['Aside'] DAside: typeof import('vue-devui/layout/index.es.js')['Aside']
DAvatar: typeof import('vue-devui/avatar/index.es.js')['Avatar'] DAvatar: typeof import('vue-devui/avatar/index.es.js')['Avatar']
DButton: typeof import('vue-devui/button/index.es.js')['Button'] DButton: typeof import('vue-devui/button/index.es.js')['Button']

View File

@ -1 +1 @@
import{d as a,c as s,o,a as t,_ as p}from"./index-CKgGlOuy.js";const r={class:"appshare-page"},n=a({__name:"AppShare",setup(_){return(c,e)=>(o(),s("div",r,e[0]||(e[0]=[t("h1",null,"软件分享页",-1)])))}}),l=p(n,[["__scopeId","data-v-e10305df"]]);export{l as default}; import{d as a,c as s,o,a as t,_ as p}from"./index-BEOLs7p_.js";const r={class:"appshare-page"},n=a({__name:"AppShare",setup(_){return(c,e)=>(o(),s("div",r,e[0]||(e[0]=[t("h1",null,"软件分享页",-1)])))}}),l=p(n,[["__scopeId","data-v-e10305df"]]);export{l as default};

View File

@ -1,4 +1,4 @@
import{g as Ci,d as Li,r as Mi,b as ki,c as Ti,o as _i,a as Ai,_ as xi}from"./index-CKgGlOuy.js";var Lt={exports:{}};/*! import{g as Ci,d as Li,r as Mi,b as ki,c as Ti,o as _i,a as Ai,_ as xi}from"./index-BEOLs7p_.js";var Lt={exports:{}};/*!
* Vditor v3.11.2 - A markdown editor written in TypeScript. * Vditor v3.11.2 - A markdown editor written in TypeScript.
* *
* MIT License * MIT License

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

194
dist/assets/Home-dRPuO3ya.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{d as e,c as o,o as n}from"./index-CKgGlOuy.js";const s=e({__name:"NotFound",setup(t){return(a,c)=>(n(),o("div",null," 404 "))}});export{s as default}; import{d as e,c as o,o as n}from"./index-BEOLs7p_.js";const s=e({__name:"NotFound",setup(t){return(a,c)=>(n(),o("div",null," 404 "))}});export{s as default};

View File

@ -1 +1 @@
import{d as s,c as o,o as t,a,_ as n}from"./index-CKgGlOuy.js";const _={class:"plink-page"},c=s({__name:"Plink",setup(p){return(r,e)=>(t(),o("div",_,e[0]||(e[0]=[a("h1",null,"友链页",-1)])))}}),d=n(c,[["__scopeId","data-v-28948e5b"]]);export{d as default}; import{d as s,c as o,o as t,a,_ as n}from"./index-BEOLs7p_.js";const _={class:"plink-page"},c=s({__name:"Plink",setup(p){return(r,e)=>(t(),o("div",_,e[0]||(e[0]=[a("h1",null,"友链页",-1)])))}}),d=n(c,[["__scopeId","data-v-28948e5b"]]);export{d as default};

View File

@ -1 +1 @@
import{_ as i,c as l,o as _,a as t,i as s,M as o,L as a,j as r,R as p}from"./index-CKgGlOuy.js";const f={class:"widget-page"},m={class:"flex items-center"},u={class:"p-4"},v={__name:"Widget",setup(g){return(w,e)=>{const n=a("icon-widget"),c=a("vue3VideoPlay"),d=p;return _(),l("div",f,[e[1]||(e[1]=t("h1",null,"工具页",-1)),s(d,{class:"w-1/3 bg-[#ffffff60] rounded-[10px]",shadow:"hover"},{title:o(()=>[t("div",m,[s(n,{class:"w-5 mr-2"}),e[0]||(e[0]=r(" 视频工具 "))])]),content:o(()=>[t("div",u,[s(c,{class:"w-full",title:"钢铁侠",src:"https://cdn.jsdelivr.net/gh/xdlumia/files/video-play/IronMan.mp4"})])]),_:1})])}}},h=i(v,[["__scopeId","data-v-fe61d0c3"]]);export{h as default}; import{_ as i,c as l,o as _,a as t,i as s,M as o,L as a,j as r,R as p}from"./index-BEOLs7p_.js";const f={class:"widget-page"},m={class:"flex items-center"},u={class:"p-4"},v={__name:"Widget",setup(g){return(w,e)=>{const n=a("icon-widget"),c=a("vue3VideoPlay"),d=p;return _(),l("div",f,[e[1]||(e[1]=t("h1",null,"工具页",-1)),s(d,{class:"w-1/3 bg-[#ffffff60] rounded-[10px]",shadow:"hover"},{title:o(()=>[t("div",m,[s(n,{class:"w-5 mr-2"}),e[0]||(e[0]=r(" 视频工具 "))])]),content:o(()=>[t("div",u,[s(c,{class:"w-full",title:"钢铁侠",src:"https://cdn.jsdelivr.net/gh/xdlumia/files/video-play/IronMan.mp4"})])]),_:1})])}}},h=i(v,[["__scopeId","data-v-fe61d0c3"]]);export{h as default};

View File

@ -1 +1 @@
import{c as e,o}from"./index-CKgGlOuy.js";const n={__name:"home",setup(t){return(c,r)=>(o(),e("div",null," 这里是控制台首页 "))}};export{n as default}; import{c as e,o}from"./index-BEOLs7p_.js";const n={__name:"home",setup(t){return(c,r)=>(o(),e("div",null," 这里是控制台首页 "))}};export{n as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/index.html vendored
View File

@ -7,8 +7,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="这是柚子的网站,做一些分享类的功能"/> <meta name="description" content="这是柚子的网站,做一些分享类的功能"/>
<title>柚子の网站</title> <title>柚子の网站</title>
<script type="module" crossorigin src="/blog/assets/index-CKgGlOuy.js"></script> <script type="module" crossorigin src="/blog/assets/index-BEOLs7p_.js"></script>
<link rel="stylesheet" crossorigin href="/blog/assets/index-Cmmd-_pD.css"> <link rel="stylesheet" crossorigin href="/blog/assets/index-DsMJ4ylN.css">
</head> </head>
<body> <body>

View File

@ -15,6 +15,7 @@
}, },
"dependencies": { "dependencies": {
"@devui-design/icons": "^1.4.0", "@devui-design/icons": "^1.4.0",
"@imengyu/vue3-context-menu": "^1.5.2",
"@meting/core": "^1.5.13", "@meting/core": "^1.5.13",
"@unocss/reset": "^66.3.3", "@unocss/reset": "^66.3.3",
"aplayer": "^1.10.1", "aplayer": "^1.10.1",

View File

@ -7,12 +7,15 @@
<d-modal class="!w-80" v-model="modal.visible" :title="modal.title"> <d-modal class="!w-80" v-model="modal.visible" :title="modal.title">
{{ modal.content }} {{ modal.content }}
<div class="mt-10 w-full flex justify-between"> <div class="mt-4 w-full flex justify-between">
<d-button @click="modal.handdleCancel" variant="text" <!-- <d-button @click="modal.handdleCancel" variant="text"
class="w-[49%] hover:bg-[#8a6684] hover:!text-white">{{modal.cancelText}}</d-button> class="w-[49%] hover:bg-[#8a6684] hover:!text-white">{{modal.cancelText}}</d-button>
<span class="text-[20px]"> | </span> <span class="text-[20px]"> | </span>
<d-button @click="modal.handdleSubmit" variant="text" class="w-[49%] hover:bg-[#5c866a] hover:!text-white" <d-button @click="modal.handdleSubmit" variant="text" class="w-[49%] hover:bg-[#5c866a] hover:!text-white"
color="primary">{{modal.submitText}}</d-button> color="primary">{{modal.submitText}}</d-button> -->
<d-button class="w-[48%]" variant="solid" color="secondary" @click="modal.handdleCancel">{{modal.cancelText}}</d-button>
<d-button class="w-[48%]" variant="solid" color="primary" @click="modal.handdleSubmit">{{modal.submitText}}</d-button>
</div> </div>
</d-modal> </d-modal>
</template> </template>

View File

@ -15,3 +15,20 @@ export function addNav(data: any) {
data, data,
}); });
} }
// editNav
export function editNav(id: string,data: any) {
return request({
url: `/navi/${id}`,
method: "put",
data,
});
}
// deleteNav
export function deleteNav(id: string) {
return request({
url: `/navi/${id}`,
method: "delete",
});
}

View File

@ -0,0 +1,36 @@
<template>
<Teleport to="body" v-if="show">
<div class="fixed z-[99999]" :style="menuStyle">
<slot></slot>
</div>
</Teleport>
</template>
<script setup lang="ts">
//mark import
//mark data
const props = defineProps<{
show: boolean,
options: any
}>()
let timer:any = null
const menuStyle = computed(() => {
return {
top: props.options.y + 'px',
left: props.options.x + 'px',
}
})
//mark method
//mark 周期、内置函数等
onMounted(() => {
})
</script>
<style scoped lang="less">
</style>

View File

@ -12,7 +12,7 @@ import { ThemeServiceInit, infinityTheme, sweetTheme } from "devui-theme";
import { PerfectScrollbarPlugin } from "vue3-perfect-scrollbar"; import { PerfectScrollbarPlugin } from "vue3-perfect-scrollbar";
// import vue3videoPlay from "vue3-video-play"; // import vue3videoPlay from "vue3-video-play";
// import "vue3-video-play/dist/style.css"; // import "vue3-video-play/dist/style.css";
//main.js
// ThemeServiceInit({ customTheme }, "customTheme"); // ThemeServiceInit({ customTheme }, "customTheme");
const themeService = ThemeServiceInit({ infinityTheme }, "infinityTheme"); const themeService = ThemeServiceInit({ infinityTheme }, "infinityTheme");

View File

@ -21,15 +21,17 @@
<PerfectScrollbar class="" :style="navStyle"> <PerfectScrollbar class="" :style="navStyle">
<div class="navcard grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6 gap-5 pt-3 pb-6 px-12"> <div class="navcard grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6 gap-5 pt-3 pb-6 px-12">
<d-card class="bg-[#ffffff80] h-24" v-for="(item, index) in navlist" :key="index" <d-card class="bg-[#ffffff80] h-24" v-for="(item, index) in navlist" :key="index"
@click="goExtra(item.menu_link)"> @click="goExtra(item.menu_link)" @contextmenu.prevent="handdleContextMenu($event, item)">
<template #content> <template #content>
<div class="mt-1 w-full flex flex-col items-center cursor-pointer hover:!text-primary"> <div class="mt-1 w-full flex flex-col items-center cursor-pointer hover:!text-primary">
<div :style="{ background: item.color }" <div :style="{ background: item.color }"
class="w-8 h-8 rounded-full text-white flex items-center justify-center" v-if="item.icon_error"> class="w-8 h-8 rounded-full text-white flex items-center justify-center" v-if="item.icon_error">
{{ item.first }}</div> {{ item.first }}</div>
<img class="grid-image w-8 h-8 rounded-full" v-else :src="item.menu_icon" @error="imgErr(index as number)" /> <img class="grid-image w-8 h-8 rounded-full" v-else :src="item.menu_icon"
@error="imgErr(index as number)" />
<div class="mt-2 w-full text-center text-lg truncate">{{ item.menu_name || "" }}</div> <div class="mt-2 w-full text-center text-lg truncate">{{ item.menu_name || "" }}</div>
<em class="absolute rounded-md top-0 left-0 px-2 text-white text-center text-sm" :style="{ background: getItemColor(item) }">{{ item.tag }}</em> <em class="absolute rounded-md top-0 left-0 px-2 text-white text-center text-sm"
:style="{ background: getItemColor(item) }">{{ item.tag }}</em>
</div> </div>
</template> </template>
</d-card> </d-card>
@ -81,13 +83,45 @@
<!-- 音乐插件 --> <!-- 音乐插件 -->
<aplayer></aplayer> <aplayer></aplayer>
<!-- 右键菜单 -->
<contextMenu :show="menuShow" :options="MenuOptions">
<div class="!p-0 !m-0 bg-white cursor-pointer !rounded-md" @mouseenter="removeTimer" @mouseleave="hideMenu">
<div :class="menuS" @click="handdleMenuItem(1)">
修改名称
</div>
<div :class="menuS" @click="handdleMenuItem(2)">
修改链接
</div>
<div :class="menuS" @click="handdleMenuItem(3)">
修改标签
</div>
<div :class="menuS" @click="handdleMenuItem(4)">
删除导航
</div>
</div>
</contextMenu>
<!-- 编辑弹窗 -->
<d-modal class="!w-120" v-model="editModal" title="导航修改">
<div class="mb-2">
<span class="text-primary" v-if="currentClickedItem === 1">导航名称{{ currentItem.menu_name }}</span>
<span class="text-primary" v-if="currentClickedItem === 2">导航链接{{ currentItem.menu_link }}</span>
<span class="text-primary" v-if="currentClickedItem === 3">导航标签{{ currentItem.tag }}</span>
</div>
<d-input v-model="editInput" placeholder="请输入修改内容"></d-input>
<div class="mt-8 flex justify-between">
<d-button class="w-[48%]" variant="solid" color="secondary" @click="handdleItemCancel">取消</d-button>
<d-button class="w-[48%]" variant="solid" color="primary" @click="handdleItemSubmit">确定</d-button>
</div>
</d-modal>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { deepclone, getDictValue } from '@/util/index.ts' import contextMenu from '@/components/contextMenu.vue';
import { deepclone, getDictValue } from '@/util/index.ts';
definePage({ definePage({
name: 'home', name: 'home',
meta: { meta: {
@ -95,8 +129,17 @@ definePage({
} }
}) })
// 右键菜单
const menuShow = ref(false)
const MenuOptions: any = reactive({});
const currentItem = ref<any>(null);
const menuS = '!m-0 py-3 px-6 text-sm text-gray-700 w-full flex items-center justify-center hover:bg-[#f5f0f0] hover:text-primary';
const currentClickedItem = ref<number>(0);
const editModal = ref<boolean>(false);
const editInput = ref<string>('');
// 新增导航弹窗 // 新增导航弹窗
const visible: any = ref(false) const visible: any = ref<boolean>(false)
const navData: any = reactive({ const navData: any = reactive({
menu_link: '', menu_link: '',
menu_name: '', menu_name: '',
@ -149,6 +192,72 @@ const tagList: any = ref([
const usrLog = $store.log.useLogStore() const usrLog = $store.log.useLogStore()
let timer: any = null;
// 2秒后自动隐藏菜单
const hideMenu = () => {
timer = setTimeout(() => {
menuShow.value = false
}, 2000)
}
const removeTimer = () => {
clearTimeout(timer)
}
function handdleMenuItem(type: number) {
menuShow.value = false
currentClickedItem.value = type
if (type == 4) {
// 删除导航
if (!currentItem.value) return
$modal({
title: '删除导航',
content: `确定要删除【${currentItem.value.menu_name}】吗?删除后不可恢复哦~`,
cancelText: '取消',
submitText: '删除',
handdleSubmit: async () => {
const res = await $http.nav.deleteNav(currentItem.value.nid)
console.log('>>> --> handdleMenuItem --> res:', res)
if (res.code == 200) {
$msg.success('删除成功')
getNavList()
}
}
})
return
}
editModal.value = true
}
function handdleItemCancel() {
editModal.value = false
editInput.value = ''
}
async function handdleItemSubmit() {
if (!editInput.value) {
$msg.error('请输入修改内容')
return
}
let updateData: any = {}
if (currentClickedItem.value == 1) {
updateData.menu_name = editInput.value
} else if (currentClickedItem.value == 2) {
updateData.menu_link = editInput.value
} else if (currentClickedItem.value == 3) {
updateData.tag = editInput.value.slice(0, 4)
}
const res = await $http.nav.editNav(currentItem.value.nid, updateData)
console.log('>>> --> handdleItemSubmit --> res:', res)
if (res.code == 200) {
$msg.success('修改成功')
editModal.value = false
editInput.value = ''
getNavList()
}
}
async function getNavList() { async function getNavList() {
const userinfo = $cookies.get("userinfo") const userinfo = $cookies.get("userinfo")
if (!userinfo) { if (!userinfo) {
@ -166,7 +275,7 @@ async function getNavList() {
console.log("&&&&&&&&&&&&&&", navlist.value); console.log("&&&&&&&&&&&&&&", navlist.value);
res.data.forEach((i: any) => { res.data.forEach((i: any) => {
if (!tagList.value.find((t: any) => t.name == i.tag)) if (!tagList.value.find((t: any) => t.name == i.tag))
tagList.value.push({ name: i.tag, color: getRandomDarkColor(30, 128), checked: false }) tagList.value.push({ name: i.tag.substring(0, 4), color: getRandomDarkColor(30, 128), checked: false })
}) })
cloneNavlist = deepclone(res.data) cloneNavlist = deepclone(res.data)
} }
@ -290,7 +399,24 @@ async function navSubmit() {
formNav.value.resetFields() formNav.value.resetFields()
getNavList() getNavList()
} }
// 清空navData
navData.menu_link = ''
navData.menu_name = ''
navData.tag = ''
navData.menu_icon = ''
} }
// 处理右键菜单
function handdleContextMenu(event: MouseEvent, item: any) {
menuShow.value = true; // 先隐藏菜单以重置位置
MenuOptions.x = event.clientX;
MenuOptions.y = event.clientY;
currentItem.value = item; // 存储当前右键点击的项
}
// 监听store的登录状态 // 监听store的登录状态
watch(() => usrLog.isLogin, (newVal) => { watch(() => usrLog.isLogin, (newVal) => {
console.log('********** --> watch --> newVal:', newVal) console.log('********** --> watch --> newVal:', newVal)
@ -325,14 +451,8 @@ onMounted(() => {
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
/* 首页样式 /* layer: default */
.main-content {
background-image: url('@/assets/images/小鹿.png');
background-repeat: no-repeat;
background-position: 111% center;
background-size: auto 100%;
}
*/
.navcard { .navcard {
display: grid; display: grid;

View File

@ -507,6 +507,18 @@
dependencies: dependencies:
katex "^0.12.0" katex "^0.12.0"
"@imengyu/vue-scroll-rect@^0.1.4":
version "0.1.7"
resolved "https://registry.npmmirror.com/@imengyu/vue-scroll-rect/-/vue-scroll-rect-0.1.7.tgz#c72344f6c0094e2230e584a7919b9e19e4de97ae"
integrity sha512-a1ysL8LV2iFcHzMJ6tlrqr8JKtYIBrb1U3XcbjNmhponENUSbRvxs4b1sJd5sXdCFIIkDydwQM/SjVhfT7uBUw==
"@imengyu/vue3-context-menu@^1.5.2":
version "1.5.2"
resolved "https://registry.npmmirror.com/@imengyu/vue3-context-menu/-/vue3-context-menu-1.5.2.tgz#4f5fa1c12a4b004d0ce6e004a6b717c30053e1bb"
integrity sha512-JtM9y4JKsqwpsLQnK38Fm3DqV5ZwIs/HksxQYHU6yUuuWScdr5C+NTF16J0KxmSbMHOTwVTMwJCBkwcC1X6y1w==
dependencies:
"@imengyu/vue-scroll-rect" "^0.1.4"
"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5":
version "0.3.12" version "0.3.12"
resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz#2234ce26c62889f03db3d7fea43c1932ab3e927b" resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz#2234ce26c62889f03db3d7fea43c1932ab3e927b"