首页+登录页

This commit is contained in:
2025-08-07 16:39:37 +08:00
commit a161520e7b
60 changed files with 5456 additions and 0 deletions

240
src/views/Home.vue Normal file
View File

@ -0,0 +1,240 @@
<template>
<div class="home-page" :style="contentStyle">
<d-layout>
<d-content class="main-content">
<div class="pt-8 px-12">
<d-input class="devui-input-demo__mt" size="lg" v-model="searchWord" @keyup.enter="search" placeholder="请输入">
<template #prepend>
<d-select class="w-48" size="lg" v-model="broswer" :options="options"></d-select>
</template>
<template #append>
<d-icon name="search" style="font-size: inherit;" @click="search" />
</template>
</d-input>
</div>
<!-- 图片网格展示区域 -->
<PerfectScrollbar class="" :style="navStyle">
<div class="navcard grid-cols-4 gap-6 p-12">
<d-card class="bg-[white] h-25" v-for="(item, index) in navlist" :key="index"
@click="goExtra(item.menu_link)">
<template #content>
<div class="mt-2 w-full flex flex-col items-center cursor-pointer">
<div :style="{ background: item.color }"
class="w-8 h-8 rounded-full text-white flex items-center justify-center" v-if="item.icon_error">
{{ item.first }}</div>
<img v-else width="32" :src="item.menu_icon" @error="imgErr(index)" class="grid-image" />
<div class="mt-1 w-full text-center">{{ item.menu_name || "" }}</div>
</div>
</template>
</d-card>
<d-card class="bg-[white] h-25">
<div @click="addNav" class="w-full h-full flex flex-col items-center justify-center cursor-pointer">
<div :style="{ background: getRandomDarkColor() }"
class="w-12 h-12 rounded-full text-2xl text-white flex items-center justify-center">
+
</div>
</div>
</d-card>
</div>
</PerfectScrollbar>
</d-content>
<d-aside class="daside w-120">
<homeSide></homeSide>
</d-aside>
</d-layout>
<!-- 新增导航弹窗 -->
<d-modal class="!w-120" v-model="visible" title="新增导航">
<d-form ref="formNav" layout="vertical" :data="navData">
<d-form-item field="username">
<d-input @blur="getIcon" v-model="navData.menu_link" placeholder="请输入单行链接(必填)" />
</d-form-item>
<d-form-item field="password">
<d-input v-model="navData.menu_name" placeholder="请输入导航名称(必填)" />
</d-form-item>
<d-form-item class="form-operation-wrap">
<div class="flex">
<d-input v-model="navData.menu_icon" placeholder="请输入图标链接" />
<img class="ml-5" v-if="navData.menu_icon" width="30" height="30" :src="navData.menu_icon" alt="">
<div v-else class="ml-5 w-[30px] h-[30px]"></div>
</div>
</d-form-item>
</d-form>
<div class="mt-10 w-full flex justify-between">
<d-button @click="navCancel" variant="text" class="w-[49%] hover:bg-[#8a6684] hover:!text-white">取消</d-button>
<span class="text-[20px]"> | </span>
<d-button @click="navSubmit" variant="text" class="w-[49%] hover:bg-[#5c866a] hover:!text-white"
color="primary">确定</d-button>
</div>
</d-modal>
</div>
</template>
<script setup lang="ts">
import homeSide from '@/components/homeSide.vue'
import { getDictValue } from '@/util/index.ts'
// 新增导航弹窗
const visible: any = ref(false)
const navData: any = reactive({
menu_link: '',
menu_name: '',
menu_icon: ''
})
const formNav: any = ref(null)
// 首页逻辑
const nav: any = $store.nav.useNavStore()
const contentStyle: any = ref({})
const navStyle: any = ref({})
const searchWord: any = ref('')
const broswer: any = ref('bing')
const options = ref([
{
name: '必应',
value: 'bing',
url: 'https://cn.bing.com/search?q='
},
{
name: '百度',
value: 'baidu',
url: 'https://www.baidu.com/s?wd='
},
{
name: '谷歌',
value: 'google',
url: 'https://www.google.com/search?q='
},
{
name: '翻译',
value: 'trans',
url: 'https://translate.volcengine.com?text='
},
])
// 图片数据
const navlist: any = ref([])
async function getNavList() {
const res = await $http.nav.getNavList()
res.data.forEach((i: any) => {
i.icon_error = false
i.first = i.menu_name.at(0)
i.color = getRandomDarkColor()
});
navlist.value = res.data
console.log("&&&&&&&&&&&&&&", navlist.value);
}
function imgErr(index: number) {
navlist.value[index].icon_error = true
}
function goExtra(link: string) {
window.open(link, "_BLANK")
}
function search() {
const res = getDictValue(options.value, "value", broswer.value, "url")
if (res) {
window.open(res + searchWord.value, "_BLANK")
}
}
const getRandomDarkColor = (min: number = 30, max: number = 128): string => {
// 确保最大值不超过255且最小值不小于0
min = Math.max(0, min);
max = Math.min(255, max);
// 确保最大值大于最小值
if (max <= min) {
max = min + 30;
}
// 生成随机RGB值 (深色)
const r = Math.floor(Math.random() * (max - min + 1)) + min;
const g = Math.floor(Math.random() * (max - min + 1)) + min;
const b = Math.floor(Math.random() * (max - min + 1)) + min;
// 转换为十六进制
const toHex = (c: number) => {
const hex = c.toString(16);
return hex.length === 1 ? '0' + hex : hex;
};
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}
function addNav() {
if (!$cookies.get("token")) {
$msg.error("请先登录");
return
}
visible.value = true
}
async function getIcon() {
if (!navData.menu_link) return
const res = await $http.mix.getIcon({
url: navData.menu_link
})
console.log('>>> --> getIcon --> res:', res)
if (res.code == 200) {
navData.menu_icon = res.data.url
}
}
function navCancel() {
visible.value = false
formNav.value.resetFields()
}
async function navSubmit() {
console.log('>>> --> navSubmit --> navData:', navData)
if (!navData.menu_link || !navData.menu_name) {
$msg.error('请输入链接和名称')
return
}
const res = await $http.nav.addNav(navData)
console.log('>>> --> navSubmit --> res:', res)
if (res.code == 200) {
$msg.success('添加成功')
visible.value = false
formNav.value.resetFields()
getNavList()
}
}
onMounted(() => {
// console.log("&&&&&&&&&&&&&&", nav.navH);
contentStyle.value = {
height: `calc(100vh - ${nav.navH}px)`
}
navStyle.value = {
height: `calc(100vh - ${nav.navH}px - 110px)`
}
getNavList()
})
</script>
<style scoped lang="less">
/* 首页样式 */
.navcard {
display: grid;
}
:deep(.devui-input-slot__prepend) {
width: 90px;
}
</style>