refacting: delete cloud comp
This commit is contained in:
@@ -1,31 +0,0 @@
|
|||||||
import { MockMethod } from 'vite-plugin-mock'
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
url: '/cloud/api/v1/file/list',
|
|
||||||
method: 'get',
|
|
||||||
response: ({ body, query }) => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
server_filename: "java实战.pdf",
|
|
||||||
path: "/",
|
|
||||||
isDir: 0,
|
|
||||||
size: 1000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
server_filename: "天道.pdf",
|
|
||||||
path: "/",
|
|
||||||
isDir: 0,
|
|
||||||
size: 1000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
server_filename: "我得音乐",
|
|
||||||
path: "/",
|
|
||||||
isDir: 1,
|
|
||||||
size: 10001
|
|
||||||
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
] as MockMethod[]
|
|
||||||
+13
-2
@@ -16,10 +16,21 @@ const post_event = (data: {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const get_event = () => {
|
/**
|
||||||
|
* 查询事件(按月份 + 分页)
|
||||||
|
* @param params.month yyyy-MM(必填,null 时后端默认当前月)
|
||||||
|
* @param params.page 页码(从 1 开始,默认 1)
|
||||||
|
* @param params.pageSize 每页大小(默认 10,上限 100)
|
||||||
|
*/
|
||||||
|
const get_event = (params?: {
|
||||||
|
month?: string
|
||||||
|
page?: number
|
||||||
|
pageSize?: number
|
||||||
|
}) => {
|
||||||
return service({
|
return service({
|
||||||
url: '/calendar/api/v1/events',
|
url: '/calendar/api/v1/events',
|
||||||
method: 'GET'
|
method: 'GET',
|
||||||
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import { service } from '../utils/request'
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
'Content-Type': 'application/json;charsetset=UTF-8'
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFileList = (url: string) => {
|
|
||||||
return service({
|
|
||||||
url: '/cloud/api/v1/file/list',
|
|
||||||
method: 'GET',
|
|
||||||
params: { url: url },
|
|
||||||
headers: headers
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export { getFileList }
|
|
||||||
@@ -21,9 +21,6 @@
|
|||||||
<el-icon v-if="index == 4" :size="20">
|
<el-icon v-if="index == 4" :size="20">
|
||||||
<Calendar />
|
<Calendar />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<el-icon v-if="index == 5" :size="20">
|
|
||||||
<MostlyCloudy />
|
|
||||||
</el-icon>
|
|
||||||
<el-icon v-if="index == 10" :size="20">
|
<el-icon v-if="index == 10" :size="20">
|
||||||
<Setting />
|
<Setting />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
|
|||||||
+477
-40
@@ -1,22 +1,73 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ttt">
|
<div class="ttt">
|
||||||
<div class="yuliu" :style="style">
|
<!-- 动态光斑背景 -->
|
||||||
|
<div class="bg-orbs">
|
||||||
|
<span class="orb orb-1"></span>
|
||||||
|
<span class="orb orb-2"></span>
|
||||||
|
<span class="orb orb-3"></span>
|
||||||
</div>
|
</div>
|
||||||
<el-card class="box-card">
|
|
||||||
|
|
||||||
<el-form>
|
<div class="content">
|
||||||
<div class="title-container">
|
<!-- 玻璃卡片 -->
|
||||||
<h3>秘密基地</h3>
|
<div class="glass-card">
|
||||||
|
<div class="brand">
|
||||||
|
<span class="brand-mark">✦</span>
|
||||||
|
<h1 class="brand-name">{{ SITE_NAME }}</h1>
|
||||||
|
<p class="brand-sub">Secret Base · 秘密基地</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<el-button type="primary" size="large" @click="handleOidcLogin">统一登录</el-button>
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
class="login-btn"
|
||||||
|
size="large"
|
||||||
|
@click="handleOidcLogin"
|
||||||
|
>
|
||||||
|
<span class="login-btn-content">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M12 2L3 7v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-9-5zm0 2.18l7 3.89v4.93c0 4.43-3.07 8.58-7 9.71-3.93-1.13-7-5.28-7-9.71V8.07l7-3.89z"/>
|
||||||
|
<path d="M12 11h-2v2h-2v2h2v2h2v-2h2v-2h-2z"/>
|
||||||
|
</svg>
|
||||||
|
Login
|
||||||
|
</span>
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<!-- <p class="login-hint">
|
||||||
|
通过 <a href="https://goauthentik.io" target="_blank">Authentik</a> OIDC 安全登录
|
||||||
|
</p> -->
|
||||||
</div>
|
</div>
|
||||||
</el-form>
|
|
||||||
------------------------------------------------------------
|
|
||||||
<div>
|
|
||||||
微信/支付宝
|
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
|
||||||
|
<!-- 右侧关联链接 -->
|
||||||
|
<div class="side-links">
|
||||||
|
<a
|
||||||
|
v-for="link in sideLinks"
|
||||||
|
:key="link.name"
|
||||||
|
:href="link.url"
|
||||||
|
target="_blank"
|
||||||
|
class="side-link"
|
||||||
|
:title="link.name"
|
||||||
|
>
|
||||||
|
<span class="side-link-icon" v-html="link.icon"></span>
|
||||||
|
<span class="side-link-name">{{ link.name }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 左侧外部网站链接 -->
|
||||||
|
<div class="external-links">
|
||||||
|
<a
|
||||||
|
v-for="link in externalLinks"
|
||||||
|
:key="link.name"
|
||||||
|
:href="link.url"
|
||||||
|
target="_blank"
|
||||||
|
class="external-link"
|
||||||
|
:title="link.name"
|
||||||
|
>
|
||||||
|
<span class="external-link-icon" v-html="link.icon"></span>
|
||||||
|
<span class="external-link-name">{{ link.name }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="footer-left">
|
<div class="footer-left">
|
||||||
<span>{{ SITE_NAME }} {{ COPYRIGHT_TEXT }}</span>
|
<span>{{ SITE_NAME }} {{ COPYRIGHT_TEXT }}</span>
|
||||||
@@ -32,7 +83,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive } from 'vue'
|
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { SITE_NAME, COPYRIGHT_TEXT, ICP_NUMBER, ICP_QUERY_URL } from '@/const/site'
|
import { SITE_NAME, COPYRIGHT_TEXT, ICP_NUMBER, ICP_QUERY_URL } from '@/const/site'
|
||||||
|
|
||||||
@@ -62,50 +112,430 @@ const openGithub = () => {
|
|||||||
window.open('https://github.com/simple321vip', '_blank')
|
window.open('https://github.com/simple321vip', '_blank')
|
||||||
}
|
}
|
||||||
|
|
||||||
const style = reactive({} as any)
|
/**
|
||||||
style.width = window.innerWidth * 0.7 + 'px'
|
* 右侧固定关联网站链接。改这里即可增删。
|
||||||
|
* icon: 简单 inline SVG(24x24),用 currentColor 跟父元素文字色联动
|
||||||
|
*/
|
||||||
|
const sideLinks = [
|
||||||
|
{
|
||||||
|
name: 'Gitea',
|
||||||
|
url: 'https://gitea.violin-work.online',
|
||||||
|
// Gitea 标志性元素:圆形 + 三个肉垫(猫爪印)
|
||||||
|
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="14" r="6"/><ellipse cx="6" cy="8" rx="2" ry="2.5"/><ellipse cx="12" cy="5" rx="2" ry="2.5"/><ellipse cx="18" cy="8" rx="2" ry="2.5"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Blog',
|
||||||
|
url: 'https://www.violin-home.cn/blog',
|
||||||
|
icon: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zM7 10h10v2H7zm0 4h10v2H7z"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Authentik',
|
||||||
|
url: 'https://auth.violin-work.online',
|
||||||
|
// Authentik 标志性元素:盾牌 + 钥匙孔
|
||||||
|
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.5a2 2 0 110 1 2 2 0 010-1zm-2.83 4.95L8 14l-1.17 2.45L4 18l2.83-1.55z"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'KubeSphere',
|
||||||
|
url: 'https://k8s.violin-work.online',
|
||||||
|
// KubeSphere 标志性元素:球体 + 经线纬线(地球网格)
|
||||||
|
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><ellipse cx="12" cy="12" rx="10" ry="4"/><ellipse cx="12" cy="12" rx="4" ry="10"/><line x1="2" y1="12" x2="22" y2="12"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Viking (RAG)',
|
||||||
|
url: 'https://viking.violin-work.online/',
|
||||||
|
// Viking / 维京:维京头盔 + 翅膀,或简化为双翼头盔
|
||||||
|
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2L9 5H6v4l-3 2 3 1v3a6 6 0 0012 0v-3l3-1-3-2V5h-3l-3-3zm0 4a2 2 0 110 4 2 2 0 010-4z"/><path d="M3 11l-1 1 1 1zm18 0l1 1-1 1z"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Nextcloud',
|
||||||
|
url: 'https://cloud.violin-work.online/',
|
||||||
|
// Nextcloud 标志性元素:三个圆环组成的"云"
|
||||||
|
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="7" cy="14" r="4"/><circle cx="14" cy="9" r="4"/><circle cx="18" cy="15" r="3"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MinIO',
|
||||||
|
url: 'https://minio.violin-work.online/',
|
||||||
|
// MinIO 标志性元素:三层堆叠的小方块
|
||||||
|
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><rect x="3" y="14" width="18" height="6" rx="1"/><rect x="5" y="8" width="14" height="5" rx="1" opacity="0.7"/><rect x="7" y="3" width="10" height="4" rx="1" opacity="0.45"/></svg>'
|
||||||
|
}
|
||||||
|
] as const
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 左侧固定外部网站链接。
|
||||||
|
*/
|
||||||
|
const externalLinks = [
|
||||||
|
{
|
||||||
|
name: 'GitHub',
|
||||||
|
url: 'https://github.com/simple321vip',
|
||||||
|
icon: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.4 3-.405 1.02.005 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MiniMax',
|
||||||
|
url: 'https://www.minimaxi.com/',
|
||||||
|
// MiniMax 标志性元素:六角星 + 中心点(极小化极大概括)
|
||||||
|
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l2.5 5.5L20 9l-4 4 1 6-5-3-5 3 1-6-4-4 5.5-1.5L12 2z"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '百度',
|
||||||
|
url: 'https://www.baidu.com/',
|
||||||
|
// 百度:爪形脚印
|
||||||
|
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><ellipse cx="9" cy="11" rx="3.5" ry="5"/><ellipse cx="15" cy="11" rx="3.5" ry="5"/><circle cx="6" cy="5" r="1.5"/><circle cx="12" cy="3" r="1.5"/><circle cx="18" cy="5" r="1.5"/></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '腾讯云',
|
||||||
|
url: 'https://cloud.tencent.com/',
|
||||||
|
// 腾讯云:云朵 + T 字标识
|
||||||
|
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M6 18a4 4 0 010-8 5 5 0 019.58-1.5A4 4 0 0118 18H6z"/><text x="12" y="16" font-size="8" font-weight="bold" text-anchor="middle" fill="#0f0c29">T</text></svg>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '菜鸟教程',
|
||||||
|
url: 'https://www.runoob.com/',
|
||||||
|
// 菜鸟教程:鸟的剪影(飞行)
|
||||||
|
icon: '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M22 4l-2 2-3-1-5 3-4-2-3 3 2 2 4-2 3 2 4-2 4 1 1-2-3-2 2-2zM2 14l4 4 6-1 4 2 2-1-3-3-4 1-3-3-4 1-2-1z"/></svg>'
|
||||||
|
}
|
||||||
|
] as const
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.text {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
padding: 18px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ttt {
|
.ttt {
|
||||||
/* flex column + min-height 100vh:footer 永远在视口底部,
|
position: relative;
|
||||||
内容少时 footer 贴底,内容多时 footer 在内容之后但不漂 */
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.yuliu {
|
/* ============ 动态光斑背景 ============ */
|
||||||
/* width: 600px; */
|
.bg-orbs {
|
||||||
height: 300px;
|
position: absolute;
|
||||||
float: left;
|
inset: 0;
|
||||||
/* background-color: antiquewhite; */
|
overflow: hidden;
|
||||||
background: url('../../assets/have_a_nice_day.jpeg') center top no-repeat;
|
pointer-events: none;
|
||||||
background-size: cover;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box-card {
|
.orb {
|
||||||
/* 中间内容区撑开剩余空间 */
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
filter: blur(80px);
|
||||||
|
opacity: 0.5;
|
||||||
|
animation: float 20s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orb-1 {
|
||||||
|
width: 500px;
|
||||||
|
height: 500px;
|
||||||
|
background: radial-gradient(circle, #667eea 0%, transparent 70%);
|
||||||
|
top: -150px;
|
||||||
|
left: -100px;
|
||||||
|
animation-delay: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orb-2 {
|
||||||
|
width: 600px;
|
||||||
|
height: 600px;
|
||||||
|
background: radial-gradient(circle, #f093fb 0%, transparent 70%);
|
||||||
|
bottom: -200px;
|
||||||
|
right: -150px;
|
||||||
|
animation-delay: -7s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orb-3 {
|
||||||
|
width: 400px;
|
||||||
|
height: 400px;
|
||||||
|
background: radial-gradient(circle, #4facfe 0%, transparent 70%);
|
||||||
|
top: 40%;
|
||||||
|
left: 50%;
|
||||||
|
animation-delay: -14s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes float {
|
||||||
|
0%, 100% { transform: translate(0, 0) scale(1); }
|
||||||
|
33% { transform: translate(50px, -30px) scale(1.1); }
|
||||||
|
66% { transform: translate(-30px, 50px) scale(0.9); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============ 内容区 ============ */
|
||||||
|
.content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============ 玻璃卡片 ============ */
|
||||||
|
.glass-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
padding: 56px 40px 48px;
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
-webkit-backdrop-filter: blur(20px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow:
|
||||||
|
0 8px 32px rgba(0, 0, 0, 0.3),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-mark {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 32px;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #f093fb 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
animation: pulse 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { opacity: 0.7; transform: scale(1); }
|
||||||
|
50% { opacity: 1; transform: scale(1.1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-name {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
background: linear-gradient(135deg, #fff 0%, #b8c6db 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-sub {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============ 分隔线 ============ */
|
||||||
|
.divider {
|
||||||
|
height: 1px;
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
transparent 0%,
|
||||||
|
rgba(255, 255, 255, 0.2) 50%,
|
||||||
|
transparent 100%);
|
||||||
|
margin: 24px 0 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============ 登录按钮 ============ */
|
||||||
|
.login-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px !important;
|
||||||
|
border: none !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
||||||
|
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.6) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-btn-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-hint {
|
||||||
|
margin: 20px 0 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(255, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-hint a {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: 1px dashed rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-hint a:hover {
|
||||||
|
color: #fff;
|
||||||
|
border-bottom-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============ 右侧固定关联链接 ============ */
|
||||||
|
.side-links {
|
||||||
|
position: fixed;
|
||||||
|
right: 32px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-link {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-link:hover {
|
||||||
|
background: rgba(102, 126, 234, 0.25);
|
||||||
|
border-color: rgba(102, 126, 234, 0.6);
|
||||||
|
color: #fff;
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-link-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tooltip 显示名字 */
|
||||||
|
.side-link-name {
|
||||||
|
position: absolute;
|
||||||
|
right: 56px;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(10px);
|
||||||
|
pointer-events: none;
|
||||||
|
transition: all 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-link:hover .side-link-name {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端隐藏侧栏(屏幕太窄) */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.side-links {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============ 左侧外部网站链接 ============ */
|
||||||
|
.external-links {
|
||||||
|
position: fixed;
|
||||||
|
left: 32px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.external-link {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.external-link:hover {
|
||||||
|
background: rgba(118, 75, 162, 0.25);
|
||||||
|
border-color: rgba(240, 147, 251, 0.6);
|
||||||
|
color: #fff;
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 4px 20px rgba(240, 147, 251, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.external-link-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tooltip 在右侧(跟左侧链接方向相反) */
|
||||||
|
.external-link-name {
|
||||||
|
position: absolute;
|
||||||
|
left: 56px;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-10px);
|
||||||
|
pointer-events: none;
|
||||||
|
transition: all 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.external-link:hover .external-link-name {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.external-links {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============ Footer ============ */
|
||||||
.footer {
|
.footer {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 12px 24px;
|
padding: 16px 32px;
|
||||||
border-top: 1px solid rgba(151, 151, 151, 0.2);
|
color: rgba(255, 255, 255, 0.5);
|
||||||
font-size: 16px;
|
font-size: 13px;
|
||||||
color: #666;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-left {
|
.footer-left {
|
||||||
@@ -120,20 +550,27 @@ style.width = window.innerWidth * 0.7 + 'px'
|
|||||||
}
|
}
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
color: #ccc;
|
color: rgba(255, 255, 255, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icp-link {
|
.icp-link {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
transition: color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icp-link:hover {
|
.icp-link:hover {
|
||||||
color: #409eff;
|
color: rgba(255, 255, 255, 0.85);
|
||||||
}
|
}
|
||||||
|
|
||||||
.github-icon {
|
.github-icon {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-icon:hover {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -43,13 +43,6 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
name: '日历'
|
name: '日历'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/cloud',
|
|
||||||
component: () => import('@/view/cloud/index.vue'),
|
|
||||||
meta: {
|
|
||||||
name: '个人云盘'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/market',
|
path: '/market',
|
||||||
component: () => import('@/view/recommend/index.vue'),
|
component: () => import('@/view/recommend/index.vue'),
|
||||||
|
|||||||
+339
-102
@@ -1,61 +1,137 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form :model="form" label-width="120px">
|
<!-- 顶部 header:标题 + 主操作 -->
|
||||||
<el-form-item label="关键字搜索">
|
<div class="blog-header">
|
||||||
<el-col :span="7">
|
<div class="blog-header-left">
|
||||||
<el-input v-model="form.key_word" />
|
<span class="blog-icon">📚</span>
|
||||||
</el-col>
|
<h2 class="blog-title">博客</h2>
|
||||||
<el-col :span="1"></el-col>
|
<el-tag v-if="pagination.total > 0" type="info" size="small" round>
|
||||||
<el-col :span="10">
|
共 {{ pagination.total }} 篇
|
||||||
<span>分类搜索</span>
|
</el-tag>
|
||||||
<el-select v-model="form.btId" placeholder="选择分类">
|
</div>
|
||||||
|
<div class="blog-header-right">
|
||||||
|
<el-button type="primary" :icon="EditPen" @click="openBlogEditer">创作</el-button>
|
||||||
|
<el-dropdown @command="handleBatchCommand">
|
||||||
|
<el-button :icon="MoreFilled">
|
||||||
|
批量<el-icon class="el-icon--right"><ArrowDown /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="publishAll">
|
||||||
|
<el-icon><Promotion /></el-icon> 重新发布全部
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="publishProfile">
|
||||||
|
<el-icon><User /></el-icon> 重新发布 profile
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 搜索筛选区 -->
|
||||||
|
<el-card class="filter-card" shadow="never">
|
||||||
|
<el-form :inline="true" :model="form" class="filter-form">
|
||||||
|
<el-form-item label="关键字">
|
||||||
|
<el-input
|
||||||
|
v-model="form.key_word"
|
||||||
|
placeholder="搜索标题或内容"
|
||||||
|
clearable
|
||||||
|
style="width: 220px"
|
||||||
|
@keyup.enter="onSearch"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分类">
|
||||||
|
<el-select v-model="form.btId" placeholder="全部分类" clearable style="width: 160px">
|
||||||
<el-option v-for="item in btNameList" :key="item.btId" :label="item.btName" :value="item.btId" />
|
<el-option v-for="item in btNameList" :key="item.btId" :label="item.btName" :value="item.btId" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="创作时间">
|
<el-form-item label="时间">
|
||||||
<el-col :span="7">
|
<el-date-picker
|
||||||
<el-date-picker v-model="form.start_day" type="date" placeholder="开始时间" style="width: 100%" />
|
v-model="dateRange"
|
||||||
</el-col>
|
type="daterange"
|
||||||
<el-col :span="2" class="text-center">
|
range-separator="至"
|
||||||
<span class="text-gray-500">-</span>
|
start-placeholder="开始"
|
||||||
</el-col>
|
end-placeholder="结束"
|
||||||
<el-col :span="7">
|
value-format="YYYY-MM-DD"
|
||||||
<el-time-picker v-model="form.end_day" placeholder="结束时间" style="width: 100%" />
|
style="width: 260px"
|
||||||
</el-col>
|
/>
|
||||||
<el-col :span="6" style="text-align: center;">
|
</el-form-item>
|
||||||
<el-button type="primary" @click="onSearch">搜索</el-button>
|
<el-form-item>
|
||||||
</el-col>
|
<el-button type="primary" :icon="Search" @click="onSearch">搜索</el-button>
|
||||||
|
<el-button :icon="RefreshLeft" @click="onReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-button @click="openBlogEditer">创作</el-button>
|
</el-card>
|
||||||
<el-button @click="publishbyBids">重新发布Wiki</el-button>
|
|
||||||
<el-button @click="publishProfile">重新发布profile</el-button>
|
|
||||||
|
|
||||||
<div class="blog_list" v-for="item in data_list">
|
<!-- 加载态 -->
|
||||||
<h3 style="cursor: pointer; width: min-content;white-space:nowrap" @click="openBlog(item.bid)">{{ item.title }}</h3>
|
<div v-if="isLoading" class="loading">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
<el-icon class="is-loading"><Loading /></el-icon>
|
||||||
<p style="font-size: 12px; height: 16px;line-height: 16px;overflow: hidden;text-overflow: ellipsis;">{{
|
<span>加载中...</span>
|
||||||
item.content
|
</div>
|
||||||
}}</p>
|
|
||||||
<el-button size="small" type="success" @click="publishbyBid(item.bid)">重新发布
|
<!-- 空态 -->
|
||||||
|
<el-empty v-else-if="data_list.length === 0" description="暂无博客,点击右上方开始创作" />
|
||||||
|
|
||||||
|
<!-- 博客列表卡片 -->
|
||||||
|
<div v-else class="blog-list">
|
||||||
|
<el-card
|
||||||
|
v-for="item in data_list"
|
||||||
|
:key="item.bid"
|
||||||
|
class="blog-card"
|
||||||
|
shadow="hover"
|
||||||
|
@click="openBlog(item.bid)"
|
||||||
|
>
|
||||||
|
<div class="blog-card-header">
|
||||||
|
<h3 class="blog-card-title">{{ item.title }}</h3>
|
||||||
|
<div class="blog-card-actions" @click.stop>
|
||||||
|
<el-button size="small" :icon="View" @click="openBlog(item.bid)">查看</el-button>
|
||||||
|
<el-button size="small" type="success" :icon="Promotion" @click="publishbyBid(item.bid)">
|
||||||
|
发布
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<!-- <span style="font-size: 12px;">{{ item.blog_prex }}</span> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="page_style">
|
|
||||||
<!-- <el-pagination small layout="prev, pager, next" :total="50" /> -->
|
<p class="blog-card-preview">{{ item.content || '(无内容预览)' }}</p>
|
||||||
<el-pagination small background layout="prev, pager, next" :total="50" class="mt-4" />
|
|
||||||
|
<div class="blog-card-meta">
|
||||||
|
<el-tag v-if="item.btName" size="small" type="primary" effect="plain">
|
||||||
|
📁 {{ item.btName }}
|
||||||
|
</el-tag>
|
||||||
|
<span class="meta-item">📅 {{ item.createDate || '未知时间' }}</span>
|
||||||
|
<span v-if="readingMinutes(item.content)" class="meta-item">
|
||||||
|
⏱ {{ readingMinutes(item.content) }} 分钟
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div v-if="data_list.length > 0" class="pagination-wrap">
|
||||||
|
<el-pagination
|
||||||
|
background
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:total="pagination.total"
|
||||||
|
:page-size="pagination.pageSize"
|
||||||
|
:current-page="pagination.page"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
@size-change="onPageSizeChange"
|
||||||
|
@current-change="onPageChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Cookies from 'js-cookie'
|
import { onMounted, reactive, ref, watch } from 'vue'
|
||||||
import { reactive } from 'vue'
|
import {
|
||||||
import { getBlogs, getBtName, publish, publishAll } from '../../api/blogView'
|
Search, RefreshLeft, EditPen, MoreFilled, ArrowDown,
|
||||||
import { publish_profile } from '../../api/profile'
|
Promotion, User, View, Loading
|
||||||
import router from '../../router/index'
|
} from '@element-plus/icons-vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { getBlogs, getBtName, publish, publishAll } from '@/api/blogView'
|
||||||
|
import { publish_profile } from '@/api/profile'
|
||||||
|
import router from '@/router'
|
||||||
|
|
||||||
// -- INTERFACE OR TYPE DEFINITION --
|
// -- TYPE --
|
||||||
interface Blog {
|
interface Blog {
|
||||||
bid: string
|
bid: string
|
||||||
btId: string
|
btId: string
|
||||||
@@ -63,6 +139,7 @@ interface Blog {
|
|||||||
title: string
|
title: string
|
||||||
blog_prex: string
|
blog_prex: string
|
||||||
content: string
|
content: string
|
||||||
|
createDate?: string
|
||||||
}
|
}
|
||||||
interface Blog_Type {
|
interface Blog_Type {
|
||||||
btId: string
|
btId: string
|
||||||
@@ -72,92 +149,252 @@ interface Blog_Type {
|
|||||||
// -- REACTIVE OBJECT --
|
// -- REACTIVE OBJECT --
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
key_word: '',
|
key_word: '',
|
||||||
btId: '',
|
btId: '' as string | undefined,
|
||||||
start_day: '',
|
start_day: '' as string | undefined,
|
||||||
end_day: '',
|
end_day: '' as string | undefined,
|
||||||
page_number: 1,
|
|
||||||
page_size: 5,
|
|
||||||
total: 1
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const dateRange = ref<[string, string] | null>(null)
|
||||||
|
|
||||||
const data_list = reactive<Blog[]>([])
|
const data_list = reactive<Blog[]>([])
|
||||||
const btNameList = reactive<Blog_Type[]>([])
|
const btNameList = reactive<Blog_Type[]>([])
|
||||||
|
|
||||||
// -- EVENT DEFINITION
|
const isLoading = ref(false)
|
||||||
|
const pagination = reactive({ page: 1, pageSize: 10, total: 0 })
|
||||||
|
|
||||||
|
// -- HELPERS --
|
||||||
|
const readingMinutes = (text?: string): number => {
|
||||||
|
if (!text) return 0
|
||||||
|
// 中文 ~300 字/分钟
|
||||||
|
const len = text.length
|
||||||
|
return Math.max(1, Math.round(len / 300))
|
||||||
|
}
|
||||||
|
|
||||||
|
const extractDateRange = () => {
|
||||||
|
if (dateRange.value && dateRange.value.length === 2) {
|
||||||
|
form.start_day = dateRange.value[0]
|
||||||
|
form.end_day = dateRange.value[1]
|
||||||
|
} else {
|
||||||
|
form.start_day = undefined
|
||||||
|
form.end_day = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- EVENT DEFINITION --
|
||||||
|
const fetchBlogs = async () => {
|
||||||
|
isLoading.value = true
|
||||||
|
extractDateRange()
|
||||||
|
try {
|
||||||
|
const res = await getBlogs({
|
||||||
|
...form,
|
||||||
|
page: pagination.page,
|
||||||
|
page_size: pagination.pageSize
|
||||||
|
})
|
||||||
|
// 后端 ApiResponse 结构:{code, message, data: PageResponse | List}
|
||||||
|
const payload = res.data?.data ?? res.data
|
||||||
|
const items = Array.isArray(payload) ? payload : (payload?.items ?? [])
|
||||||
|
pagination.total = payload?.total ?? items.length
|
||||||
|
pagination.page = payload?.page ?? pagination.page
|
||||||
|
pagination.pageSize = payload?.pageSize ?? pagination.pageSize
|
||||||
|
|
||||||
|
data_list.splice(0, data_list.length)
|
||||||
|
items.forEach((item: Blog) => data_list.push(item))
|
||||||
|
} catch {
|
||||||
|
// 错误由 axios 拦截器处理
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchBtNames = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getBtName()
|
||||||
|
const payload = res.data?.data ?? res.data
|
||||||
|
const items = Array.isArray(payload) ? payload : []
|
||||||
|
btNameList.splice(0, btNameList.length)
|
||||||
|
items.forEach((item: Blog_Type) => btNameList.push(item))
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onSearch = () => {
|
const onSearch = () => {
|
||||||
getBlogs(form).then(response => {
|
pagination.page = 1
|
||||||
data_list.splice(0, data_list.length);
|
fetchBlogs()
|
||||||
response.data.forEach((item: Blog) => {
|
}
|
||||||
data_list.push(item)
|
|
||||||
})
|
const onReset = () => {
|
||||||
})
|
form.key_word = ''
|
||||||
|
form.btId = ''
|
||||||
|
form.start_day = ''
|
||||||
|
form.end_day = ''
|
||||||
|
dateRange.value = null
|
||||||
|
pagination.page = 1
|
||||||
|
fetchBlogs()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPageChange = (p: number) => {
|
||||||
|
pagination.page = p
|
||||||
|
fetchBlogs()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPageSizeChange = (s: number) => {
|
||||||
|
pagination.pageSize = s
|
||||||
|
pagination.page = 1
|
||||||
|
fetchBlogs()
|
||||||
}
|
}
|
||||||
|
|
||||||
const openBlog = (bid: string) => {
|
const openBlog = (bid: string) => {
|
||||||
const { href } = router.resolve({
|
const { href } = router.resolve({ path: '/view', query: { bid } })
|
||||||
path: '/view',
|
window.open(href, '_blank')
|
||||||
query: {
|
|
||||||
bid: bid
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.open(href, '_blank');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const openBlogEditer = () => {
|
const openBlogEditer = () => {
|
||||||
const { href } = router.resolve({
|
const { href } = router.resolve({ path: '/write/' })
|
||||||
path: '/write/'
|
window.open(href, '_blank')
|
||||||
});
|
|
||||||
window.open(href, '_blank');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishbyBids = () => {
|
const publishbyBid = async (bid: string) => {
|
||||||
publishAll()
|
await publish(bid)
|
||||||
|
ElMessage.success('发布指令已发送')
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishProfile = () => {
|
const handleBatchCommand = async (cmd: string) => {
|
||||||
publish_profile()
|
if (cmd === 'publishAll') {
|
||||||
}
|
await publishAll()
|
||||||
|
ElMessage.success('批量发布已触发')
|
||||||
const publishbyBid = (bid: string) => {
|
} else if (cmd === 'publishProfile') {
|
||||||
publish(bid)
|
await publish_profile()
|
||||||
|
ElMessage.success('profile 重新发布已触发')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AUTO INVOKE FUNCTION
|
* AUTO INVOKE FUNCTION
|
||||||
*/
|
*/
|
||||||
(() => {
|
onMounted(() => {
|
||||||
|
fetchBtNames()
|
||||||
getBlogs(form).then(response => {
|
fetchBlogs()
|
||||||
response.data.forEach((item: Blog) => {
|
})
|
||||||
data_list.push(item)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
getBtName().then(response => {
|
|
||||||
btNameList.push({ btId: "", btName: "选择分类" })
|
|
||||||
response.data.forEach((item: any) => {
|
|
||||||
const bts = {
|
|
||||||
btId: item.btId,
|
|
||||||
btName: item.btName
|
|
||||||
}
|
|
||||||
btNameList.push(bts)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
})()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style lang="scss" scoped>
|
||||||
.blog_list {
|
.blog-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
justify-content: space-between;
|
||||||
border-bottom: 1px solid rgba(151, 151, 151, 0.3);
|
align-items: center;
|
||||||
padding-top: 10px;
|
margin-bottom: 16px;
|
||||||
padding-bottom: 10px;
|
}
|
||||||
|
.blog-header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.blog-icon { font-size: 24px; }
|
||||||
|
.blog-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
.blog-header-right {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page_style {
|
.filter-card {
|
||||||
margin-top: 25px;
|
margin-bottom: 16px;
|
||||||
|
background: #fafbfc;
|
||||||
|
}
|
||||||
|
.filter-form {
|
||||||
|
margin-bottom: -16px; /* 抵消 el-form-item 底部 margin */
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 40px 0;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
padding: 18px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
flex: 1;
|
||||||
|
/* 单行省略 */
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
.blog-card:hover .blog-card-title { color: #409eff; }
|
||||||
|
|
||||||
|
.blog-card-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-preview {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #606266;
|
||||||
|
/* 两行省略 */
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-card-meta {
|
||||||
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.meta-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrap {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 8px 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
+106
-30
@@ -21,13 +21,37 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-calendar>
|
</el-calendar>
|
||||||
|
|
||||||
<span>待办事项</span>
|
<div class="toolbar">
|
||||||
<el-table :data="eventDataList">
|
<span class="toolbar-label">待办事项</span>
|
||||||
|
<el-select v-model="pageSize" size="small" style="width: 110px">
|
||||||
|
<el-option label="10 条/页" :value="10" />
|
||||||
|
<el-option label="20 条/页" :value="20" />
|
||||||
|
<el-option label="50 条/页" :value="50" />
|
||||||
|
<el-option label="100 条/页" :value="100" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="eventDataList" v-loading="isComponentLoading">
|
||||||
<el-table-column property="eventDate" label="Date" width="150" />
|
<el-table-column property="eventDate" label="Date" width="150" />
|
||||||
<el-table-column property="title" label="title" width="200" />
|
<el-table-column property="title" label="title" width="200" />
|
||||||
<el-table-column property="eventInfo" label="eventInfo" />
|
<el-table-column property="eventInfo" label="eventInfo" />
|
||||||
|
<el-table-column label="操作" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button size="small" type="danger" @click="onDelete(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
|
<el-pagination
|
||||||
|
class="pagination"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, total"
|
||||||
|
:total="pagination.total"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:current-page="pagination.page"
|
||||||
|
@current-change="onPageChange"
|
||||||
|
/>
|
||||||
|
|
||||||
<el-dialog v-model="dialogFormVisible" title="待办事项">
|
<el-dialog v-model="dialogFormVisible" title="待办事项">
|
||||||
<el-form :model="form">
|
<el-form :model="form">
|
||||||
<el-form-item label="日期" :label-width="formLabelWidth">
|
<el-form-item label="日期" :label-width="formLabelWidth">
|
||||||
@@ -59,14 +83,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { nextTick, onMounted, reactive, ref } from 'vue'
|
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { get_holiday } from '@/service/calendar'
|
import { get_holiday } from '@/service/calendar'
|
||||||
import { toLocalDate } from '@/utils/date'
|
import { toLocalDate } from '@/utils/date'
|
||||||
import { get_event, post_event, put_event, delete_event } from '@/api/calendar'
|
import { get_event, post_event, delete_event } from '@/api/calendar'
|
||||||
import { Event } from '@/entity/index'
|
import { Event } from '@/entity/index'
|
||||||
import { settingsStore } from '@/store/modules/settings'
|
import { settingsStore } from '@/store/modules/settings'
|
||||||
|
|
||||||
|
|
||||||
// -- IMPORT --
|
// -- IMPORT --
|
||||||
const useSettingsStore = settingsStore()
|
const useSettingsStore = settingsStore()
|
||||||
|
|
||||||
@@ -91,6 +115,16 @@ const isComponentLoading = ref(true)
|
|||||||
const checkedalerts = ref([])
|
const checkedalerts = ref([])
|
||||||
const alerts = ['email', 'messageBox', '手机短信', '微信通知']
|
const alerts = ['email', 'messageBox', '手机短信', '微信通知']
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const pagination = reactive({ page: 1, total: 0 })
|
||||||
|
|
||||||
|
// 当前选中的月份(yyyy-MM)
|
||||||
|
const monthStr = computed(() => {
|
||||||
|
const d = value.value
|
||||||
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`
|
||||||
|
})
|
||||||
|
|
||||||
// -- EVENT DEFINITION
|
// -- EVENT DEFINITION
|
||||||
const onclick = (date: Date) => {
|
const onclick = (date: Date) => {
|
||||||
form.eventDate = toLocalDate(date)
|
form.eventDate = toLocalDate(date)
|
||||||
@@ -115,44 +149,72 @@ const handleCommand = (command: string) => {
|
|||||||
useSettingsStore.settings.isRouterAlive = true
|
useSettingsStore.settings.isRouterAlive = true
|
||||||
useSettingsStore.settings.isLoading = false
|
useSettingsStore.settings.isLoading = false
|
||||||
})
|
})
|
||||||
} else if (command === 'closeOther') {
|
|
||||||
// useSettingsStore.closeOther()
|
|
||||||
} else {
|
|
||||||
// useSettingsStore.closeLeftOrRight(command)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onPageChange = (p: number) => {
|
||||||
|
pagination.page = p
|
||||||
|
reloadEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
const reloadEvents = async () => {
|
||||||
|
isComponentLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await get_event({
|
||||||
|
month: monthStr.value,
|
||||||
|
page: pagination.page,
|
||||||
|
pageSize: pageSize.value
|
||||||
|
})
|
||||||
|
// 后端 ApiResponse 结构:{code, message, data: {items, total, page, pageSize}}
|
||||||
|
const pageResp = res.data?.data ?? res.data
|
||||||
|
const items = Array.isArray(pageResp) ? pageResp : (pageResp?.items ?? [])
|
||||||
|
pagination.total = pageResp?.total ?? items.length
|
||||||
|
pagination.page = pageResp?.page ?? pagination.page
|
||||||
|
|
||||||
|
eventDataList.length = 0
|
||||||
|
eventDataObject.value = {}
|
||||||
|
items.forEach((record: Event) => {
|
||||||
|
eventDataList.push(record)
|
||||||
|
eventDataObject[record.eventDate] = record.title
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
// 错误已由 axios 拦截器处理
|
||||||
|
} finally {
|
||||||
|
isComponentLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = async (row: Event) => {
|
||||||
|
if (!row.id) return
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(`确认删除 "${row.title}"?`, '提示', { type: 'warning' })
|
||||||
|
} catch {
|
||||||
|
return // 用户取消
|
||||||
|
}
|
||||||
|
await delete_event(row.id)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
reloadEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换月份 / 改变 pageSize → 重新加载
|
||||||
|
watch([monthStr, pageSize], () => {
|
||||||
|
pagination.page = 1
|
||||||
|
reloadEvents()
|
||||||
|
}, { deep: true })
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AUTO INVOKE FUNCTION
|
* AUTO INVOKE FUNCTION
|
||||||
*/
|
*/
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 先显示日历(避免数据加载失败阻塞日历渲染)
|
|
||||||
isComponentActive.value = true
|
isComponentActive.value = true
|
||||||
|
|
||||||
await get_holiday().then((resp) => {
|
await get_holiday().then((resp) => {
|
||||||
// holidays-jp.github.io 直接返回 JSON 对象,不是 ApiResponse 结构
|
|
||||||
Object.keys(resp.data).forEach(element => {
|
Object.keys(resp.data).forEach(element => {
|
||||||
holidays[element] = resp.data[element]
|
holidays[element] = resp.data[element]
|
||||||
})
|
})
|
||||||
}).catch(() => {
|
}).catch(() => { /* 节假日失败不影响 */ })
|
||||||
// 节假日数据失败不影响日历显示
|
|
||||||
})
|
|
||||||
|
|
||||||
await get_event().then((response) => {
|
await reloadEvents()
|
||||||
// 后端 ApiResponse 结构:{code, message, data: [...]}
|
|
||||||
const list = response.data?.data ?? response.data
|
|
||||||
eventDataList.length = 0
|
|
||||||
if (Array.isArray(list)) {
|
|
||||||
list.forEach((record: Event) => {
|
|
||||||
eventDataList.push(record)
|
|
||||||
eventDataObject[record.eventDate] = record.title
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
// 业务异常已经被 axios 拦截器 toast 了
|
|
||||||
})
|
|
||||||
|
|
||||||
isComponentLoading.value = false
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -162,8 +224,22 @@ onMounted(async () => {
|
|||||||
.date-cell-holiday {
|
.date-cell-holiday {
|
||||||
background: #dfd;
|
background: #dfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-cell-event {
|
.date-cell-event {
|
||||||
background: yellow;
|
background: yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin: 16px 0 8px;
|
||||||
|
}
|
||||||
|
.toolbar-label {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.pagination {
|
||||||
|
margin: 16px 0;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-table :data="dirContexts" @cell-mouse-enter="rowMouseEnter" @cell-mouse-leave="rowMouseLeave"
|
|
||||||
:row-style="handleRowStyle" @row-contextmenu="rightClickRow" @row-click="leftClickRow" ref="multipleTableRef">
|
|
||||||
<el-table-column prop="name" width="200px">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="isDir" width="130px">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="size" width="130px">
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="path" width="130px">
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<Contextmenu auto-ajust-placement ref="contextmenu">
|
|
||||||
<ContextmenuItem v-if="isSelect" @click="createFolder">
|
|
||||||
<el-icon class="contextmenu-icon">
|
|
||||||
<FolderOpened />
|
|
||||||
</el-icon>
|
|
||||||
<label> _new folder</label>
|
|
||||||
</ContextmenuItem>
|
|
||||||
<ContextmenuItem v-if="isSelect" @click="createFolder">
|
|
||||||
<el-icon class="contextmenu-icon">
|
|
||||||
<FolderOpened />
|
|
||||||
</el-icon>
|
|
||||||
<label> _copy</label>
|
|
||||||
</ContextmenuItem>
|
|
||||||
<ContextmenuItem v-if="isSelect" @click="createFolder">
|
|
||||||
<el-icon class="contextmenu-icon">
|
|
||||||
<FolderOpened />
|
|
||||||
</el-icon>
|
|
||||||
<label> _rename</label>
|
|
||||||
</ContextmenuItem>
|
|
||||||
<ContextmenuItem v-if="isSelect" @click="createFolder">
|
|
||||||
<el-icon class="contextmenu-icon">
|
|
||||||
<FolderOpened />
|
|
||||||
</el-icon>
|
|
||||||
<label> _delete</label>
|
|
||||||
</ContextmenuItem>
|
|
||||||
<ContextmenuItem v-if="isSelect" @click="createFolder">
|
|
||||||
<el-icon class="contextmenu-icon">
|
|
||||||
<FolderOpened />
|
|
||||||
</el-icon>
|
|
||||||
<label> _delete</label>
|
|
||||||
</ContextmenuItem>
|
|
||||||
|
|
||||||
</Contextmenu>
|
|
||||||
|
|
||||||
<!-- aplayer -->
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { defineAsyncComponent, reactive, toRaw } from 'vue';
|
|
||||||
import { getFileList } from '../../api/cloud'
|
|
||||||
import { Contextmenu, ContextmenuDivider, ContextmenuItem } from "v-contextmenu";
|
|
||||||
import { computed, getCurrentInstance, onBeforeUnmount, onMounted, ref } from "vue";
|
|
||||||
import { ElTable, type TableInstance } from 'element-plus'
|
|
||||||
|
|
||||||
// -- IMPORT --
|
|
||||||
|
|
||||||
|
|
||||||
const currentInstance = getCurrentInstance()
|
|
||||||
|
|
||||||
// -- INTERFACE OR TYPE DEFINITION --
|
|
||||||
interface DirContext {
|
|
||||||
size: number
|
|
||||||
isDir: number
|
|
||||||
name: string
|
|
||||||
path: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface KeyBoard {
|
|
||||||
isCtrl: boolean
|
|
||||||
isShift: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -- REACTIVE OBJECT --
|
|
||||||
const dirContexts = reactive<DirContext[]>([])
|
|
||||||
const multipleSelection = reactive<Set<DirContext>>(new Set())
|
|
||||||
const keyBoard = reactive<KeyBoard>({
|
|
||||||
isCtrl: false,
|
|
||||||
isShift: false
|
|
||||||
})
|
|
||||||
|
|
||||||
// -- REF OBJECT --
|
|
||||||
const multipleTableRef = ref<TableInstance>()
|
|
||||||
const hoverUnSelection = ref<DirContext>()
|
|
||||||
const isSelect = ref(true)
|
|
||||||
const selection = ref()
|
|
||||||
const isRightClick = ref(false)
|
|
||||||
|
|
||||||
|
|
||||||
// -- KEYBOARD AND MOUSE EVENT --
|
|
||||||
const rowMouseEnter = (row: any, column: any, cell: any, event: any) => {
|
|
||||||
if (!multipleSelection.has(row)) {
|
|
||||||
hoverUnSelection.value = row
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rowMouseLeave = (row: any, column: any, cell: any, event: any) => {
|
|
||||||
if (!multipleSelection.has(row)) {
|
|
||||||
hoverUnSelection.value = undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const leftClickRow = (row: any, column: any, event: any) => {
|
|
||||||
|
|
||||||
// CTRL KEY + LEFT CLICK
|
|
||||||
if (keyBoard.isCtrl) {
|
|
||||||
if (multipleSelection.has(row)) {
|
|
||||||
multipleSelection.delete(row)
|
|
||||||
} else {
|
|
||||||
multipleSelection.add(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ONLY LEFT CLICK
|
|
||||||
} else {
|
|
||||||
multipleSelection.clear()
|
|
||||||
multipleSelection.add(row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rightClickRow = (row: any, column: any, event: any) => {
|
|
||||||
isRightClick.value = true
|
|
||||||
let contextmenuRef: any = currentInstance?.proxy?.$refs.contextmenu
|
|
||||||
if (!multipleSelection.has(row)) {
|
|
||||||
multipleSelection.clear()
|
|
||||||
multipleSelection.add(row)
|
|
||||||
}
|
|
||||||
event.preventDefault()
|
|
||||||
contextmenuRef.show({
|
|
||||||
top: event.clientY,
|
|
||||||
left: event.clientX
|
|
||||||
})
|
|
||||||
|
|
||||||
window.onclick = () => {
|
|
||||||
contextmenuRef.hide()
|
|
||||||
isRightClick.value = false
|
|
||||||
// contextMenuTargetBlank.value = false;
|
|
||||||
// contextMenuTargetFile.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const createFolder = () => {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -- STYLE --
|
|
||||||
const handleRowStyle = ({ row, rowIndex }: any) => {
|
|
||||||
if (multipleSelection.has(row)) {
|
|
||||||
return {
|
|
||||||
"background": "#B4C7E7"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toRaw(hoverUnSelection.value)?.name == toRaw(row).name) {
|
|
||||||
return {
|
|
||||||
"background": "#DAE3F3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AUTO INVOKE FUNCTION
|
|
||||||
*/
|
|
||||||
(() => {
|
|
||||||
|
|
||||||
// MOUSE KEYDOWM EVENT
|
|
||||||
document.onkeydown = (event) => {
|
|
||||||
|
|
||||||
switch (event.key) {
|
|
||||||
case 'Control':
|
|
||||||
keyBoard.isCtrl = true
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MOUSE KEYUP EVENT
|
|
||||||
document.onkeyup = (event) => {
|
|
||||||
|
|
||||||
switch (event.key) {
|
|
||||||
case 'Control':
|
|
||||||
keyBoard.isCtrl = false
|
|
||||||
break
|
|
||||||
case 'Escape':
|
|
||||||
if (isRightClick.value) {
|
|
||||||
let contextmenuRef: any = currentInstance?.proxy?.$refs.contextmenu
|
|
||||||
contextmenuRef.hide()
|
|
||||||
isRightClick.value = false
|
|
||||||
} else {
|
|
||||||
multipleSelection.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
keyBoard.isCtrl = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// INITIALIZE DATA QUERY
|
|
||||||
getFileList("").then(response => {
|
|
||||||
|
|
||||||
response.data.forEach((element: any) => {
|
|
||||||
const dirContext: DirContext = {
|
|
||||||
size: element.size,
|
|
||||||
isDir: element.isDir,
|
|
||||||
name: element.server_filename,
|
|
||||||
path: element.path
|
|
||||||
}
|
|
||||||
dirContexts.push(dirContext)
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
})()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.el-table--enable-row-hover .el-table__body tr:hover>td {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -83,10 +83,6 @@ export default defineConfig(({ mode, command }) => {
|
|||||||
target: 'http://localhost:8088',
|
target: 'http://localhost:8088',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
'^/cloud/api/.*': {
|
|
||||||
target: 'http://localhost:8089',
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
|
||||||
'^/wiki/api/.*': {
|
'^/wiki/api/.*': {
|
||||||
target: 'http://localhost:8090',
|
target: 'http://localhost:8090',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user