更新v1.1.0
Squashed commit of the following: commit 4443f7c8251b31687ed93114930ab3d769f4ed6c Author: Sun <95302870@qq.com> Date: Tue Nov 28 22:10:49 2023 +0800 美化关于页 commit 95ca46d460eba469ca8ae54f65c7773835061c0f Author: Sun <95302870@qq.com> Date: Tue Nov 28 21:59:48 2023 +0800 更新版本号,更新说明文件增加新版预览截图 commit 052e5f81fe4065e10199d52bc041329fc9c5fe86 Author: Sun <95302870@qq.com> Date: Tue Nov 28 21:06:52 2023 +0800 修复后端mkdirAll权限的问题 commit ace57d5ba69c311e40997d5791cf03a8b28e0c07 Author: Sun <95302870@qq.com> Date: Tue Nov 28 20:59:35 2023 +0800 修改配置文件 commit 099015f2767cedfd6eae91e60131817471eb1f24 Author: Sun <95302870@qq.com> Date: Tue Nov 28 14:10:39 2023 +0800 增加docker-compose文件 commit e229003431ff2476f0ab63a8dffb88504716ba48 Author: Sun <95302870@qq.com> Date: Tue Nov 28 13:53:20 2023 +0800 提交更新日志文件 commit e8736b8b62db6d590c063b42757599381429541e Author: Sun <95302870@qq.com> Date: Tue Nov 28 13:49:38 2023 +0800 增加隐藏小图标 commit 038af3aaa91a023cc10aabff5b0cfd15c64d0b46 Author: Sun <95302870@qq.com> Date: Tue Nov 28 13:49:04 2023 +0800 优化 密码限制 commit 4cd15a383923bf3de56e9e4dc6df3bf97236ed18 Author: Sun <95302870@qq.com> Date: Tue Nov 28 12:34:59 2023 +0800 增加反馈入口 commit daf6aea902893f816dca5c0bb09326f2f110ddcc Merge: 3edfaddb057e25Author: Sun <95302870@qq.com> Date: Mon Nov 27 22:19:59 2023 +0800 Merge branch 'master' into dev commit 3edfaddd173efcbbde867dc9ce9b022920b39061 Author: Sun <95302870@qq.com> Date: Mon Nov 27 22:17:08 2023 +0800 修改docker的编译镜像和运行镜像为alpine,兼容极空间设备 commit 3445f97152c2f6b9f1f9f68b46e4b84f8240c9c2 Author: Sun <95302870@qq.com> Date: Mon Nov 27 13:58:59 2023 +0800 修复前端编译错误 commit 3ef02013ffb595e7805692350389bb623155cfe9 Author: Sun <95302870@qq.com> Date: Mon Nov 27 13:56:12 2023 +0800 更新beta版本号 commit 620f0f1e1523f34e87001e8a2bbe3d4a01cb0b9b Author: Sun <95302870@qq.com> Date: Mon Nov 27 13:53:46 2023 +0800 修复 添加图标成功后遗留旧数据的问题 commit 55d877d1ca11e83d9f7325a321aeb5b65ad4ee8b Author: Sun <95302870@qq.com> Date: Mon Nov 27 13:41:55 2023 +0800 增加置顶按钮 commit f28dd63328aeca5d8c3c036c5786304f8a33b1f9 Author: Sun <95302870@qq.com> Date: Mon Nov 27 12:56:14 2023 +0800 优化roundmodal的样式和手机端设置样式 commit c19ce176878ea2ef06b0a5dc75bd6d8239892302 Author: Sun <95302870@qq.com> Date: Mon Nov 27 11:06:44 2023 +0800 优化手机端logo文字显示问题 commit 018dabb2faddc0541fb33ef5a51a126575a59cf5 Author: Sun <95302870@qq.com> Date: Mon Nov 27 10:51:02 2023 +0800 更新说明文件 commit 02239e3686933e4c33e430be8300ec2d0be41887 Author: Sun <95302870@qq.com> Date: Sun Nov 26 22:59:11 2023 +0800 优化 登录页面 commit 6aa92e8ba6c4eeb2fa2995ee93aeeac86b54b551 Author: Sun <95302870@qq.com> Date: Sat Nov 25 23:59:40 2023 +0800 增加编译脚本 commit d93df810fa95a7baa28ca5323903b93f286ba741 Author: Sun <95302870@qq.com> Date: Sat Nov 25 15:48:00 2023 +0800 修改相关logo图片 commit 036a56ddc7a555d6227c92dfa2abfe84f9042662 Merge: 7018872feacc89Author: Sun <95302870@qq.com> Date: Fri Nov 24 16:00:23 2023 +0800 Merge branch 'master' into dev commit 7018872ce9fd0fa8f1ff4731a16b2ea90fb9153f Author: Sun <95302870@qq.com> Date: Fri Nov 24 15:31:31 2023 +0800 更新版本标签 commit 4fae97dd932ce4638d869a0c7a123c788c3e3e43 Author: Sun <95302870@qq.com> Date: Fri Nov 24 15:07:39 2023 +0800 更新版本1.1.0 测试版 commit 890a3c3dbdccbe4dfd5a6915e87a2649c9141e7b Author: Sun <95302870@qq.com> Date: Fri Nov 24 14:31:26 2023 +0800 右键菜单新增打开局域网或者互联网地址,优化分组管理图标不统一的问题 commit 4f014cf4aa384a2c8a03585ffd6ce41c941b9356 Author: Sun <95302870@qq.com> Date: Fri Nov 24 13:33:43 2023 +0800 增加 关联删除,优化添加密码长度限制20 commit 5658e6c379b077d359fff75c5e9b904cbce5f81e Author: Sun <95302870@qq.com> Date: Fri Nov 24 12:09:41 2023 +0800 增加更新日志 commit f142d1b378e0525db157a93cca61ee86bf1eb08d Author: Sun <95302870@qq.com> Date: Fri Nov 24 12:09:30 2023 +0800 添加应用图标验证分组信息必填 commit 2ff2b6b32a4bb70653e3a7312ccb0f4b0b945f07 Author: Sun <95302870@qq.com> Date: Thu Nov 23 23:45:10 2023 +0800 优化关于页面,及更新版本序号为2 commit c9b482b24e2d23d638501dbaa44f826386c420b5 Author: Sun <95302870@qq.com> Date: Thu Nov 23 22:12:13 2023 +0800 优化关于设置版本号 commit ed70059ffbce1ae8a9e2e0378803f7875ada342b Author: Sun <95302870@qq.com> Date: Thu Nov 23 21:41:22 2023 +0800 修复分组管理不能滚动的问题 commit faa4222b1494271878c2c7469c14a4efa49c6761 Author: Sun <95302870@qq.com> Date: Thu Nov 23 21:24:34 2023 +0800 修复分组写死的问题 commit 4f2d0c858e55735b9ba3a8453de9407b76346805 Author: Sun <95302870@qq.com> Date: Thu Nov 23 20:24:39 2023 +0800 初步尝试构建测试版本 commit 596bed19dcf3bceb77e30f9c24218888f8da7e64 Author: Sun <95302870@qq.com> Date: Thu Nov 23 19:24:05 2023 +0800 修复搜索框配置bug,云端没有默认值,前端打不开搜索引擎选择栏 commit 489fbf748a7e35c6b69198b19038c01a548e20f2 Author: Sun <95302870@qq.com> Date: Thu Nov 23 19:22:57 2023 +0800 增加logo和版本打印,修复模块配置的索引报错 commit 263dab607af8a830acee44e37776bda4da814b40 Author: Sun <95302870@qq.com> Date: Wed Nov 22 23:02:37 2023 +0800 调整排序样式 commit c0adf335d3e48e6770d56eb506b471852fbfbc43 Author: Sun <95302870@qq.com> Date: Wed Nov 22 22:58:18 2023 +0800 说明文件增加logo commit 721d22e75b93d3646f5f206d208a9a743840a25b Author: Sun <95302870@qq.com> Date: Wed Nov 22 22:40:40 2023 +0800 更换logo commit 4df58fec7b2054ce97cf2989045affd144aa4f8b Author: Sun <95302870@qq.com> Date: Wed Nov 22 22:38:27 2023 +0800 完善关于页面 commit 63777f0bbac85550fafe1b084bd664c7722ab934 Author: Sun <95302870@qq.com> Date: Wed Nov 22 21:12:14 2023 +0800 字体为纯白色的时候,详情图标会根据背景的明暗度计算字体颜色 commit f328dc73305665a921e030dd4a06d759d0cac3bf Author: Sun <95302870@qq.com> Date: Wed Nov 22 17:21:40 2023 +0800 详情图标居中 commit 663f37bf1a26b7dff24edcfb149222ac780cb90d Author: Sun <95302870@qq.com> Date: Wed Nov 22 16:45:11 2023 +0800 将图标单独拆分为子组件应用图标 commit 30cd5ab460e032f7f6d7c23eb7a9c7af735d0f41 Author: Sun <95302870@qq.com> Date: Wed Nov 22 13:36:32 2023 +0800 增加详情图标隐藏描述信息等设置 commit 945a94e76cae4251953512cc09f348ad38bb9a38 Author: Sun <95302870@qq.com> Date: Wed Nov 22 12:28:46 2023 +0800 优化图标背景色:支持透明图标并更换背景颜色字段 commit 437053fc9d8d9e3c55aac4d259e4b4c4bc11de58 Author: Sun <95302870@qq.com> Date: Wed Nov 22 11:20:56 2023 +0800 完善搜索框 commit a9914f8e8ced23b8c50701a85d522c8f0fcd1c2b Author: Sun <95302870@qq.com> Date: Wed Nov 22 01:27:16 2023 +0800 关闭模块配置相关接口开发模式 commit 2a9e22d4b781f43c0f9b8a867d26c295e756175b Author: Sun <95302870@qq.com> Date: Wed Nov 22 01:24:09 2023 +0800 完成搜索框的样式和模块配置的state等api对接 commit 7f771650ef7272e474f74ed689ab844bf90b946f Author: Sun <95302870@qq.com> Date: Wed Nov 22 00:45:25 2023 +0800 增加搜索引擎图标 commit a0e0039ae89eaa27e4b849baa0168716866682ea Author: Sun <95302870@qq.com> Date: Tue Nov 21 19:54:36 2023 +0800 增加 模块配置表 commit 017869794177d7a5d4c12c0eac6fb7c7fe79734e Author: Sun <95302870@qq.com> Date: Tue Nov 21 13:10:39 2023 +0800 图标标题加粗 commit 7a2d896a44262b54d6a1d2d12fe6bbbc31b1ca49 Author: Sun <95302870@qq.com> Date: Tue Nov 21 12:53:39 2023 +0800 增加图标的预设颜色 commit a6c3120c186646b323e57ecce0f85ec9c79a41a5 Author: Sun <95302870@qq.com> Date: Tue Nov 21 12:18:46 2023 +0800 增加遮罩 commit 84d3db81ea2aaa0f67a67690e449d15401e8e511 Author: Sun <95302870@qq.com> Date: Tue Nov 21 11:05:24 2023 +0800 极简小图标增加鼠标悬浮详情 commit 666a6a117bc30c64a78ab0fe2cb7836c602b2741 Author: Sun <95302870@qq.com> Date: Mon Nov 20 23:33:10 2023 +0800 修复 sort字段未修改归0的问题 commit 71afd530d7a740763326a6117f8e7c04ac1f7f69 Author: Sun <95302870@qq.com> Date: Mon Nov 20 23:32:48 2023 +0800 适配纯透明图标,增强图标背景色,增加图标url连接支持 commit 619c5e28e1c51c16e14ed09601709ab751ee2454 Author: Sun <95302870@qq.com> Date: Mon Nov 20 22:37:43 2023 +0800 修复 每次修改图标都重置了排序号 commit 8a17f1c0bf2f00c530dee61cd5070a74ca4a53b2 Author: Sun <95302870@qq.com> Date: Mon Nov 20 21:21:20 2023 +0800 分组为空的时候显示添加图标的图标 commit 755cf3dc569e402cb3dfc9915e94de4f2595571b Author: Sun <95302870@qq.com> Date: Mon Nov 20 20:52:45 2023 +0800 首页图标排序完成 commit 5ccf23c68b3284be00dadf94073a665826737a77 Author: Sun <95302870@qq.com> Date: Mon Nov 20 14:30:12 2023 +0800 添加修改图标适配分组 commit 47209d729270bf4704428ecd91606d4721bd9a13 Author: Sun <95302870@qq.com> Date: Mon Nov 20 11:06:32 2023 +0800 保存分组和分组排序已经完成 commit 17403de7ed236a097d70cd5ecbbe261e620ff377 Merge: d0d88eb980d81aAuthor: Sun <95302870@qq.com> Date: Sun Nov 19 23:38:00 2023 +0800 Merge branch 'master' into dev commit d0d88eb548bbe9d7f5ad663f383db858843a8d8c Merge: 728dbc8 47b479c Author: Sun <95302870@qq.com> Date: Sun Nov 19 11:13:34 2023 +0800 Merge branch 'docker-build' into dev commit 47b479cf8da7214dd9e0592b461743ab7d3824ed Author: Sun <95302870@qq.com> Date: Sun Nov 19 11:12:54 2023 +0800 修改前端程序名 commit 728dbc80ff7885d0b4cf289b06763cc60ed17d7e Author: Sun <95302870@qq.com> Date: Thu Nov 16 13:44:47 2023 +0800 新增删除应用分组和修改应用分组,以及图标真正的按组读取 commit a3dbd948ca743384a2de3685083695603d674bf1 Author: Sun <95302870@qq.com> Date: Wed Nov 15 22:49:58 2023 +0800 增加图标组api commit de21f3f232c1243917b5c55ba4bedb01437f8564 Author: Sun <95302870@qq.com> Date: Wed Nov 15 22:49:44 2023 +0800 重新划分应用盒子的结构 commit 7c409112ba1f8eefb7df7fffdb78b285e3f5322c Author: Sun <95302870@qq.com> Date: Wed Nov 15 22:27:07 2023 +0800 [后端] 增加应用分组 commit ebf9500529c7db30b1c6e1ed4056013d0f83827a Merge: acedcb3 97d4f83 Author: Sun <95302870@qq.com> Date: Wed Nov 15 20:38:05 2023 +0800 Merge branch 'feature/drag' into dev commit acedcb32a03ed0ee1833143912a9215182da3fb6 Merge: f105e10c84eae3Author: Sun <95302870@qq.com> Date: Wed Nov 15 20:37:26 2023 +0800 Merge branch 'master' into dev commit 97d4f8368dffca2a16d729e666068a552feca87d Author: Sun <95302870@qq.com> Date: Wed Nov 15 20:36:19 2023 +0800 更新软件包 commit 5108f65275181b899b8fc100c615cb6065dcca5d Author: Sun <95302870@qq.com> Date: Wed Nov 15 20:30:39 2023 +0800 简单监听了一下拖拽 commit dae9aea41f1540ccb74abea2a31af5d2a1e4dcfd Merge: 396db51f672034Author: Sun <95302870@qq.com> Date: Wed Nov 15 10:01:00 2023 +0800 Merge branch 'master' into feature/drag commit 396db51979d513559512b0a9702dd0d616c2872b Author: Sun <95302870@qq.com> Date: Wed Nov 15 00:08:02 2023 +0800 历史性时刻,拖拽图标 commit f105e10fe1ced11d0b32eba37cfbfdb94f6ad07b Author: Sun <95302870@qq.com> Date: Tue Nov 14 11:35:52 2023 +0800 尝试增加一个分组标题 commit 7e2354f4ed509c7d05667604b7eb56e91f911ed0 Author: Sun <95302870@qq.com> Date: Sun Nov 12 23:07:37 2023 +0800 优化 枚举引用错误 commit 27e85b7da339706ea97604a785bf013dad5f9534 Author: Sun <95302870@qq.com> Date: Sun Nov 12 23:06:50 2023 +0800 优化路由 commit fef462804c0d445f5b9bc7e38e226b55c26017ee Author: Sun <95302870@qq.com> Date: Sun Nov 12 21:28:57 2023 +0800 更换enums的位置
This commit is contained in:
@@ -1,33 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
import RecursiveList from './RecursiveList.vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { NColorPicker } from 'naive-ui'
|
||||
|
||||
const multiLevelData = [
|
||||
{
|
||||
name: '开发笔记',
|
||||
children: [
|
||||
{
|
||||
name: 'Level 2 Item 1',
|
||||
extand: true,
|
||||
children: [
|
||||
{ name: 'SAI-Chat开发' },
|
||||
{ name: '笔记本项目' },
|
||||
],
|
||||
},
|
||||
{ name: '测试项目' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '学习笔记',
|
||||
children: [
|
||||
{ name: 'Blender' },
|
||||
{ name: 'mongo' },
|
||||
],
|
||||
},
|
||||
]
|
||||
// 获取背景颜色的 RGB 值(假设这里获取到了背景颜色的值)
|
||||
const bgColor = ref('#000000') // 假设为蓝色
|
||||
|
||||
// 计算颜色的明暗度
|
||||
const calculateLuminance = (color: string) => {
|
||||
const hex = color.replace(/^#/, '')
|
||||
const r = parseInt(hex.substring(0, 2), 16)
|
||||
const g = parseInt(hex.substring(2, 4), 16)
|
||||
const b = parseInt(hex.substring(4, 6), 16)
|
||||
return (0.299 * r + 0.587 * g + 0.114 * b) / 255
|
||||
}
|
||||
|
||||
// 根据明暗度切换文本颜色
|
||||
const textColor = computed(() => {
|
||||
const luminance = calculateLuminance(bgColor.value)
|
||||
return luminance > 0.5 ? 'black' : 'white'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<RecursiveList :items="multiLevelData" />
|
||||
<div :style="{ backgroundColor: bgColor, color: textColor }">
|
||||
Background Color Example
|
||||
</div>
|
||||
<NColorPicker v-model:value="bgColor" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 样式可以根据实际需求自定义 */
|
||||
div {
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
<script setup lang='ts'>
|
||||
import { NLayout, NLayoutContent, NLayoutSider } from 'naive-ui'
|
||||
import { computed } from 'vue'
|
||||
// import { Header } from './components'
|
||||
// import { LeftSider, RightSider } from './layout'
|
||||
import Index from './index.vue'
|
||||
import { usePanelState } from '@/store/modules'
|
||||
|
||||
const panelState = usePanelState()
|
||||
|
||||
const leftSiderCollapsed = computed(() => panelState.leftSiderCollapsed)
|
||||
const rightSiderCollapsed = computed(() => panelState.rightSiderCollapsed)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<NLayout has-sider class="h-full">
|
||||
<NLayoutSider
|
||||
v-model:collapsed="leftSiderCollapsed"
|
||||
collapse-mode="transform"
|
||||
:collapsed-width="0"
|
||||
:width="240"
|
||||
bordered
|
||||
>
|
||||
<LeftSider />
|
||||
</NLayoutSider>
|
||||
<NLayoutContent>
|
||||
<NLayout has-sider sider-placement="right" class="h-full">
|
||||
<NLayoutContent class="h-full">
|
||||
<!-- 内容 -->
|
||||
<!-- <Header /> -->
|
||||
<div>
|
||||
<Index />
|
||||
</div>
|
||||
</NLayoutContent>
|
||||
<NLayoutSider
|
||||
v-model:collapsed="rightSiderCollapsed"
|
||||
collapse-mode="transform"
|
||||
:collapsed-width="0"
|
||||
:width="280"
|
||||
content-style="padding: 20px;"
|
||||
bordered
|
||||
>
|
||||
<RightSider />
|
||||
</NLayoutSider>
|
||||
</NLayout>
|
||||
</NLayoutContent>
|
||||
</NLayout>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,87 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { NEllipsis } from 'naive-ui'
|
||||
import { ItemIcon } from '@/components/common'
|
||||
import { PanelPanelConfigStyleEnum } from '@/enums'
|
||||
|
||||
interface Prop {
|
||||
itemInfo?: Panel.ItemInfo
|
||||
size?: number // 默认70
|
||||
forceBackground?: string // 强制背景色
|
||||
iconTextColor?: string
|
||||
iconTextInfoHideDescription: boolean
|
||||
iconTextIconHideTitle: boolean
|
||||
style: PanelPanelConfigStyleEnum
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Prop>(), {
|
||||
size: 70,
|
||||
})
|
||||
|
||||
const defaultBackground = '#2a2a2a6b'
|
||||
|
||||
const calculateLuminance = (color: string) => {
|
||||
const hex = color.replace(/^#/, '')
|
||||
const r = parseInt(hex.substring(0, 2), 16)
|
||||
const g = parseInt(hex.substring(2, 4), 16)
|
||||
const b = parseInt(hex.substring(4, 6), 16)
|
||||
return (0.299 * r + 0.587 * g + 0.114 * b) / 255
|
||||
}
|
||||
|
||||
const textColor = computed(() => {
|
||||
const luminance = calculateLuminance(props.itemInfo?.icon?.backgroundColor || defaultBackground)
|
||||
return luminance > 0.5 ? 'black' : 'white'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<!-- 详情图标 -->
|
||||
<div
|
||||
v-if="style === PanelPanelConfigStyleEnum.info"
|
||||
class="w-full rounded-2xl transition-all duration-200 hover:shadow-[0_0_20px_10px_rgba(0,0,0,0.2)] flex"
|
||||
:style="{ background: itemInfo?.icon?.backgroundColor || defaultBackground }"
|
||||
>
|
||||
<!-- 图标 -->
|
||||
<div class="w-[70px] h-[70px]">
|
||||
<div class="w-[70px] h-full flex items-center justify-center ">
|
||||
<ItemIcon :item-icon="itemInfo?.icon" force-background="transparent" :size="50" class="overflow-hidden rounded-xl" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文字 -->
|
||||
<!-- 如果为纯白色,将自动根据背景的明暗计算字体的黑白色 -->
|
||||
<div class="text-white flex items-center" :style="{ color: (iconTextColor === '#ffffff') ? textColor : iconTextColor, maxWidth: 'calc(100% - 80px)' }">
|
||||
<div class="w-full">
|
||||
<div class="font-semibold w-full">
|
||||
<NEllipsis>
|
||||
{{ itemInfo?.title }}
|
||||
</NEllipsis>
|
||||
</div>
|
||||
<div v-if="!iconTextInfoHideDescription">
|
||||
<NEllipsis :line-clamp="2" class="text-xs">
|
||||
{{ itemInfo?.description }}
|
||||
</NEllipsis>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 极简图标(APP) -->
|
||||
<div v-if="style === PanelPanelConfigStyleEnum.icon">
|
||||
<div
|
||||
class="overflow-hidden rounded-2xl sunpanel w-[70px] h-[70px] mx-auto rounded-2xl transition-all duration-200 hover:shadow-[0_0_20px_10px_rgba(0,0,0,0.2)]"
|
||||
:title="itemInfo?.description"
|
||||
>
|
||||
<ItemIcon :item-icon="itemInfo?.icon" />
|
||||
</div>
|
||||
<div
|
||||
v-if="!iconTextIconHideTitle"
|
||||
class="text-center app-icon-text-shadow cursor-pointer mt-[2px]"
|
||||
:style="{ color: iconTextColor }"
|
||||
>
|
||||
<span>{{ itemInfo?.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { NButton, NColorPicker, NInput, NRadio, NUpload, useMessage } from 'naive-ui'
|
||||
import type { UploadFileInfo } from 'naive-ui'
|
||||
import { defineProps, ref } from 'vue'
|
||||
import { defineProps, ref, watch } from 'vue'
|
||||
import { ItemIcon } from '@/components/common'
|
||||
import { useAuthStore } from '@/store'
|
||||
|
||||
@@ -17,19 +17,27 @@ const checkedValueRef = ref<number | null>(props.itemIcon?.itemType || 1)
|
||||
|
||||
// 默认图标背景色
|
||||
const defautSwatchesBackground = [
|
||||
'#000',
|
||||
'#00000000',
|
||||
'#000000',
|
||||
'#ffffff',
|
||||
'#18A058',
|
||||
'#2080F0',
|
||||
'#F0A020',
|
||||
'rgba(208, 48, 80, 1)',
|
||||
'#C418D1FF',
|
||||
]
|
||||
|
||||
const initData: Panel.ItemIcon = {
|
||||
itemType: 1,
|
||||
bgColor: '#000',
|
||||
backgroundColor: '#2a2a2a6b',
|
||||
}
|
||||
|
||||
const itemIconInfo = ref<Panel.ItemIcon>(props.itemIcon ? { ...props.itemIcon } : { ...initData })
|
||||
// const itemIconInfo = ref<Panel.ItemIcon>(props.itemIcon ?? { ...initData })
|
||||
const itemIconInfo = ref<Panel.ItemIcon>({
|
||||
...initData,
|
||||
...props.itemIcon,
|
||||
backgroundColor: props.itemIcon?.backgroundColor || initData.backgroundColor,
|
||||
})
|
||||
|
||||
function handleIconTypeRadioChange(type: number) {
|
||||
checkedValueRef.value = type
|
||||
@@ -38,7 +46,6 @@ function handleIconTypeRadioChange(type: number) {
|
||||
}
|
||||
|
||||
function handleChange() {
|
||||
// console.log('值', itemIconInfo.value)
|
||||
emit('update:itemIcon', itemIconInfo.value || null)
|
||||
}
|
||||
|
||||
@@ -61,6 +68,10 @@ const handleUploadFinish = ({
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
watch(itemIconInfo.value, () => {
|
||||
handleChange()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -94,19 +105,57 @@ const handleUploadFinish = ({
|
||||
</NRadio>
|
||||
</div>
|
||||
|
||||
<div class="flex h-[100px]">
|
||||
<div>
|
||||
<div class="border rounded-2xl bg-slate-200">
|
||||
<ItemIcon :item-icon="itemIconInfo" />
|
||||
<div class=" h-[100px]">
|
||||
<div class="flex">
|
||||
<div>
|
||||
<div class="border rounded-2xl bg-slate-200 overflow-hidden rounded-2xl transparent-grid">
|
||||
<ItemIcon :item-icon="itemIconInfo" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 文字 -->
|
||||
<div class="ml-[20px]">
|
||||
<!-- <NImage :src="model.icon" preview-disabled /> -->
|
||||
<div v-if="checkedValueRef === 1">
|
||||
<NInput v-model:value="itemIconInfo.text" class="mb-[5px]" size="small" type="text" placeholder="请输入文字作为图标" @input="handleChange" />
|
||||
</div>
|
||||
|
||||
<div v-if="checkedValueRef === 3">
|
||||
<div>
|
||||
<NInput v-model:value="itemIconInfo.text" class="mb-[5px]" size="small" type="text" placeholder="请输入图标名字" @input="handleChange" />
|
||||
|
||||
<NButton quaternary type="info">
|
||||
<a target="_blank" href="https://icon-sets.iconify.design/">图标库</a>
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图片 -->
|
||||
<div v-if="checkedValueRef === 2">
|
||||
<NInput v-model:value="itemIconInfo.src" class="mb-[5px] w-full" size="small" type="text" placeholder="输入图标地址或上传" @input="handleChange" />
|
||||
<NUpload
|
||||
action="/api/file/uploadImg"
|
||||
:show-file-list="false"
|
||||
name="imgfile"
|
||||
:headers="{
|
||||
token: authStore.token as string,
|
||||
}"
|
||||
@finish="handleUploadFinish"
|
||||
>
|
||||
<NButton size="small">
|
||||
点击上传
|
||||
</NButton>
|
||||
</NUpload>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 文字 -->
|
||||
<div class="ml-[20px]">
|
||||
<!-- <NImage :src="model.icon" preview-disabled /> -->
|
||||
<div v-if="checkedValueRef === 1">
|
||||
<NInput v-model:value="itemIconInfo.text" class="mb-[5px]" size="small" type="text" placeholder="请输入文字作为图标" @input="handleChange" />
|
||||
|
||||
<div class="flex items-center mt-[10px]">
|
||||
<div class="w-auto text-slate-500 mr-[10px]">
|
||||
背景色:
|
||||
</div>
|
||||
<div class="w-[150px] flex items-center mr-[10px]">
|
||||
<NColorPicker
|
||||
v-model:value="itemIconInfo.bgColor"
|
||||
v-model:value="itemIconInfo.backgroundColor"
|
||||
size="small"
|
||||
:modes="['hex']"
|
||||
:swatches="defautSwatchesBackground"
|
||||
@@ -114,39 +163,21 @@ const handleUploadFinish = ({
|
||||
@update-value="handleChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="checkedValueRef === 3">
|
||||
<div>
|
||||
<NInput v-model:value="itemIconInfo.text" class="mb-[5px]" size="small" type="text" placeholder="请输入图标名字" @input="handleChange" />
|
||||
<a target="_blank" href="https://icon-sets.iconify.design/" class="text-[blue]">图标库</a>
|
||||
</div>
|
||||
<NColorPicker
|
||||
v-model:value="itemIconInfo.bgColor"
|
||||
size="small"
|
||||
:modes="['hex']"
|
||||
:swatches="defautSwatchesBackground"
|
||||
@complete="handleChange"
|
||||
@update-value="handleChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 图片 -->
|
||||
<div v-if="checkedValueRef === 2">
|
||||
<NUpload
|
||||
action="/api/file/uploadImg"
|
||||
:show-file-list="false"
|
||||
name="imgfile"
|
||||
:headers="{
|
||||
token: authStore.token as string,
|
||||
}"
|
||||
@finish="handleUploadFinish"
|
||||
>
|
||||
<NButton size="small">
|
||||
点击上传
|
||||
</NButton>
|
||||
</NUpload>
|
||||
<div v-if="itemIconInfo.backgroundColor !== initData.backgroundColor" class="w-auto text-slate-500 mr-[10px] cursor-pointer">
|
||||
<NButton quaternary type="info" @click="itemIconInfo.backgroundColor = initData.backgroundColor">
|
||||
恢复默认
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.transparent-grid {
|
||||
background-image: linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%),
|
||||
linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%);
|
||||
background-size: 16px 16px;
|
||||
background-position: 0 0, 8px 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, defineEmits, defineProps, onMounted, ref, watch } from 'vue'
|
||||
import type { FormInst, FormRules } from 'naive-ui'
|
||||
import { NButton, NForm, NFormItem, NInput, NModal, NSelect, useMessage } from 'naive-ui'
|
||||
import { NButton, NForm, NFormItem, NGrid, NGridItem, NInput, NModal, NSelect, useMessage } from 'naive-ui'
|
||||
import IconEditor from './IconEditor.vue'
|
||||
import { edit } from '@/api/panel/itemIcon'
|
||||
import { getList as getGroupList } from '@/api/panel/itemIconGroup'
|
||||
|
||||
interface Props {
|
||||
visible: boolean
|
||||
@@ -13,6 +14,10 @@ interface Props {
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emit>()
|
||||
const ms = useMessage()
|
||||
const itemIconGroupOptions = ref<{
|
||||
label: string
|
||||
value: number
|
||||
}[]>([])
|
||||
|
||||
const restoreDefault: Panel.Info = {
|
||||
icon: null,
|
||||
@@ -29,6 +34,9 @@ interface Emit {
|
||||
}
|
||||
|
||||
const model = ref<Panel.Info>(props.itemInfo !== null ? { ...props.itemInfo } : { ...restoreDefault })
|
||||
// const model = computed(()=>{
|
||||
// return props.itemInfo !== null ? { ...props.itemInfo } : { ...restoreDefault }
|
||||
// })
|
||||
const formRef = ref<FormInst | null>(null)
|
||||
|
||||
const rules: FormRules = {
|
||||
@@ -43,6 +51,11 @@ const rules: FormRules = {
|
||||
type: 'string',
|
||||
message: '必填项',
|
||||
},
|
||||
// itemIconGroupId: {
|
||||
// required: true,
|
||||
// trigger: ['blur', 'change'],
|
||||
// message: '必填项',
|
||||
// },
|
||||
}
|
||||
|
||||
const options = [
|
||||
@@ -61,6 +74,22 @@ const options = [
|
||||
},
|
||||
]
|
||||
|
||||
// const urlProtocolOptions = [
|
||||
// {
|
||||
// default: true,
|
||||
// label: 'http://',
|
||||
// value: 'http://',
|
||||
// },
|
||||
// {
|
||||
// label: 'https://',
|
||||
// value: 'https://',
|
||||
// },
|
||||
// {
|
||||
// label: '不使用',
|
||||
// value: '',
|
||||
// },
|
||||
// ]
|
||||
|
||||
// 更新值父组件传来的值
|
||||
const show = computed({
|
||||
get: () => props.visible,
|
||||
@@ -70,15 +99,15 @@ const show = computed({
|
||||
})
|
||||
|
||||
async function editApi() {
|
||||
const { code, data } = await edit<Panel.ItemInfo>(model.value)
|
||||
const { code, data, msg } = await edit<Panel.ItemInfo>(model.value)
|
||||
if (code === 0) {
|
||||
show.value = false
|
||||
model.value = restoreDefault
|
||||
model.value = { ...restoreDefault }
|
||||
|
||||
emit('done', data)
|
||||
}
|
||||
else {
|
||||
ms.error('保存失败')
|
||||
ms.error(`保存失败:${msg}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,39 +120,77 @@ const handleValidateButtonClick = (e: MouseEvent) => {
|
||||
}
|
||||
|
||||
watch(() => props.itemInfo, (newValue) => {
|
||||
model.value = newValue || restoreDefault
|
||||
model.value = newValue || { ...restoreDefault }
|
||||
getGroupListOptions()
|
||||
})
|
||||
|
||||
function getGroupListOptions() {
|
||||
getGroupList<Common.ListResponse<Panel.ItemIconGroup[]>>().then(({ data, code, msg }) => {
|
||||
if (code === 0) {
|
||||
itemIconGroupOptions.value = []
|
||||
|
||||
for (let i = 0; i < data.list.length; i++) {
|
||||
const element = data.list[i]
|
||||
if (i === 0 && !model.value.itemIconGroupId) {
|
||||
restoreDefault.itemIconGroupId = element.id
|
||||
model.value.itemIconGroupId = element.id
|
||||
}
|
||||
|
||||
itemIconGroupOptions.value.push({
|
||||
value: element.id as number,
|
||||
label: element.title as string,
|
||||
})
|
||||
}
|
||||
}
|
||||
else {
|
||||
ms.error(`分组信息获取失败:${msg}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// rolesLoading.value = true
|
||||
getGroupListOptions()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NModal v-model:show="show" preset="card" style="width: 600px;border-radius: 1rem;" :title="itemInfo ? '修改项目' : '添加项目'">
|
||||
<NForm ref="formRef" :model="model" :rules="rules">
|
||||
<NFormItem path="title" label="标题">
|
||||
<NInput v-model:value="model.title" type="text" show-count :maxlength="20" placeholder="请输入标题" />
|
||||
</NFormItem>
|
||||
<NFormItem path="icon" label="图标">
|
||||
<IconEditor v-model:item-icon="model.icon" />
|
||||
</NFormItem>
|
||||
<NFormItem path="url" label="跳转地址">
|
||||
<NInput v-model:value="model.url" type="text" :maxlength="1000" placeholder="请输入跳转地址" />
|
||||
</NFormItem>
|
||||
<NFormItem path="lanUrl" label="局域网跳转地址">
|
||||
<NInput v-model:value="model.lanUrl" type="text" :maxlength="1000" placeholder="(可以留空)切换到局域网模式,点击会使用该地址" />
|
||||
</NFormItem>
|
||||
<NFormItem path="description" label="描述信息">
|
||||
<NInput v-model:value="model.description" type="text" show-count :maxlength="100" placeholder="请填写描述信息" />
|
||||
</NFormItem>
|
||||
<NFormItem path="openMethod" label="打开方式">
|
||||
<NSelect v-model:value="model.openMethod" :options="options" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<div class="h-[600px] overflow-auto p-[5px]">
|
||||
<NForm ref="formRef" :model="model" :rules="rules">
|
||||
<NGrid cols="2" :x-gap="10" item-responsive>
|
||||
<NGridItem span="2 500:1">
|
||||
<NFormItem path="itemIconGroupId" label="分组">
|
||||
<NSelect v-model:value="model.itemIconGroupId" :options="itemIconGroupOptions" />
|
||||
</NFormItem>
|
||||
</NGridItem>
|
||||
<NGridItem span="2 500:1">
|
||||
<NFormItem path="title" label="标题">
|
||||
<NInput v-model:value="model.title" type="text" show-count :maxlength="20" placeholder="请输入标题" />
|
||||
</NFormItem>
|
||||
</NGridItem>
|
||||
</NGrid>
|
||||
|
||||
<NFormItem path="icon" label="图标">
|
||||
<IconEditor v-model:item-icon="model.icon" />
|
||||
</NFormItem>
|
||||
<NFormItem path="url" label="跳转地址">
|
||||
<!-- <NSelect :style="{ width: '100px' }" :options="urlProtocolOptions" /> -->
|
||||
<NInput v-model:value="model.url" type="text" :maxlength="1000" placeholder="http(s)://" />
|
||||
</NFormItem>
|
||||
<NFormItem path="lanUrl" label="局域网跳转地址">
|
||||
<NInput v-model:value="model.lanUrl" type="text" :maxlength="1000" placeholder="http(s)://(可以留空,切换到局域网模式,点击会使用该地址)" />
|
||||
</NFormItem>
|
||||
<NFormItem path="description" label="描述信息">
|
||||
<NInput v-model:value="model.description" type="text" show-count :maxlength="100" placeholder="请填写描述信息" />
|
||||
</NFormItem>
|
||||
<NFormItem path="openMethod" label="打开方式">
|
||||
<NSelect v-model:value="model.openMethod" :options="options" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<NButton type="success" @click="handleValidateButtonClick">
|
||||
<NButton type="success" style="float: right;" @click="handleValidateButtonClick">
|
||||
确定
|
||||
</NButton>
|
||||
</template>
|
||||
|
||||
@@ -5,6 +5,8 @@ import Style from './tabs/Style.vue'
|
||||
import About from './tabs/About.vue'
|
||||
import Users from './tabs/Users.vue'
|
||||
import UserInfo from './tabs/UserInfo.vue'
|
||||
import ItemGroupManage from './tabs/ItemGroupManage.vue'
|
||||
|
||||
import { RoundCardModal } from '@/components/common'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -25,11 +27,14 @@ const show = computed({
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<RoundCardModal v-model:show="show" title="设置" style="max-height: 700px;">
|
||||
<NTabs type="line" animated>
|
||||
<RoundCardModal v-model:show="show" title="设置" style="max-height: 700px;max-width: 600px;">
|
||||
<NTabs type="line" size="small" animated>
|
||||
<NTabPane name="style" tab="样式">
|
||||
<Style />
|
||||
</NTabPane>
|
||||
<NTabPane name="itemGroupManage" tab="分组管理">
|
||||
<ItemGroupManage />
|
||||
</NTabPane>
|
||||
<NTabPane name="userInfo" tab="登录信息">
|
||||
<UserInfo />
|
||||
</NTabPane>
|
||||
@@ -45,7 +50,7 @@ const show = computed({
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.text-shadow{
|
||||
.text-shadow {
|
||||
text-shadow: 0px 0px 5px gray;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
import { NDivider, NGradientText } from 'naive-ui'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { get } from '@/api/system/about'
|
||||
import srcSvglogo from '@/assets/logo.svg'
|
||||
import srcGitee from '@/assets/about_image/gitee.png'
|
||||
import srcGithub from '@/assets/about_image/github.png'
|
||||
import srcDocker from '@/assets/about_image/docker.png'
|
||||
|
||||
interface Version {
|
||||
versionName: string
|
||||
@@ -20,26 +24,53 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="text-3xl">
|
||||
<NGradientText type="danger">
|
||||
Sun-Panel
|
||||
</NGradientText>
|
||||
v{{ versionName }}
|
||||
<div>
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<img :src="srcSvglogo" width="100" height="100" alt="">
|
||||
<div class="text-3xl font-semibold">
|
||||
{{ $t('common.appName') }}
|
||||
</div>
|
||||
<div class="text-xl">
|
||||
<NGradientText type="info">
|
||||
<a href="https://github.com/hslr-s/sun-panel/releases" class="font-semibold" title="点此查看更新说明" target="_blank">v{{ versionName }}</a>
|
||||
</NGradientText>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NDivider />
|
||||
<div class="text-lg">
|
||||
开发者: <a href="https://blog.enianteam.com/u/sun/content/11" target="_blank" class="link">红烧猎人</a>
|
||||
</div>
|
||||
<div class="text-lg">
|
||||
项目开源地址:
|
||||
<a href="https://github.com/hslr-s/sun-panel" target="_blank" class="link">Github</a> |
|
||||
<a href="https://gitee.com/hslr/sun-panel" target="_blank" class="link">Gitee</a>
|
||||
<div class="flex flex-col items-center justify-center text-base">
|
||||
<div>
|
||||
建议反馈:<a href="https://github.com/hslr-s/sun-panel/issues" target="_blank" class="link">Github Issues</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
QQ交流群:<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=_I9WIoJn1roIdoaAqelSj9qClLKlXIa1&authKey=GfsQP2GagHnus0jMc7U8Sm6VhWjtsipXUzCHbFwQsGyHMgmYWx6ZbAP%2Bhut%2B4D6N&noverify=0&group_code=276594668" target="_blank" class="link">276594668</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
开发者:<a href="https://blog.enianteam.com/u/sun/content/11" target="_blank" class="link">红烧猎人</a> | <a href="https://github.com/hslr-s/sun-panel/blob/master/doc/donate.md" target="_blank" class="text-red-600 hover:text-red-900">🧧打赏</a>
|
||||
</div>
|
||||
|
||||
<div class="flex mt-[10px]">
|
||||
<div class="flex items-center mx-[10px]">
|
||||
<img class="w-[20px] h-[20px] mr-[5px]" :src="srcGithub" alt="">
|
||||
<a href="https://github.com/hslr-s/sun-panel" target="_blank" class="link">Github</a>
|
||||
</div>
|
||||
<div class="flex items-center mx-[10px]">
|
||||
<img class="w-[20px] h-[20px] mr-[5px]" :src="srcGitee" alt="">
|
||||
<a href="https://gitee.com/hslr/sun-panel" target="_blank" class="link">Gitee</a>
|
||||
</div>
|
||||
<div class="flex items-center mx-[10px]">
|
||||
<img class="w-[20px] h-[20px] mr-[5px]" :src="srcDocker" alt="">
|
||||
<a href="https://hub.docker.com/r/hslr/sun-panel" target="_blank" class="link">Docker</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.link{
|
||||
color:blue
|
||||
color:rgb(0, 89, 255)
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -56,6 +56,12 @@ const rules: FormRules = {
|
||||
type: 'number',
|
||||
message: '请选择账号状态',
|
||||
},
|
||||
password: {
|
||||
trigger: 'blur',
|
||||
min: 6,
|
||||
max: 20,
|
||||
message: '6-20个字符',
|
||||
},
|
||||
}
|
||||
|
||||
// 更新值父组件传来的值
|
||||
@@ -106,7 +112,7 @@ const handleValidateButtonClick = (e: MouseEvent) => {
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem path="password" label="密码">
|
||||
<NInput v-model:value="model.password" type="text" :placeholder="`${userInfo?.id ? '请输入新密码,留空密码不变' : '请输入密码'}`" />
|
||||
<NInput v-model:value="model.password" :maxlength="20" type="password" :placeholder="`${userInfo?.id ? '请输入新密码,留空密码不变' : '请输入密码'}`" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import type { FormInst, FormRules } from 'naive-ui'
|
||||
import { NButton, NCard, NForm, NFormItem, NInput, useDialog, useMessage } from 'naive-ui'
|
||||
import { VueDraggable } from 'vue-draggable-plus'
|
||||
import { deletes, edit, getList, saveSort } from '@/api/panel/itemIconGroup'
|
||||
import { RoundCardModal, SvgIcon } from '@/components/common'
|
||||
|
||||
interface EditModalArg {
|
||||
show: boolean
|
||||
editStatus: number // 1.添加 2.编辑
|
||||
model: Panel.ItemIconGroup
|
||||
rules: FormRules
|
||||
}
|
||||
|
||||
const formRef = ref<FormInst | null>(null)
|
||||
const ms = useMessage()
|
||||
const dialog = useDialog()
|
||||
const sortStatus = ref(false)
|
||||
|
||||
const defaultMNodal = {
|
||||
title: '',
|
||||
icon: 'material-symbols:folder-outline',
|
||||
sort: 9999,
|
||||
}
|
||||
|
||||
const editModalArg = ref<EditModalArg>({
|
||||
show: false,
|
||||
editStatus: 1,
|
||||
model: defaultMNodal,
|
||||
rules: {
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
message: '必填项',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const groups = ref<Panel.ItemIconGroup[]>([])
|
||||
|
||||
function handleAddGroup() {
|
||||
editModalArg.value.show = !editModalArg.value.show
|
||||
}
|
||||
|
||||
function handleEditGroup(groupInfo: Panel.ItemIconGroup) {
|
||||
editModalArg.value.show = true
|
||||
editModalArg.value.model = groupInfo
|
||||
}
|
||||
|
||||
function handleDragSort() {
|
||||
sortStatus.value = true
|
||||
}
|
||||
|
||||
function handleSaveSort() {
|
||||
const saveItems: Common.SortItemRequest[] = []
|
||||
for (let i = 0; i < groups.value.length; i++) {
|
||||
const element = groups.value[i]
|
||||
saveItems.push({
|
||||
id: element.id as number,
|
||||
sort: i + 1,
|
||||
})
|
||||
}
|
||||
saveSort(saveItems).then(({ code, msg }) => {
|
||||
if (code === 0) {
|
||||
ms.success('保存成功')
|
||||
sortStatus.value = false
|
||||
}
|
||||
else {
|
||||
ms.error(`保存失败:${msg}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete(groupInfo: Panel.ItemIconGroup) {
|
||||
dialog.warning({
|
||||
title: '警告',
|
||||
content: `你确定删除此分组[ ${groupInfo.title} ],删除后此分组应用图标将丢失?`,
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
if (groupInfo.id) {
|
||||
deletes([groupInfo.id]).then(({ code, msg }) => {
|
||||
if (code !== 0)
|
||||
ms.error(`删除失败:${msg}`)
|
||||
else
|
||||
refreshList()
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
function handleSaveGroup() {
|
||||
formRef.value?.validate((errors) => {
|
||||
if (!errors) {
|
||||
edit(editModalArg.value.model).then(({ code, msg }) => {
|
||||
if (code !== 0)
|
||||
ms.error(msg)
|
||||
|
||||
refreshList()
|
||||
editModalArg.value.show = false
|
||||
editModalArg.value.model = { ...defaultMNodal }
|
||||
})
|
||||
}
|
||||
else { console.log(errors) }
|
||||
})
|
||||
}
|
||||
|
||||
function refreshList() {
|
||||
getList<Common.ListResponse<Panel.ItemIconGroup[]>>().then(({ code, data }) => {
|
||||
groups.value = data.list
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
refreshList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-[500px]">
|
||||
<div>
|
||||
<NButton type="success" size="small" style="margin-right: 10px;" @click="handleAddGroup">
|
||||
新增分组
|
||||
</NButton>
|
||||
|
||||
<NButton v-if="!sortStatus" size="small" @click="handleDragSort">
|
||||
排序
|
||||
</NButton>
|
||||
|
||||
<NButton v-else type="warning" size="small" @click="handleSaveSort">
|
||||
保存排序
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
<div class=" overflow-auto w-full mt-[20px] bg-slate-200 rounded-xl" style="height:calc(100% - 50px)">
|
||||
<VueDraggable
|
||||
v-model="groups"
|
||||
item-key="sort" :animation="300"
|
||||
:style="{ padding: sortStatus ? '20px' : '10px' }"
|
||||
:disabled="!sortStatus"
|
||||
>
|
||||
<div v-for="(item, index) in groups" :key="index" class="w-full">
|
||||
<NCard size="small" style="border-radius:10px;margin-bottom: 10px;">
|
||||
<div class="flex" :class="sortStatus ? 'cursor-move' : ''">
|
||||
<div class="flex items-center">
|
||||
<span class="mr-[10px]">
|
||||
<SvgIcon class="text-[20px]" icon="material-symbols:ad-group-outline" />
|
||||
<!-- <SvgIcon class="text-[20px]" :icon="item.icon" /> -->
|
||||
</span>
|
||||
<span>
|
||||
{{ item.title }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="ml-auto">
|
||||
<span>
|
||||
<NButton strong secondary type="success" size="small" @click="handleEditGroup(item)">
|
||||
<template #icon>
|
||||
<SvgIcon icon="basil:edit-solid" />
|
||||
</template>
|
||||
</NButton>
|
||||
</span>
|
||||
<span class="ml-[10px]">
|
||||
<NButton strong secondary type="error" size="small" class="ml-[10px]" @click="handleDelete(item)">
|
||||
<template #icon>
|
||||
<SvgIcon icon="material-symbols:delete" />
|
||||
</template>
|
||||
</NButton>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
</VueDraggable>
|
||||
</div>
|
||||
|
||||
<RoundCardModal v-model:show="editModalArg.show" type="small" :title="editModalArg.editStatus === 1 ? '添加' : '编辑'" style="width: 400px;">
|
||||
<NForm ref="formRef" :model="editModalArg.model" :rules="editModalArg.rules">
|
||||
<NFormItem path="title" label="分组名称">
|
||||
<NInput v-model:value="editModalArg.model.title" type="text" :maxlength="20" show-count placeholder="请输入" />
|
||||
</NFormItem>
|
||||
|
||||
<!-- <NFormItem path="name" label="昵称">
|
||||
<NInput v-model:value="editModalArg.model" type="text" placeholder="请输入昵称" />
|
||||
</NFormItem> -->
|
||||
</NForm>
|
||||
<template #footer>
|
||||
<NButton type="success" size="small" class="float-right" @click="handleSaveGroup">
|
||||
确定
|
||||
</NButton>
|
||||
</template>
|
||||
</RoundCardModal>
|
||||
</div>
|
||||
</template>
|
||||
@@ -4,6 +4,7 @@ import type { UploadFileInfo } from 'naive-ui'
|
||||
import { NButton, NCard, NColorPicker, NInput, NPopconfirm, NSelect, NSlider, NSwitch, NUpload, NUploadDragger, useMessage } from 'naive-ui'
|
||||
import { useAuthStore, usePanelState } from '@/store'
|
||||
import { set as setUserConfig } from '@/api/panel/userConfig'
|
||||
import { PanelPanelConfigStyleEnum } from '@/enums/panel'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const panelState = usePanelState()
|
||||
@@ -14,11 +15,11 @@ const isSaveing = ref(false)
|
||||
const iconTypeOptions = [
|
||||
{
|
||||
label: '详情图标',
|
||||
value: 0,
|
||||
value: PanelPanelConfigStyleEnum.info,
|
||||
},
|
||||
{
|
||||
label: '小图标',
|
||||
value: 1,
|
||||
value: PanelPanelConfigStyleEnum.icon,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -85,6 +86,68 @@ function resetPanelConfig() {
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px]">
|
||||
搜索框
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<span class="mr-[10px]">显示</span>
|
||||
<NSwitch v-model:value="panelState.panelConfig.searchBoxShow" />
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px]">
|
||||
图标
|
||||
</div>
|
||||
<div class="mt-[5px]">
|
||||
<div>
|
||||
样式
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NSelect v-model:value="panelState.panelConfig.iconStyle" :options="iconTypeOptions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="panelState.panelConfig.iconStyle === PanelPanelConfigStyleEnum.info" class="mt-[5px]">
|
||||
<div>
|
||||
隐藏描述信息
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NSwitch v-model:value="panelState.panelConfig.iconTextInfoHideDescription" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="panelState.panelConfig.iconStyle === PanelPanelConfigStyleEnum.icon" class="mt-[5px]">
|
||||
<div>
|
||||
隐藏标题
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NSwitch v-model:value="panelState.panelConfig.iconTextIconHideTitle" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-[5px]">
|
||||
<div>
|
||||
文字颜色
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NColorPicker
|
||||
v-model:value="panelState.panelConfig.iconTextColor"
|
||||
:show-alpha="false"
|
||||
size="small"
|
||||
:modes="['hex']"
|
||||
:swatches="[
|
||||
'#000000',
|
||||
'#ffffff',
|
||||
'#18A058',
|
||||
'#2080F0',
|
||||
'#F0A020',
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</NCard>
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px]">
|
||||
壁纸
|
||||
@@ -99,9 +162,9 @@ function resetPanelConfig() {
|
||||
:directory-dnd="true"
|
||||
@finish="handleUploadBackgroundFinish"
|
||||
>
|
||||
<NUploadDragger>
|
||||
<NUploadDragger style="width: 100%;">
|
||||
<div
|
||||
class="h-[150px] w-[280px] border bg-slate-100 flex justify-center items-center cursor-pointer rounded-[10px]"
|
||||
class="h-[200px] w-full border bg-slate-100 flex justify-center items-center cursor-pointer rounded-[10px]"
|
||||
:style="{ background: `url(${panelState.panelConfig.backgroundImageSrc}) no-repeat`, backgroundSize: 'cover' }"
|
||||
>
|
||||
<div class="text-shadow text-white">
|
||||
@@ -111,39 +174,14 @@ function resetPanelConfig() {
|
||||
</NUploadDragger>
|
||||
</NUpload>
|
||||
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<span class="mr-[10px]">模糊处理</span>
|
||||
<div class="flex items-center mt-[10px]">
|
||||
<span class="mr-[10px]">模糊</span>
|
||||
<NSlider v-model:value="panelState.panelConfig.backgroundBlur" class="max-w-[200px]" :step="2" :max="20" />
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<div class="text-slate-500 mb-[5px]">
|
||||
图标
|
||||
</div>
|
||||
<div>
|
||||
样式
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NSelect v-model:value="panelState.panelConfig.iconStyle" :options="iconTypeOptions" />
|
||||
</div>
|
||||
<div>
|
||||
文字颜色
|
||||
</div>
|
||||
<div class="flex items-center mt-[5px]">
|
||||
<NColorPicker
|
||||
v-model:value="panelState.panelConfig.iconTextColor"
|
||||
:show-alpha="false"
|
||||
size="small"
|
||||
:modes="['hex']"
|
||||
:swatches="[
|
||||
'#000000',
|
||||
'#ffffff',
|
||||
'#18A058',
|
||||
'#2080F0',
|
||||
'#F0A020',
|
||||
]"
|
||||
/>
|
||||
<div class="flex items-center mt-[10px]">
|
||||
<span class="mr-[10px]">遮罩</span>
|
||||
<NSlider v-model:value="panelState.panelConfig.backgroundMaskNumber" class="max-w-[200px]" :step="0.1" :max="1" />
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { NButton, NCard, useDialog, useMessage } from 'naive-ui'
|
||||
import { useAuthStore, usePanelState, useUserStore } from '@/store'
|
||||
import { logout } from '@/api'
|
||||
import { router } from '@/router'
|
||||
import { SvgIcon } from '@/components/common/'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const authStore = useAuthStore()
|
||||
@@ -51,6 +52,9 @@ function handleLogiut() {
|
||||
|
||||
<NCard style="border-radius:10px" class="mt-[10px]" size="small">
|
||||
<NButton size="small" quaternary type="error" @click="handleLogiut">
|
||||
<template #icon>
|
||||
<SvgIcon icon="tabler:logout" />
|
||||
</template>
|
||||
退出登录
|
||||
</NButton>
|
||||
</NCard>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { h, onMounted, reactive, ref } from 'vue'
|
||||
import { NButton, NDataTable, NDropdown, useDialog, useMessage } from 'naive-ui'
|
||||
import { NAlert, NButton, NDataTable, NDropdown, useDialog, useMessage } from 'naive-ui'
|
||||
import type { DataTableColumns, PaginationProps } from 'naive-ui'
|
||||
import EditUser from './EditUser/index.vue'
|
||||
import { deletes as usersDeletes, getList as usersGetList } from '@/api/panel/users'
|
||||
@@ -171,7 +171,10 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div class="h-[500px] overflow-auto">
|
||||
<div class="mb-[10px]">
|
||||
<NAlert type="info" :bordered="false">
|
||||
账号之间的数据不互通
|
||||
</NAlert>
|
||||
<div class="my-[10px]">
|
||||
<NButton type="primary" size="small" ghost @click="handleAdd">
|
||||
添加
|
||||
</NButton>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import Result from './Result/index.vue'
|
||||
import EditItem from './EditItem/index.vue'
|
||||
import Setting from './Setting/index.vue'
|
||||
import AppIcon from './AppIcon/index.vue'
|
||||
|
||||
export {
|
||||
Result, EditItem, Setting,
|
||||
Result, EditItem, Setting, AppIcon,
|
||||
}
|
||||
|
||||
+313
-158
@@ -1,20 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import { NButton, NButtonGroup, NDropdown, NEllipsis, NGrid, NGridItem, NModal, NSkeleton, NSpin, useDialog, useMessage } from 'naive-ui'
|
||||
import { nextTick, onMounted, ref } from 'vue'
|
||||
import { EditItem, Setting } from './components'
|
||||
import { Clock } from '@/components/deskModule'
|
||||
import { ItemIcon, SvgIcon } from '@/components/common'
|
||||
import { deletes, getListByGroupId } from '@/api/panel/itemIcon'
|
||||
import { VueDraggable } from 'vue-draggable-plus'
|
||||
import { NBackTop, NButton, NButtonGroup, NDropdown, NModal, NSkeleton, NSpin, useDialog, useMessage } from 'naive-ui'
|
||||
import { nextTick, onMounted, ref, watch } from 'vue'
|
||||
import { AppIcon, EditItem, Setting } from './components'
|
||||
import { Clock, SearchBox } from '@/components/deskModule'
|
||||
import { SvgIcon } from '@/components/common'
|
||||
import { deletes, getListByGroupId, saveSort } from '@/api/panel/itemIcon'
|
||||
import { getList as getGroupList } from '@/api/panel/itemIconGroup'
|
||||
|
||||
import { getInfo } from '@/api/system/user'
|
||||
import { usePanelState, useUserStore } from '@/store'
|
||||
import { PanelStateNetworkModeEnum } from '@/enum'
|
||||
import { PanelPanelConfigStyleEnum, PanelStateNetworkModeEnum } from '@/enums'
|
||||
import { setTitle } from '@/utils/cmn'
|
||||
|
||||
interface StateDragAppSort {
|
||||
status: boolean
|
||||
}
|
||||
interface ItemGroup extends Panel.ItemIconGroup {
|
||||
items?: Panel.ItemInfo[]
|
||||
}
|
||||
|
||||
const stateDragAppSort = ref<StateDragAppSort>({
|
||||
status: false,
|
||||
})
|
||||
|
||||
const ms = useMessage()
|
||||
const dialog = useDialog()
|
||||
const panelState = usePanelState()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const scrollContainerRef = ref<HTMLElement | undefined>(undefined)
|
||||
|
||||
const editItemInfoShow = ref<boolean>(false)
|
||||
const editItemInfoData = ref<Panel.ItemInfo | null>(null)
|
||||
const windowShow = ref<boolean>(false)
|
||||
@@ -31,27 +47,33 @@ const currentRightSelectItem = ref<Panel.ItemInfo | null>(null)
|
||||
|
||||
const settingModalShow = ref(false)
|
||||
|
||||
const dropdownMenuOptions = [
|
||||
{
|
||||
label: '新窗口打开',
|
||||
key: 'newWindows',
|
||||
},
|
||||
{
|
||||
label: '编辑',
|
||||
key: 'edit',
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
key: 'delete',
|
||||
},
|
||||
]
|
||||
const items = ref<Panel.ItemInfo[]>()
|
||||
const items = ref<ItemGroup[]>([])
|
||||
|
||||
function handleAddAppClick() {
|
||||
editItemInfoData.value = null
|
||||
editItemInfoShow.value = true
|
||||
}
|
||||
|
||||
function openPage(openMethod: number, url: string, title?: string) {
|
||||
switch (openMethod) {
|
||||
case 1:
|
||||
window.location.href = url
|
||||
break
|
||||
case 2:
|
||||
window.open(url)
|
||||
break
|
||||
case 3:
|
||||
windowShow.value = true
|
||||
windowSrc.value = url
|
||||
windowTitle.value = title || url
|
||||
windowIframeIsLoad.value = true
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function handleItemClick(item: Panel.ItemInfo) {
|
||||
let jumpUrl = ''
|
||||
|
||||
@@ -60,23 +82,7 @@ function handleItemClick(item: Panel.ItemInfo) {
|
||||
if (item.lanUrl === '')
|
||||
jumpUrl = item.url
|
||||
|
||||
switch (item.openMethod) {
|
||||
case 1:
|
||||
window.location.href = jumpUrl
|
||||
break
|
||||
case 2:
|
||||
window.open(jumpUrl)
|
||||
break
|
||||
case 3:
|
||||
windowShow.value = true
|
||||
windowSrc.value = jumpUrl
|
||||
windowTitle.value = item.title
|
||||
windowIframeIsLoad.value = true
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
openPage(item.openMethod, jumpUrl, item.title)
|
||||
}
|
||||
|
||||
function handWindowIframeIdLoad(payload: Event) {
|
||||
@@ -84,9 +90,18 @@ function handWindowIframeIdLoad(payload: Event) {
|
||||
}
|
||||
|
||||
function getList() {
|
||||
getListByGroupId<Common.ListResponse<Panel.ItemInfo[]>>().then((res) => {
|
||||
if (res.code === 0)
|
||||
items.value = res.data.list
|
||||
// 获取组数据
|
||||
getGroupList<Common.ListResponse<ItemGroup[]>>().then(({ code, data, msg }) => {
|
||||
if (code === 0)
|
||||
items.value = data.list
|
||||
for (let i = 0; i < data.list.length; i++) {
|
||||
const element = data.list[i]
|
||||
getListByGroupId<Common.ListResponse<Panel.ItemInfo[]>>(element.id).then((res) => {
|
||||
if (res.code === 0)
|
||||
items.value[i].items = res.data.list
|
||||
})
|
||||
}
|
||||
// console.log(items)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,6 +115,14 @@ function handleSelect(key: string | number) {
|
||||
case 'newWindows':
|
||||
window.open(jumpUrl)
|
||||
break
|
||||
case 'openWanUrl':
|
||||
if (currentRightSelectItem.value)
|
||||
openPage(currentRightSelectItem.value?.openMethod, currentRightSelectItem.value?.url, currentRightSelectItem.value?.title)
|
||||
break
|
||||
case 'openLanUrl':
|
||||
if (currentRightSelectItem.value && currentRightSelectItem.value.lanUrl)
|
||||
openPage(currentRightSelectItem.value?.openMethod, currentRightSelectItem.value.lanUrl, currentRightSelectItem.value?.title)
|
||||
break
|
||||
case 'edit':
|
||||
// 这里有个奇怪的问题,如果不使用{...}的方式 父组件的值会同步修改 标记一下
|
||||
editItemInfoData.value = { ...currentRightSelectItem.value } as Panel.ItemInfo
|
||||
@@ -153,12 +176,83 @@ function handleEditSuccess(item: Panel.ItemInfo) {
|
||||
function handleChangeNetwork(mode: PanelStateNetworkModeEnum) {
|
||||
panelState.setNetworkMode(mode)
|
||||
if (mode === PanelStateNetworkModeEnum.lan)
|
||||
ms.success('已经切换成局域网模式,此时再点击已填写局域网地址的图标将跳转至局域网地址(此配置仅保存在本地)')
|
||||
ms.success('已经切换成局域网模式(此配置仅保存在本地)')
|
||||
|
||||
else
|
||||
ms.success('已经切换成互联网模式(此配置仅保存在本地)')
|
||||
}
|
||||
|
||||
// 结束拖拽
|
||||
function handleEndDrag(event: any, itemIconGroup: Panel.ItemIconGroup) {
|
||||
// console.log(event)
|
||||
// console.log(items.value)
|
||||
}
|
||||
|
||||
function handleSaveSort(itemGroup: ItemGroup) {
|
||||
const saveItems: Common.SortItemRequest[] = []
|
||||
if (itemGroup.items) {
|
||||
for (let i = 0; i < itemGroup.items.length; i++) {
|
||||
const element = itemGroup.items[i]
|
||||
saveItems.push({
|
||||
id: element.id as number,
|
||||
sort: i + 1,
|
||||
})
|
||||
}
|
||||
|
||||
saveSort({ itemIconGroupId: itemGroup.id as number, sortItems: saveItems }).then(({ code, msg }) => {
|
||||
if (code === 0) {
|
||||
//
|
||||
ms.success('保存成功')
|
||||
// sortStatus.value = false
|
||||
}
|
||||
else {
|
||||
ms.error(`保存失败:${msg}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getDropdownMenuOptions() {
|
||||
const dropdownMenuOptions = [
|
||||
{
|
||||
label: '新窗口打开',
|
||||
key: 'newWindows',
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
if (currentRightSelectItem.value?.lanUrl && panelState.networkMode === PanelStateNetworkModeEnum.wan) {
|
||||
dropdownMenuOptions.push({
|
||||
label: '打开局域网地址',
|
||||
key: 'openLanUrl',
|
||||
})
|
||||
}
|
||||
|
||||
if (currentRightSelectItem.value?.lanUrl && panelState.networkMode === PanelStateNetworkModeEnum.lan) {
|
||||
dropdownMenuOptions.push({
|
||||
label: '打开互联网地址',
|
||||
key: 'openWanUrl',
|
||||
})
|
||||
}
|
||||
|
||||
dropdownMenuOptions.push({
|
||||
label: '编辑',
|
||||
key: 'edit',
|
||||
}, {
|
||||
label: '删除',
|
||||
key: 'delete',
|
||||
})
|
||||
|
||||
return dropdownMenuOptions
|
||||
}
|
||||
|
||||
watch(() => stateDragAppSort.value.status, (newvalue: boolean) => {
|
||||
if (newvalue === false)
|
||||
getList()
|
||||
else
|
||||
ms.warning('进入排序模式,记得点击保存再退出')
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
|
||||
@@ -180,120 +274,137 @@ onMounted(() => {
|
||||
<template>
|
||||
<div class="w-full h-full sun-main ">
|
||||
<div
|
||||
class="cover"
|
||||
:style="{
|
||||
class="cover" :style="{
|
||||
filter: `blur(${panelState.panelConfig.backgroundBlur}px)`,
|
||||
background: `url(${panelState.panelConfig.backgroundImageSrc}) no-repeat`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}"
|
||||
/>
|
||||
<div class="absolute w-full h-full overflow-auto">
|
||||
<div class="mask" :style="{ backgroundColor: `rgba(0,0,0,${panelState.panelConfig.backgroundMaskNumber})` }" />
|
||||
<div ref="scrollContainerRef" class="absolute w-full h-full overflow-auto">
|
||||
<div class="p-2.5 max-w-[1200px] mx-auto mt-[10%]">
|
||||
<!-- 头 -->
|
||||
<div class="mx-[auto] w-[80%]">
|
||||
<div class="flex mx-[auto] items-center justify-center text-white">
|
||||
<div>
|
||||
<span class="text-5xl font-bold text-shadow">
|
||||
<span class="text-2xl md:text-5xl font-bold text-shadow">
|
||||
{{ panelState.panelConfig.logoText }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-2xl mx-[10px]">
|
||||
<div class="text-base lg:text-2xl mx-[10px]">
|
||||
|
|
||||
</div>
|
||||
<div class="text-shadow">
|
||||
<Clock :hide-second="!panelState.panelConfig.clockShowSecond" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="flex mt-[20px] mx-auto w-[80%]">
|
||||
<SearchBox />
|
||||
</div> -->
|
||||
<div v-if="panelState.panelConfig.searchBoxShow" class="flex mt-[20px] mx-auto sm:w-full lg:w-[80%]">
|
||||
<SearchBox />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图标 -->
|
||||
<!-- 应用盒子 -->
|
||||
<div class="mt-[50px]">
|
||||
<!-- 详情图标 -->
|
||||
<div v-if="panelState.panelConfig.iconStyle === 0">
|
||||
<NGrid :x-gap="15" :y-gap="15" item-responsive cols="1 200:1 400:2 600:3 800:4 1000:5 1200:6">
|
||||
<NGridItem v-for="(item, index) in items" :key="index">
|
||||
<div @contextmenu="(e) => handleContextMenu(e, item)">
|
||||
<div
|
||||
class="w-full rounded-2xl cursor-pointer transition-all duration-200 hover:shadow-[0_0_20px_10px_rgba(0,0,0,0.2)] bg-[#2a2a2a6b] flex"
|
||||
@click="handleItemClick(item)"
|
||||
>
|
||||
<div class="w-[70px]">
|
||||
<ItemIcon :item-icon="item.icon" />
|
||||
</div>
|
||||
<div class="text-white m-[8px_8px_0_8px]" :style="{ color: panelState.panelConfig.iconTextColor }">
|
||||
<div>
|
||||
<NEllipsis>
|
||||
{{ item.title }}
|
||||
</NEllipsis>
|
||||
</div>
|
||||
<div>
|
||||
<NEllipsis :line-clamp="2" class="text-xs">
|
||||
{{ item.description }}
|
||||
</NEllipsis>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
<!-- 组纵向排列 -->
|
||||
<div
|
||||
v-for="(itemGroup, itemGroupIndex) in items"
|
||||
:key="itemGroupIndex"
|
||||
class="mt-[50px]"
|
||||
:class="stateDragAppSort.status ? 'shadow-2xl border shadow-[0_0_30px_10px_rgba(0,0,0,0.8)] p-[10px] rounded-2xl' : ''"
|
||||
>
|
||||
<!-- 分组标题 -->
|
||||
<div class="text-white text-xl font-extrabold mb-[20px] ml-[10px]">
|
||||
{{ itemGroup.title }}
|
||||
</div>
|
||||
|
||||
<NGridItem>
|
||||
<div>
|
||||
<div
|
||||
class="w-full rounded-2xl cursor-pointer transition-all duration-200 hover:shadow-[0_0_20px_10px_rgba(0,0,0,0.2)] bg-[#2a2a2a6b] flex"
|
||||
@click="handleAddAppClick"
|
||||
>
|
||||
<ItemIcon :item-icon="{ itemType: 3, text: 'subway:add', bgColor: '#00000000' }" />
|
||||
<div class="text-white m-[8px]" :style="{ color: panelState.panelConfig.iconTextColor }">
|
||||
<div>
|
||||
<NEllipsis>
|
||||
添加图标
|
||||
</NEllipsis>
|
||||
</div>
|
||||
<!-- 详情图标 -->
|
||||
<div v-if="panelState.panelConfig.iconStyle === PanelPanelConfigStyleEnum.info">
|
||||
<div v-if="itemGroup.items">
|
||||
<VueDraggable
|
||||
v-model="itemGroup.items" item-key="sort" :animation="300"
|
||||
class="mx-auto mt-4 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:12 gap-5"
|
||||
filter=".not-drag"
|
||||
:disabled="!stateDragAppSort.status"
|
||||
@end="(event) => handleEndDrag(event, itemGroup)"
|
||||
>
|
||||
<div v-for="item, index in itemGroup.items" :key="index" :title="item.description" @contextmenu="(e) => handleContextMenu(e, item)">
|
||||
<AppIcon
|
||||
:class="stateDragAppSort.status ? 'cursor-move' : 'cursor-pointer'"
|
||||
:item-info="item"
|
||||
:icon-text-color="panelState.panelConfig.iconTextColor"
|
||||
:icon-text-info-hide-description="panelState.panelConfig.iconTextInfoHideDescription || false"
|
||||
:icon-text-icon-hide-title="panelState.panelConfig.iconTextIconHideTitle || false"
|
||||
:style="0"
|
||||
@click="handleItemClick(item)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="text text-xs">
|
||||
<NEllipsis>
|
||||
新增一个新的图标
|
||||
</NEllipsis>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="itemGroup.items.length === 0" class="not-drag">
|
||||
<AppIcon
|
||||
:class="stateDragAppSort.status ? 'cursor-move' : 'cursor-pointer'"
|
||||
:item-info="{ icon: { itemType: 3, text: 'subway:add' }, title: '添加图标', url: '', openMethod: 0 }"
|
||||
:icon-text-color="panelState.panelConfig.iconTextColor"
|
||||
:icon-text-info-hide-description="panelState.panelConfig.iconTextInfoHideDescription || false"
|
||||
:icon-text-icon-hide-title="panelState.panelConfig.iconTextIconHideTitle || false"
|
||||
:style="0"
|
||||
@click="handleAddAppClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
</NGrid>
|
||||
</div>
|
||||
</VueDraggable>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- APP图标宫型盒子 -->
|
||||
<div v-if="panelState.panelConfig.iconStyle === 1">
|
||||
<NGrid :x-gap="12" :y-gap="8" item-responsive cols="3 300:4 600:6 900:8">
|
||||
<NGridItem v-for="(item, index) in items" :key="index">
|
||||
<div @contextmenu="(e) => handleContextMenu(e, item)">
|
||||
<div
|
||||
class="w-[70px] h-[70px] mx-auto rounded-2xl cursor-pointer transition-all duration-200 hover:shadow-[0_0_20px_10px_rgba(0,0,0,0.2)] bg-[#2a2a2a6b]"
|
||||
@click="handleItemClick(item)"
|
||||
>
|
||||
<ItemIcon :item-icon="item.icon" />
|
||||
</div>
|
||||
<div class="text-center app-icon-text-shadow cursor-pointer mt-[2px]" :style="{ color: panelState.panelConfig.iconTextColor }" @click="handleItemClick(item)">
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
<!-- APP图标宫型盒子 -->
|
||||
<div v-if="panelState.panelConfig.iconStyle === PanelPanelConfigStyleEnum.icon">
|
||||
<div v-if="itemGroup.items">
|
||||
<VueDraggable
|
||||
v-model="itemGroup.items" item-key="id" :animation="300"
|
||||
class="mx-auto mt-4 grid grid-cols-4 sm:grid-cols-6 md:grid-cols-8 lg:grid-cols-10 xl:12 gap-5"
|
||||
|
||||
<NGridItem>
|
||||
<div>
|
||||
<div class="w-[70px] h-[70px] mx-auto rounded-2xl cursor-pointer transition-all duration-200 hover:shadow-[0_0_20px_10px_rgba(0,0,0,0.2)]" @click="handleAddAppClick">
|
||||
<ItemIcon :item-icon="{ itemType: 3, text: 'subway:add', bgColor: '#343434' }" />
|
||||
filter=".not-drag"
|
||||
:disabled="!stateDragAppSort.status"
|
||||
>
|
||||
<div v-for="item, index in itemGroup.items" :key="index" :title="item.description" @contextmenu="(e) => handleContextMenu(e, item)">
|
||||
<AppIcon
|
||||
:class="stateDragAppSort.status ? 'cursor-move' : 'cursor-pointer'"
|
||||
:item-info="item"
|
||||
:icon-text-color="panelState.panelConfig.iconTextColor"
|
||||
:icon-text-info-hide-description="!panelState.panelConfig.iconTextInfoHideDescription"
|
||||
:icon-text-icon-hide-title="panelState.panelConfig.iconTextIconHideTitle || false"
|
||||
:style="1"
|
||||
@click="handleItemClick(item)"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-center app-icon-text-shadow cursor-pointer mt-[2px]" :style="{ color: panelState.panelConfig.iconTextColor }" @click="handleAddAppClick">
|
||||
添加图标
|
||||
|
||||
<div v-if="itemGroup.items.length === 0" class="not-drag">
|
||||
<AppIcon
|
||||
:class="stateDragAppSort.status ? 'cursor-move' : 'cursor-pointer'"
|
||||
:item-info="{ icon: { itemType: 3, text: 'subway:add' }, title: '添加图标', url: '', openMethod: 0 }"
|
||||
:icon-text-color="panelState.panelConfig.iconTextColor"
|
||||
:icon-text-info-hide-description="!panelState.panelConfig.iconTextInfoHideDescription"
|
||||
:icon-text-icon-hide-title="panelState.panelConfig.iconTextIconHideTitle || false"
|
||||
:style="1"
|
||||
@click="handleAddAppClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
</NGrid>
|
||||
</vuedraggable>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑栏 -->
|
||||
<div v-if="stateDragAppSort.status" class="flex mt-[10px]">
|
||||
<div>
|
||||
<NButton color="#2a2a2a6b" @click="handleSaveSort(itemGroup)">
|
||||
<template #icon>
|
||||
<SvgIcon class="text-white font-xl" icon="material-symbols:save" />
|
||||
</template>
|
||||
<div>
|
||||
保存排序
|
||||
</div>
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -301,29 +412,48 @@ onMounted(() => {
|
||||
|
||||
<!-- 右键菜单 -->
|
||||
<NDropdown
|
||||
placement="bottom-start"
|
||||
trigger="manual"
|
||||
:x="dropdownMenuX"
|
||||
:y="dropdownMenuY"
|
||||
:options="dropdownMenuOptions"
|
||||
:show="dropdownShow"
|
||||
:on-clickoutside="onClickoutside"
|
||||
@select="handleSelect"
|
||||
placement="bottom-start" trigger="manual" :x="dropdownMenuX" :y="dropdownMenuY"
|
||||
:options="getDropdownMenuOptions()" :show="dropdownShow" :on-clickoutside="onClickoutside" @select="handleSelect"
|
||||
/>
|
||||
|
||||
<!-- 悬浮按钮 -->
|
||||
<div class="fixed-element shadow-[0_0_10px_2px_rgba(0,0,0,0.2)]">
|
||||
<NButtonGroup vertical>
|
||||
<NButton v-if="panelState.networkMode === PanelStateNetworkModeEnum.lan" color="#2a2a2a6b" title="当前:局域网模式,点击切换成互联网模式" @click="handleChangeNetwork(PanelStateNetworkModeEnum.wan)">
|
||||
<NButton v-if="stateDragAppSort.status" color="#2a2a2a6b" @click="stateDragAppSort.status = !stateDragAppSort.status">
|
||||
<template #icon>
|
||||
<SvgIcon class="text-white font-xl" icon="ri:drag-drop-line" />
|
||||
</template>
|
||||
</NButton>
|
||||
<NButtonGroup v-if="!stateDragAppSort.status" vertical>
|
||||
<NButton color="#2a2a2a6b" @click="handleAddAppClick">
|
||||
<template #icon>
|
||||
<SvgIcon class="text-white font-xl" icon="typcn:plus" />
|
||||
</template>
|
||||
</NButton>
|
||||
|
||||
<NButton
|
||||
v-if="panelState.networkMode === PanelStateNetworkModeEnum.lan" color="#2a2a2a6b"
|
||||
title="当前:局域网模式,点击切换成互联网模式" @click="handleChangeNetwork(PanelStateNetworkModeEnum.wan)"
|
||||
>
|
||||
<template #icon>
|
||||
<SvgIcon class="text-white font-xl" icon="material-symbols:lan-outline" />
|
||||
</template>
|
||||
</NButton>
|
||||
<NButton v-if="panelState.networkMode === PanelStateNetworkModeEnum.wan" color="#2a2a2a6b" title="当前:互联网模式,点击切换成局域网模式" @click="handleChangeNetwork(PanelStateNetworkModeEnum.lan)">
|
||||
|
||||
<NButton
|
||||
v-if="panelState.networkMode === PanelStateNetworkModeEnum.wan" color="#2a2a2a6b"
|
||||
title="当前:互联网模式,点击切换成局域网模式" @click="handleChangeNetwork(PanelStateNetworkModeEnum.lan)"
|
||||
>
|
||||
<template #icon>
|
||||
<SvgIcon class="text-white font-xl" icon="mdi:wan" />
|
||||
</template>
|
||||
</NButton>
|
||||
|
||||
<NButton color="#2a2a2a6b" title="排序模式" @click="stateDragAppSort.status = !stateDragAppSort.status">
|
||||
<template #icon>
|
||||
<SvgIcon class="text-white font-xl" icon="ri:drag-drop-line" />
|
||||
</template>
|
||||
</NButton>
|
||||
|
||||
<NButton color="#2a2a2a6b" @click="settingModalShow = !settingModalShow">
|
||||
<template #icon>
|
||||
<SvgIcon class="text-white font-xl" icon="ep:setting" />
|
||||
@@ -331,20 +461,30 @@ onMounted(() => {
|
||||
</NButton>
|
||||
</NButtonGroup>
|
||||
|
||||
<NBackTop
|
||||
:listen-to="() => scrollContainerRef"
|
||||
:right="10"
|
||||
:bottom="10"
|
||||
style="background-color:transparent;border: none;box-shadow: none;"
|
||||
>
|
||||
<div class="shadow-[0_0_10px_2px_rgba(0,0,0,0.2)]">
|
||||
<NButton color="#2a2a2a6b">
|
||||
<template #icon>
|
||||
<SvgIcon class="text-white font-xl" icon="icon-park-outline:to-top" />
|
||||
</template>
|
||||
</NButton>
|
||||
</div>
|
||||
</NBackTop>
|
||||
|
||||
<Setting v-model:visible="settingModalShow" />
|
||||
</div>
|
||||
|
||||
<EditItem v-model:visible="editItemInfoShow" :item-info="editItemInfoData" @done="handleEditSuccess" />
|
||||
|
||||
<!-- 新窗口 -->
|
||||
<!-- 弹窗 -->
|
||||
<NModal
|
||||
v-model:show="windowShow"
|
||||
:mask-closable="false"
|
||||
preset="card"
|
||||
style="max-width: 1000px;height: 600px;border-radius: 1rem;"
|
||||
:bordered="false"
|
||||
size="small"
|
||||
role="dialog"
|
||||
v-model:show="windowShow" :mask-closable="false" preset="card"
|
||||
style="max-width: 1000px;height: 600px;border-radius: 1rem;" :bordered="false" size="small" role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<template #header>
|
||||
@@ -358,45 +498,60 @@ onMounted(() => {
|
||||
</template>
|
||||
<div class="w-full h-full rounded-2xl overflow-hidden border">
|
||||
<NSkeleton v-if="windowIframeIsLoad" height="100%" width="100%" />
|
||||
<iframe v-show="!windowIframeIsLoad" id="windowIframeId" ref="windowIframeRef" :src="windowSrc" class="w-full h-full" frameborder="0" @load="handWindowIframeIdLoad" />
|
||||
<iframe
|
||||
v-show="!windowIframeIsLoad" id="windowIframeId" ref="windowIframeRef" :src="windowSrc"
|
||||
class="w-full h-full" frameborder="0" @load="handWindowIframeIdLoad"
|
||||
/>
|
||||
</div>
|
||||
</NModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
body,html{
|
||||
body,
|
||||
html {
|
||||
overflow: hidden;
|
||||
background-color: rgb(54, 54, 54);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.sun-main{
|
||||
.mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sun-main {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.cover{
|
||||
position:absolute;
|
||||
width:100%;
|
||||
height:100%;
|
||||
.cover {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
/* background: url(@/assets/start_sky.jpg) no-repeat; */
|
||||
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.text-shadow{
|
||||
.text-shadow {
|
||||
text-shadow: 2px 2px 50px rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
.app-icon-text-shadow{
|
||||
.app-icon-text-shadow {
|
||||
text-shadow: 2px 2px 5px rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
.fixed-element {
|
||||
position: fixed; /* 将元素固定在屏幕上 */
|
||||
right: 30px; /* 距离屏幕顶部的距离 */
|
||||
bottom: 50px; /* 距离屏幕左侧的距离 */
|
||||
position: fixed;
|
||||
/* 将元素固定在屏幕上 */
|
||||
right: 10px;
|
||||
/* 距离屏幕顶部的距离 */
|
||||
bottom: 50px;
|
||||
/* 距离屏幕左侧的距离 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,7 +10,7 @@ const userStore = useUserStore()
|
||||
const authStore = useAuthStore()
|
||||
const ms = useMessage()
|
||||
const isShowCaptcha = ref<boolean>(false)
|
||||
const isShowRegister = ref<boolean>(false)
|
||||
// const isShowRegister = ref<boolean>(false)
|
||||
|
||||
const captchaRef = ref()
|
||||
|
||||
@@ -42,13 +42,13 @@ function handleSubmit() {
|
||||
|
||||
<template>
|
||||
<div class="login-container">
|
||||
<NCard class="login-card">
|
||||
<div class="login-title">
|
||||
<NGradientText :size="30" type="success">
|
||||
<NCard class="login-card" style="border-radius: 20px;">
|
||||
<div class="login-title ">
|
||||
<NGradientText :size="30" type="success" class="!font-bold">
|
||||
{{ $t('common.appName') }}
|
||||
</NGradientText>
|
||||
</div>
|
||||
<NForm :model="form" label-width="100px">
|
||||
<NForm :model="form" label-width="100px" @keydown.enter="handleSubmit">
|
||||
<NFormItem>
|
||||
<NInput v-model:value="form.username" placeholder="请输入邮箱地址作为账号">
|
||||
<template #prefix>
|
||||
@@ -77,13 +77,17 @@ function handleSubmit() {
|
||||
</NButton>
|
||||
</NFormItem>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<!-- <div class="flex justify-end">
|
||||
<NButton v-if="isShowRegister" quaternary type="info" class="flex" @click="$router.push({ path: '/register' })">
|
||||
注册
|
||||
</NButton>
|
||||
<!-- <NButton quaternary type="info" class="flex" @click="$router.push({ path: '/resetPassword' })">
|
||||
<NButton quaternary type="info" class="flex" @click="$router.push({ path: '/resetPassword' })">
|
||||
忘记密码?
|
||||
</NButton> -->
|
||||
</NButton>
|
||||
</div> -->
|
||||
|
||||
<div class="flex justify-center text-slate-300">
|
||||
Powered By <a href="https://github.com/hslr-s/sun-panel" target="_blank" class="ml-[5px] text-slate-500">Sun-Panel</a>
|
||||
</div>
|
||||
</NForm>
|
||||
</NCard>
|
||||
|
||||
Reference in New Issue
Block a user