初始化
This commit is contained in:
113
components/AppFooter.vue
Normal file
113
components/AppFooter.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<footer class="overflow-hidden pt-4">
|
||||
<!-- 间隔 -->
|
||||
|
||||
<div class="sm:h-[3px] h-[15px]" v-if="getPath().startsWith('/passport/login')"></div>
|
||||
<div class="sm:h-[30px] h-[15px]" v-else></div>
|
||||
|
||||
|
||||
<div class=" bg-white my-3 w-3/4 m-auto" v-if="config.copyrightForDemoData">
|
||||
<el-alert :title="config.copyrightForDemoData" center show-icon type="warning" />
|
||||
</div>
|
||||
<div class="w-full flex flex-col sm:bg-black justify-between">
|
||||
<!-- PC版 -->
|
||||
<div class="sub-menu w-full xl:w-screen-xl xl:px-0 px-4 m-auto flex justify-between py-10 text-center hidden-sm-and-down">
|
||||
|
||||
<!-- 底部菜单 -->
|
||||
<div class="left flex flex-col sm:flex-row sm:flex-wrap justify-between gap-3xl sm:w-9/12 flex-wrap w-full">
|
||||
<template v-for="item in subMenu">
|
||||
<div class="sub-menu-item text-left">
|
||||
<div class="pb-4">
|
||||
<text class="text-gray-400 hover:text-gray-300 font-bold text-[16px]">{{ item.title }}</text>
|
||||
</div>
|
||||
<template v-if="item.children">
|
||||
<div class="sub-menu-children flex flex-col gap-xs">
|
||||
<template v-for="sub in item.children">
|
||||
<a @click="openSpmUrl(`${sub.path}`,sub,sub.navigationId)" class="text-gray-400 hover:text-gray-300 cursor-pointer">{{ sub.title }}</a>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 关注我们 -->
|
||||
<div class="right w-3/12 pr-3 text-right flex justify-end" v-if="config.wxQrcode">
|
||||
<div class="qrcode flex flex-col items-center">
|
||||
<el-image :src="config.wxQrcode" class="w-[100px]" />
|
||||
<text class="text-gray-400 py-2">{{ config.wxQrcodeText }}</text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex pt-1 w-auto sm:hidden px-4 sm:px-0">
|
||||
<el-divider><span class="text-gray-400">Footer</span></el-divider>
|
||||
</div>
|
||||
<div class="mx-3 sm:hidden p-5 bg-white rounded-xl">
|
||||
<el-collapse v-model="activeNames" accordion>
|
||||
<template v-for="(item,index) in subMenu">
|
||||
<el-collapse-item :title="item.title" :name="index">
|
||||
<template v-if="item.children">
|
||||
<template v-for="sub in item.children">
|
||||
<a :href="sub.path" class="block cursor-pointer text-gray-600 hover:text-gray-900">{{ sub.title }}</a>
|
||||
</template>
|
||||
</template>
|
||||
</el-collapse-item>
|
||||
</template>
|
||||
</el-collapse>
|
||||
</div>
|
||||
|
||||
<!-- <div class="sm:hidden p-4 mt-3">-->
|
||||
<!-- <el-button type="primary" class="w-full" size="large">联系我们</el-button>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- 版权信息 -->
|
||||
<div class="w-full xl:w-screen-xl xl:px-0 px-4 w-full m-auto flex sm:flex-row flex-col-reverse sm:justify-between justify-center items-center sm:py-10 pt-6 pb-6 text-center">
|
||||
<div class="text-gray-400 sm:gap-xl leading-7 flex flex-col sm:flex-row">
|
||||
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
|
||||
<a class="text-gray-400 hover:text-gray-400" href="https://beian.miit.gov.cn/" target="_blank"> 备案号:{{ config?.icpNo }}</a>
|
||||
</div>
|
||||
<div class="tools flex gap-xl items-center opacity-80 hover:opacity-90 text-gray-400 text-sm">
|
||||
Powered by <a rel="nofollow" href="https://website.websoft.top" target="_blank" class="text-gray-400 hover:text-gray-50">云·企业官网</a>
|
||||
<!-- <el-tooltip :content="`管理后台`" v-if="config.showAdminIcon">-->
|
||||
<!-- <a :href="`https://${website.tenantId}.websoft.top`" target="_blank"><img src="@/assets/svg/websoft-mark-white.svg" alt="github" width="28" class="text-gray-400" /></a>-->
|
||||
<!-- </el-tooltip>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<footer class="absolute bottom-0 w-full" v-if="getPath().startsWith('/login')">
|
||||
<div class="w-full xl:w-screen-xl xl:p-0 px-4 w-full m-auto flex sm:flex-row flex-col-reverse sm:justify-center justify-center items-center sm:py-10 pt-6 pb-6 text-center">
|
||||
<div class="text-gray-400 sm:gap-xl leading-7 flex flex-col sm:flex-row">
|
||||
<span>Copyright © {{ new Date().getFullYear() }} {{ config?.copyright }}</span>
|
||||
<a class="text-gray-400 hover:text-gray-400" href="https://beian.miit.gov.cn/" target="_blank"> 备案号:{{ config?.icpNo }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<el-backtop></el-backtop>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 请求数据
|
||||
import {getPath, openSpmUrl, useConfigInfo} from "#imports";
|
||||
import {useSubMenu, useWebsite} from "~/composables/configState";
|
||||
import { StarFilled } from '@element-plus/icons-vue'
|
||||
|
||||
const config = useConfigInfo();
|
||||
const website = useWebsite();
|
||||
const subMenu = useSubMenu();
|
||||
console.log('---------config---------',config.value)
|
||||
console.log('---------website---------',website.value)
|
||||
console.log('---------subMenu---------',subMenu.value)
|
||||
|
||||
const activeNames = ref(['1'])
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.el-divider__text{
|
||||
display: block;
|
||||
margin-top: -1px;
|
||||
--el-bg-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
233
components/AppHeader.vue
Normal file
233
components/AppHeader.vue
Normal file
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<el-affix :offset="0" @change="onAffix">
|
||||
<header
|
||||
class="header z-100 top-0 w-full bg-white/75 opacity-90 backdrop-blur border-b border-gray-200 dark:border-gray-800 -mb-px sticky top-0 z-50 lg:mb-0 lg:border-0"
|
||||
:class="affix ? 'absolute blur-xs' : 'sticky bg-white/75'">
|
||||
<div class="xl:w-screen-xl xl:p-0 px-4 flex items-center between w-full m-auto">
|
||||
<div class="header___left flex items-center">
|
||||
<div class="logo mt-1 sm:w-[150px] h-7 w-auto py-2 flex items-center">
|
||||
<nuxt-link v-if="config?.siteLogo" to="/">
|
||||
<div class="flex flex-col text-center xl:p-0">
|
||||
<el-image
|
||||
:src="config.siteLogo"
|
||||
shape="square"
|
||||
fit="fill"
|
||||
class="lg:h-7 lg:w-auto pb-2 h-5 w-[90px] h-[28px]"
|
||||
:alt="config.siteName"
|
||||
:title="config.siteName"
|
||||
/>
|
||||
<!-- <span class="text-gray-500 text-2.5" style="line-height: 1rem">云应用开发平台</span>-->
|
||||
</div>
|
||||
|
||||
</nuxt-link>
|
||||
<nuxt-link v-else to="/">
|
||||
<text>{{ config?.siteName }}</text>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="hidden sm:flex">
|
||||
<el-menu
|
||||
:default-active="currentIndex"
|
||||
mode="horizontal"
|
||||
background-color="transparent"
|
||||
:collapse="true"
|
||||
:ellipsis="false"
|
||||
style="border: none"
|
||||
>
|
||||
<template v-for="(item, index) in navigations">
|
||||
<el-sub-menu v-if="item?.children && item.children.length > 0" :index="`${item.path}`">
|
||||
<template #title><h3 @click="openSpmUrl(`${item.path}`,item,item.navigationId)">{{ item.title }}</h3></template>
|
||||
<el-menu-item v-for="(sub,subIndex) in item.children" :index="`${sub.path}`">
|
||||
<el-space @click="openSpmUrl(`${item.path}`,sub,sub.navigationId)">
|
||||
<el-avatar :src="sub.icon" shape="square" size="small"></el-avatar>
|
||||
<span class="font-bold">{{ sub.title }}</span>
|
||||
</el-space>
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else :index="`${item.path}`"><h3 @click="openSpmUrl(`${item.path}`,item,item.navigationId)">{{ item.title }}</h3></el-menu-item>
|
||||
</template>
|
||||
<!-- <el-menu-item v-if="user?.certification">-->
|
||||
<!-- <el-space @click="loginDeveloperCenterByToken({domain:'developer.websoft.top'})">-->
|
||||
<!-- <h3>开发者中心</h3>-->
|
||||
<!-- </el-space>-->
|
||||
<!-- </el-menu-item>-->
|
||||
<!-- <el-menu-item v-else>-->
|
||||
<!-- <el-space @click="openSpmUrl(`https://developer.websoft.top`)">-->
|
||||
<!-- <h3>开发者中心</h3>-->
|
||||
<!-- </el-space>-->
|
||||
<!-- </el-menu-item>-->
|
||||
</el-menu>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header__right items-center pr-4 xl:pr-0 md:flex hidden">
|
||||
<el-space class="sm:flex hidden" size="large" v-if="config.showSearchTools">
|
||||
<el-button v-if="token" @click="loginAdminByToken">控制台</el-button>
|
||||
<!-- <el-button v-if="token" @click="navigateTo(`/manage`)">控制台</el-button>-->
|
||||
<!-- <el-button v-if="config.showSearchIcon" circle :icon="ElIconSearch" @click="navigateTo('/search')"></el-button>-->
|
||||
<!-- <el-button v-if="token" @click="loginAdminByToken">控制台</el-button>-->
|
||||
<ClientOnly>
|
||||
<template v-if="token">
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-space class="flex items-center cursor-pointer">
|
||||
<el-avatar class="cursor-pointer" :src="user?.logo" :size="30" />
|
||||
<span>{{ user?.tenantName }}</span>
|
||||
</el-space>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="user" @click="openSpmUrl(`/user`)">账户中心</el-dropdown-item>
|
||||
<el-dropdown-item command="password" @click="openSpmUrl(`/user/password`)">修改密码</el-dropdown-item>
|
||||
<el-dropdown-item command="auth" @click="openSpmUrl(`/user/auth`)">实名认证</el-dropdown-item>
|
||||
<el-dropdown-item command="order" @click="openSpmUrl(`/user/order`)">我的订单</el-dropdown-item>
|
||||
<!-- <el-dropdown-item divided command="order" @click="loginDeveloperCenterByToken({domain:'developer.websoft.top'})">开发者中心</el-dropdown-item>-->
|
||||
<el-dropdown-item divided command="logOut" @click="openSpmUrl('/user/logout')">退出登录
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button type="primary" v-if="!token" @click="navigateTo(`/passport/login`)">登录/注册</el-button>
|
||||
<!-- <el-button v-if="config.showLoginButton" circle :icon="ElIconUserFilled" @click="goLogin"></el-button>-->
|
||||
</template>
|
||||
</ClientOnly>
|
||||
</el-space>
|
||||
</div>
|
||||
<!-- 移动端菜单 -->
|
||||
<div class="sm:hidden flex xl:p-0 px-8">
|
||||
<el-dropdown>
|
||||
<el-space class="el-dropdown-link flex items-center">
|
||||
<el-avatar v-if="token" class="cursor-pointer" :src="user?.avatar" :size="30"/>
|
||||
<el-button v-else :icon="ElIconMenu"></el-button>
|
||||
</el-space>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<template v-for="(item, index) in navigations">
|
||||
<el-dropdown-item>
|
||||
<span @click="openSpmUrl(`${item.path}`,item,item.navigationId)">{{ item.title }}</span>
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
<template v-if="token">
|
||||
<el-dropdown-item divided @click="loginDeveloperCenterByToken">开发者中心</el-dropdown-item>
|
||||
<!-- <el-dropdown-item divided command="user" @click="navigateTo(`/user`)">账户中心</el-dropdown-item>-->
|
||||
<el-dropdown-item divided command="logOut" @click="navigateTo('/user/logout')">退出</el-dropdown-item>
|
||||
</template>
|
||||
<el-dropdown-item v-if="!token" divided @click="navigateTo(`/passport/login`)">登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<Passport :visible="showPassport" @done="reload"/>
|
||||
</header>
|
||||
<div class="header__height__placeholder"></div>
|
||||
</el-affix>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 引入所需的图标
|
||||
import {
|
||||
useCompany,
|
||||
useConfigInfo,
|
||||
useMenu,
|
||||
useProductAffix,
|
||||
useSysDomain,
|
||||
useToken,
|
||||
useUser
|
||||
} from "~/composables/configState";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {User} from "~/api/system/user/model";
|
||||
import {navigateTo, openSpmUrl} from "#imports";
|
||||
import type {Company} from "~/api/system/company/model";
|
||||
import {loginAdminByToken} from "~/utils/common";
|
||||
|
||||
const route = useRoute();
|
||||
// 配置信息
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const showPassport = ref<boolean>(false);
|
||||
const token = useToken();
|
||||
const user = useUser();
|
||||
const company = useCompany();
|
||||
const navigations = useMenu();
|
||||
const config = useConfigInfo();
|
||||
const affix = useProductAffix();
|
||||
const sysDomain = useSysDomain();
|
||||
|
||||
// 顶部栏初始数
|
||||
const visibleNumber = ref<number>(6);
|
||||
config.value.elMenuMaxNumber = 8;
|
||||
// 当前激活菜单的 index
|
||||
const currentIndex = ref<string>('/');
|
||||
|
||||
function handleCommand(command: string) {
|
||||
switch (command) {
|
||||
case 'logOut':
|
||||
logOut();
|
||||
break;
|
||||
default:
|
||||
navigateTo('/user');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const onAffix = (index: boolean) => {
|
||||
affix.value = index;
|
||||
}
|
||||
|
||||
function logOut() {
|
||||
token.value = ''
|
||||
localStorage.clear();
|
||||
navigateTo('/passport/login')
|
||||
}
|
||||
|
||||
function goLogin() {
|
||||
navigateTo('/passport/login')
|
||||
}
|
||||
|
||||
function handleSelect(key: string, keyPath: any) {
|
||||
currentIndex.value = key;
|
||||
navigateTo(`${key}`);
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
const token = localStorage.getItem('token');
|
||||
// const domain = localStorage.getItem('SysDomain');
|
||||
// if (domain) {
|
||||
// sysDomain.value = domain;
|
||||
// }
|
||||
// 获取用户信息
|
||||
if (token && token != '') {
|
||||
const {data: response} = await useServerRequest<ApiResult<User>>('/auth/user', {baseURL: runtimeConfig.public.apiServer})
|
||||
if (response.value?.data) {
|
||||
user.value = response.value?.data;
|
||||
}
|
||||
// 获取企业信息
|
||||
const {data: companyInfo} = await useServerRequest<ApiResult<Company>>('/system/company/profile', {baseURL: runtimeConfig.public.apiServer})
|
||||
if(companyInfo.value?.data){
|
||||
company.value = companyInfo.value?.data;
|
||||
// sysDomain.value = company.value?.domain || undefined;
|
||||
// user.value.tenantName = company.value?.tenantName;
|
||||
// localStorage.setItem('SysDomain', `${company.value?.domain}`);
|
||||
localStorage.setItem('CompanyLogo', `${company.value?.companyLogo}`)
|
||||
localStorage.setItem('TenantName', `${company.value?.tenantName}`)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
reload();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
body {
|
||||
background-color: #f3f6f8;
|
||||
}
|
||||
|
||||
.el-menu-item:visited, .el-menu-item:hover {
|
||||
background: none !important; /* 你可以根据需要设置不同的颜色 */
|
||||
transition: background-color 0.3s linear;
|
||||
}
|
||||
|
||||
.is-active:hover {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
</style>
|
||||
38
components/Banner.vue
Normal file
38
components/Banner.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="banner m-auto relative sm:flex">
|
||||
<template v-if="layout && layout.showBanner">
|
||||
<el-image :src="layout?.photo || config.subpageBanner" :class="layout?.style" class="sm:h-auto"></el-image>
|
||||
<div class="banner-bar absolute top-0 w-full sm:flex hidden">
|
||||
<div class="banner-text py-12 md:w-screen-xl m-auto opacity-90 flex flex-col justify-center">
|
||||
<div class="keywords my-4 text-3xl">{{ layout?.name }}</div>
|
||||
<div class="description mb-4 mt-1 text-xl max-w-3xl text-gray-600">{{ layout?.description }}</div>
|
||||
<div class="buy-btn">
|
||||
<el-button v-if="layout.demoUrl" @click="openSpmUrl(layout.demoUrl)" type="primary">演示地址</el-button>
|
||||
<el-button v-if="token && layout.buyUrl" type="warning" @click="openSpmUrl(`https://${sysDomain}/token-login`)">产品控制台</el-button>
|
||||
<el-button v-if="!token" type="warning" @click="navigateTo(`/passport/login`)">立即开通</el-button>
|
||||
<el-button v-if="layout.docUrl" @click="openSpmUrl(layout.docUrl)">产品文档</el-button>
|
||||
</div>
|
||||
<div class="demo-account mt-3 text-blue-7" v-if="layout?.account">账号密码:{{ layout.account }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo} from "~/composables/configState";
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
|
||||
const token = useToken();
|
||||
const sysDomain = useSysDomain();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
layout?: any;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const config = useConfigInfo();
|
||||
|
||||
</script>
|
||||
29
components/Breadcrumb.vue
Normal file
29
components/Breadcrumb.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="sm:py-4 sm:px-0 mx-3 py-2">
|
||||
<el-breadcrumb :separator-icon="ArrowRight">
|
||||
<el-breadcrumb-item :to="{ path: '/' }">
|
||||
<el-icon class="cursor-pointer">
|
||||
<ElIconHouse/>
|
||||
</el-icon>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-if="data?.parentName">
|
||||
<span class="cursor-pointer hover:font-bold" @click="openSpmUrl(`/product`,data,data.categoryId)">{{ data.parentName }}</span>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-if="data?.categoryName">{{ title ? title : data.categoryName }}</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-if="title">{{ title }}</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ArrowRight} from '@element-plus/icons-vue'
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
data?: any;
|
||||
title?: string;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
</script>
|
||||
44
components/CardList.vue
Normal file
44
components/CardList.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50">
|
||||
<el-image :src="item.image" fit="contain" :lazy="true" class="w-full h-[150px] cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId)" />
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<p class="text-gray-900 dark:text-white text-base font-semibold flex items-center gap-1.5">
|
||||
<a class="flex-1 cursor-pointer" @click="openSpmUrl(`/detail`,item,item.articleId)">{{ item.title }}</a>
|
||||
</p>
|
||||
<div class="text-[15px] text-gray-500 dark:text-gray-400 mt-1 line-clamp-2 sm:min-h-[45px]">
|
||||
{{ item.comments }}
|
||||
</div>
|
||||
<div class="button-group flex justify-center mt-3">
|
||||
<el-button class="w-full" :icon="ElIconView">演示</el-button>
|
||||
<el-button class="w-full" :icon="ElIconShoppingCart">购买</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {useConfigInfo} from "~/composables/configState";
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
|
||||
const token = useToken();
|
||||
const sysDomain = useSysDomain();
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
layout?: any;
|
||||
list?: any[];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const config = useConfigInfo();
|
||||
|
||||
</script>
|
||||
102
components/CompanyList.vue
Normal file
102
components/CompanyList.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div v-if="title" class="text-center flex flex-col items-center py-12 z-100 relative">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
{{ comments }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" :xs="24" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/item`, item,item.companyId,true)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
:fit="fit" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.shortName }}
|
||||
<el-tag v-if="item.chargingMethod === 0" size="small" type="success" class="text-white ml-2" effect="dark">免费</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-2">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<el-space class="flex items-end">
|
||||
<el-avatar size="small" :src="item.companyLogo" />
|
||||
<span class="text-gray-400 line-clamp-1 pr-2">{{ item.companyName }}</span>
|
||||
</el-space>
|
||||
<template v-if="item.isBuy">
|
||||
<el-button v-if="item.installed" type="primary" @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>
|
||||
<el-button v-else type="primary" @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button v-if="item.chargingMethod == 0" type="primary" @click.stop="loginDeveloperCenterByToken(item)">控制台</el-button>
|
||||
<el-button v-else type="warning" @click.stop="openSpmUrl(`/product/create`,item,item.companyId,true)">立即开通
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- <el-button type="warning" v-if="item.price" @click.stop="openSpmUrl(`/product/create`,item,item.companyId,true)">-->
|
||||
<!-- <div class="flex items-center">-->
|
||||
<!-- <span>¥{{ item.price * 0.1 }}</span>-->
|
||||
<!-- <span v-if="item.chargingMethod == 2">/年</span>-->
|
||||
<!-- <span v-if="item.chargingMethod == 3">/月</span>-->
|
||||
<!-- <span v-if="item.chargingMethod == 4">/天</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-button>-->
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
<!-- <div class="more flex justify-center w-full"><el-button>查看更多</el-button></div>-->
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {loginAdminByToken, loginByToken, loginDeveloperCenterByToken, openSpmUrl} from "~/utils/common";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Company, CompanyParam} from "~/api/system/company/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
param?: CompanyParam;
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
comments?: string;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const list = ref<Company[]>([]);
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Company>>>('/system/company/page', {
|
||||
params: props.param
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
180
components/PageBanner.vue
Normal file
180
components/PageBanner.vue
Normal file
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<div :class="form?.style" class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto xl:p-0 xl:px-0 px-4">
|
||||
<!-- <Breadcrumb :title="form?.categoryName" />-->
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 grid lg:grid-cols-2 lg:items-center">
|
||||
<div class="flex">
|
||||
<el-avatar v-if="avatar" :src="avatar" :size="120"></el-avatar>
|
||||
<div class="title-bar">
|
||||
<h1 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
<span>{{ title }}</span>
|
||||
</h1>
|
||||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400">
|
||||
{{ desc }}
|
||||
</div>
|
||||
<div class="flex justify-between w-full items-center mt-4" v-if="showSearch">
|
||||
<el-space>
|
||||
<div class="w-[500px] pr-10">
|
||||
<el-input
|
||||
v-model="where.keywords"
|
||||
placeholder="搜索"
|
||||
:prefix-icon="Search"
|
||||
@keydown.enter="handleClick"
|
||||
>
|
||||
<template #append>
|
||||
<el-button size="large" type="primary" @click="handleClick">搜索</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-space>
|
||||
<el-space>
|
||||
<el-button size="large">我的收藏</el-button>
|
||||
<el-button size="large">热门推荐</el-button>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 密码可见 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="请输入查看密码"
|
||||
:show-close="false"
|
||||
:close-on-click-modal="false"
|
||||
width="400"
|
||||
>
|
||||
<el-input type="password" v-model="password2" show-password placeholder="请输入查看密码">请输入查看密码</el-input>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="checkPassword">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {ref} from 'vue'
|
||||
import {Search} from '@element-plus/icons-vue'
|
||||
import type {ProductParam} from "~/api/oa/product/model";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
form?: any;
|
||||
layout?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
avatar?: string;
|
||||
showSearch?: boolean;
|
||||
}>(),
|
||||
{
|
||||
title: 'PageName',
|
||||
desc: 'Description',
|
||||
demoUrl: '/product',
|
||||
buyUrl: '/buy'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', show: boolean): void;
|
||||
(e: 'search', where: ProductParam): void;
|
||||
}>();
|
||||
|
||||
const route = useRoute();
|
||||
const token = useToken();
|
||||
const form = ref<Navigation>();
|
||||
const layout = ref<any>();
|
||||
const password2 = ref('');
|
||||
// 密码弹窗
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<ProductParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
// 验证密码
|
||||
const checkPassword = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<unknown>>('/cms/cms-navigation/checkNavigationPassword', {
|
||||
query: {
|
||||
password: form.value?.password,
|
||||
password2: password2.value
|
||||
}
|
||||
})
|
||||
if (response.value?.code === 0) {
|
||||
dialogVisible.value = false;
|
||||
emit('done', true);
|
||||
console.log(response.value.message);
|
||||
} else {
|
||||
ElMessage.error(response.value?.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索结果
|
||||
const handleClick = async () => {
|
||||
emit('search',where);
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
// 校验密码
|
||||
const {data: nav} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath', {
|
||||
query: {
|
||||
path: route.path
|
||||
}
|
||||
})
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value?.data;
|
||||
console.log('PageBanner.vue => ', form.value)
|
||||
// 页面布局
|
||||
if (form.value?.layout) {
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
// 允许直接访问
|
||||
if (form.value?.permission === 0) {
|
||||
emit('done', true);
|
||||
}
|
||||
// 要求登录可见
|
||||
if (form.value?.permission === 1) {
|
||||
navigateTo(`/passport/login`);
|
||||
if (token && token.value.length > 0) {
|
||||
emit('done', true);
|
||||
}
|
||||
}
|
||||
// 要求密码可见
|
||||
if (form.value?.permission === 2) {
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
30
components/PageContainer.vue
Normal file
30
components/PageContainer.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div class="container md:w-screen-xl m-auto">
|
||||
<div class="flex flex-col" v-if="!layout?.showLayout">
|
||||
<Breadcrumb :data="form" />
|
||||
<div :class="layout?.style" class="page-main w-full bg-white rounded-lg">
|
||||
<div class="p-4 leading-7" v-html="form?.design?.content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空白页 -->
|
||||
<div class="mt-[60px]" v-if="!layout">
|
||||
<el-empty description="404 页面不存在"></el-empty>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Breadcrumb from "~/components/Breadcrumb.vue";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
layout?: any;
|
||||
form?: Navigation;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
</script>
|
||||
51
components/Passport.vue
Normal file
51
components/Passport.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<el-dialog v-model="showLogin" width="500" center>
|
||||
<el-segmented v-model="loginType" :options="options" block />
|
||||
<el-form :model="form" label-width="auto" style="max-width: 600px" class="p-4">
|
||||
<el-form-item label="登录账号">
|
||||
<el-input v-model="form.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="登录密码">
|
||||
<el-input v-model="form.name" type="password" />
|
||||
</el-form-item>
|
||||
<el-form-item label="记住密码">
|
||||
<el-switch v-model="form.delivery" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button >取消</el-button>
|
||||
<el-button type="primary" >
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useShowLogin, useToken} from "~/composables/configState";
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
region: '',
|
||||
date1: '',
|
||||
date2: '',
|
||||
delivery: false,
|
||||
type: [],
|
||||
resource: '',
|
||||
desc: '',
|
||||
})
|
||||
|
||||
const showLogin = useShowLogin();
|
||||
const loginType = ref('账号登录');
|
||||
const options = [
|
||||
'账号登录',
|
||||
'扫码登录',
|
||||
]
|
||||
const token = useToken();
|
||||
</script>
|
||||
94
components/ProductList.vue
Normal file
94
components/ProductList.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div class="text-center flex flex-col items-center py-12 z-100 relative">
|
||||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="sub-title">
|
||||
<p class="text-gray-500 dark:text-gray-400 py-3">
|
||||
{{ comments }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:w-screen-xl sm:flex xl:p-0 p-4 m-auto relative">
|
||||
<el-row :gutter="24" class="flex">
|
||||
<template v-for="(item,index) in list" :key="index">
|
||||
<el-col :span="6" :xs="24" class="mb-5 min-w-xs">
|
||||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`/item`, item,item.productId,true)">
|
||||
<el-image
|
||||
:src="item.image"
|
||||
:fit="fit" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer bg-gray-50"/>
|
||||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
|
||||
<div class="text-gray-700 dark:text-white text-base font-semibold flex flex-col gap-1.5">
|
||||
<div class="flex-1 text-xl cursor-pointer flex items-center">
|
||||
{{ item.title }}
|
||||
<el-tag v-if="item.tag == '1'" size="small" type="success" class="text-white ml-2" effect="dark">免费</el-tag>
|
||||
<el-tag v-if="item.tag == '2'" size="small" type="success" class="text-white ml-2" effect="dark">开源</el-tag>
|
||||
<el-tag v-if="item.tag == '3'" size="small" type="danger" class="text-white ml-2" effect="dark">付费</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 py-2 text-gray-500 justify-between">
|
||||
<div class="text-gray-500 line-clamp-3">{{ item.comments }}</div>
|
||||
</div>
|
||||
<div class="button-group flex justify-between items-center mt-3">
|
||||
<div class="flex items-end">
|
||||
<span class="text-red-500 text-xl">¥{{ item.price }}</span>
|
||||
<span v-if="item.durationMethod == 1" class="px-1 pb-1 text-xs text-gray-400">/年</span>
|
||||
<span v-if="item.durationMethod == 2" class="px-1 pb-1 text-xs text-gray-400">/月</span>
|
||||
<span v-if="item.durationMethod == 3" class="px-1 pb-1 text-xs text-gray-400">/日</span>
|
||||
</div>
|
||||
{{ item.chargingMethod }}
|
||||
<el-button type="warning" @click.stop="openSpmUrl(`/product/create`,item,item.productId,true)">立即开通
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
<!-- <div class="more flex justify-center w-full"><el-button>查看更多</el-button></div>-->
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-if="disabled" class="px-1 text-center text-gray-500 min-h-xs">
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult, PageResult} from "~/api";
|
||||
import type {Product, ProductParam} from "~/api/oa/product/model";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
param?: ProductParam;
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
comments?: string;
|
||||
fit?: any;
|
||||
}>(),
|
||||
{
|
||||
title: '卡片标题',
|
||||
comments: '卡片描述',
|
||||
fit: 'cover'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const list = ref<Product[]>([]);
|
||||
|
||||
// 请求数据
|
||||
const reload = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<PageResult<Product>>>('/cms/cms-product/page', {
|
||||
params: props.param
|
||||
})
|
||||
if (response.value?.data) {
|
||||
if (response.value?.data.list) {
|
||||
list.value = response.value?.data.list;
|
||||
}
|
||||
}
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
42
components/ProductShopInfo.vue
Normal file
42
components/ProductShopInfo.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="shopinfo px-3 sm:px-0">
|
||||
<div class="bg-white p-3 mt-3 flex items-center justify-between rounded-xl">
|
||||
<div class="left flex gap-xs items-center">
|
||||
<el-avatar shape="square" :src="data?.image" :size="55" />
|
||||
<div class="shop-name flex flex-col">
|
||||
<div class="shop-name cursor-pointer">
|
||||
<div class="flex items-center">
|
||||
<text class="text-xl">{{ data?.merchantName }}</text>
|
||||
<el-tag type="success" size="small" class="ml-2">官方</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<el-rate
|
||||
v-model="rate"
|
||||
show-score
|
||||
text-color="#ff9900"
|
||||
score-template="{value}">
|
||||
</el-rate>
|
||||
</div>
|
||||
</div>
|
||||
<el-button>进入店铺</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import type {Merchant} from "~/api/shop/merchant/model";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
data?: Merchant;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const rate = ref(5);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
24
components/UnderMaintenance.vue
Normal file
24
components/UnderMaintenance.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<el-card class="m-5 w-screen-sm mt-[60px] m-auto">
|
||||
<!-- 异常状态 -->
|
||||
<el-result
|
||||
:icon="website.statusIcon || 'info'"
|
||||
:title="`${website.statusName || '404'}`"
|
||||
:sub-title="website.statusText || '链接失败,请检查您的网络或与网站管理员联系'"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="primary" v-if="website.statusUrl" @click="navigateTo(`${website.statusUrl}`)">{{ website.statusBtnText }}</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {useWebsite} from "~/composables/configState";
|
||||
|
||||
const website = useWebsite()
|
||||
|
||||
const navigateTo = (url: string) => {
|
||||
window.location.href = url;
|
||||
}
|
||||
</script>
|
||||
61
components/Upload.vue
Normal file
61
components/Upload.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<el-upload action="#" list-type="picture-card" :auto-upload="false">
|
||||
<el-icon><Plus /></el-icon>
|
||||
|
||||
<template #file="{ file }">
|
||||
<div>
|
||||
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
|
||||
<span class="el-upload-list__item-actions">
|
||||
<span
|
||||
class="el-upload-list__item-preview"
|
||||
@click="handlePictureCardPreview(file)"
|
||||
>
|
||||
<el-icon><zoom-in /></el-icon>
|
||||
</span>
|
||||
<span
|
||||
v-if="!disabled"
|
||||
class="el-upload-list__item-delete"
|
||||
@click="handleDownload(file)"
|
||||
>
|
||||
<el-icon><Download /></el-icon>
|
||||
</span>
|
||||
<span
|
||||
v-if="!disabled"
|
||||
class="el-upload-list__item-delete"
|
||||
@click="handleRemove(file)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
|
||||
<el-dialog v-model="dialogVisible">
|
||||
<img :src="dialogImageUrl" alt="Preview Image" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue'
|
||||
|
||||
import type { UploadFile } from 'element-plus'
|
||||
|
||||
const dialogImageUrl = ref('')
|
||||
const dialogVisible = ref(false)
|
||||
const disabled = ref(false)
|
||||
|
||||
const handleRemove = (file: UploadFile) => {
|
||||
console.log(file)
|
||||
}
|
||||
|
||||
const handlePictureCardPreview = (file: UploadFile) => {
|
||||
dialogImageUrl.value = file.url!
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleDownload = (file: UploadFile) => {
|
||||
console.log(file)
|
||||
}
|
||||
</script>
|
||||
225
components/UserCard.vue
Normal file
225
components/UserCard.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<div :class="form?.style" class="banner m-auto relative sm:flex">
|
||||
<svg viewBox="0 0 1440 181" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
class="pointer-events-none absolute w-full top-[-2px] transition-all text-green-5 flex-shrink-0 opacity-100 duration-[400ms] opacity-80 -z-10">
|
||||
<mask id="path-1-inside-1_414_5526" fill="white">
|
||||
<path d="M0 0H1440V181H0V0Z"></path>
|
||||
</mask>
|
||||
<path d="M0 0H1440V181H0V0Z" fill="url(#paint0_linear_414_5526)" fill-opacity="0.22"></path>
|
||||
<path d="M0 2H1440V-2H0V2Z" fill="url(#paint1_linear_414_5526)" mask="url(#path-1-inside-1_414_5526)"></path>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_414_5526" x1="720" y1="0" x2="720" y2="181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_414_5526" x1="0" y1="90.5" x2="1440" y2="90.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="currentColor" stop-opacity="0"></stop>
|
||||
<stop offset="0.395" stop-color="currentColor"></stop>
|
||||
<stop offset="1" stop-color="currentColor" stop-opacity="0"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div class="md:w-screen-xl m-auto xl:p-0 px-4">
|
||||
<div class="py-8 sm:py-16" _path="/templates" _dir="" _draft="false" _partial="false" _locale=""
|
||||
_id="content:4.templates.yml" _type="yaml" _source="content" _file="4.templates.yml" _stem="4.templates"
|
||||
_extension="yml">
|
||||
<div class="gap-8 sm:gap-y-16 grid lg:grid-cols-2 lg:items-center">
|
||||
<el-space class="flex">
|
||||
<div class="mr-4" v-if="company.companyLogo">
|
||||
<el-avatar :src="company.companyLogo" :size="150"></el-avatar>
|
||||
</div>
|
||||
<div class="title-bar">
|
||||
<h1 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl">
|
||||
<span>{{ company.shortName }}</span>
|
||||
</h1>
|
||||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400">
|
||||
{{ company?.comments || desc }}
|
||||
</div>
|
||||
<el-space class="mt-4">
|
||||
<el-button
|
||||
:icon="ElIconPlus"
|
||||
@click="openSpmUrl(`http://git.gxwebsoft.com/gxwebsoft/websoft-cms.git`, {},0,true)"
|
||||
>
|
||||
关注
|
||||
</el-button>
|
||||
<!-- <el-tree-select-->
|
||||
<!-- v-model="where.categoryId"-->
|
||||
<!-- v-if="navigation?.length > 0"-->
|
||||
<!-- :data="navigation"-->
|
||||
<!-- placeholder="选择分类"-->
|
||||
<!-- :render-after-expand="false"-->
|
||||
<!-- style="width: 240px"-->
|
||||
<!-- />-->
|
||||
<div class="w-[300px]">
|
||||
<el-input
|
||||
v-model="where.keywords"
|
||||
placeholder="搜索"
|
||||
:prefix-icon="Search"
|
||||
@keydown.enter="reload"
|
||||
>
|
||||
<template #append>
|
||||
<el-button type="primary" @click="reload">搜索</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-space>
|
||||
</div>
|
||||
</el-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 密码可见 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="请输入查看密码"
|
||||
:show-close="false"
|
||||
:close-on-click-modal="false"
|
||||
width="400"
|
||||
>
|
||||
<el-input type="password" v-model="password2" show-password placeholder="请输入查看密码">请输入查看密码</el-input>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="checkPassword">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<!-- <el-space class="mt-4">-->
|
||||
<!-- <el-button-->
|
||||
<!-- :icon="ElIconBottom"-->
|
||||
<!-- @click="openSpmUrl(`http://git.gxwebsoft.com/gxwebsoft/websoft-cms.git`, {},0,true)"-->
|
||||
<!-- >-->
|
||||
<!-- 关注-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button-->
|
||||
<!-- :icon="ElIconMemo"-->
|
||||
<!-- @click="openSpmUrl(`/docs`)"-->
|
||||
<!-- >-->
|
||||
<!-- 选择分类-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <div class="w-[500px]">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="where.keywords"-->
|
||||
<!-- placeholder="搜索"-->
|
||||
<!-- :prefix-icon="Search"-->
|
||||
<!-- @keydown.enter="reload"-->
|
||||
<!-- >-->
|
||||
<!-- <template #append>-->
|
||||
<!-- <el-button type="primary" @click="reload">搜索</el-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-input>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-space>-->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {Search} from '@element-plus/icons-vue'
|
||||
import {openSpmUrl} from "~/utils/common";
|
||||
import {useServerRequest} from "~/composables/useServerRequest";
|
||||
import type {ApiResult} from "~/api";
|
||||
import type {Navigation} from "~/api/cms/navigation/model";
|
||||
import {ref} from 'vue'
|
||||
import type {CompanyParam} from "~/api/system/company/model";
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
form?: any;
|
||||
layout?: any;
|
||||
title?: string;
|
||||
desc?: string;
|
||||
buyUrl?: string;
|
||||
avatar?: string;
|
||||
}>(),
|
||||
{
|
||||
title: 'PageName',
|
||||
desc: 'Description',
|
||||
demoUrl: '/product',
|
||||
buyUrl: '/buy'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', show: boolean): void;
|
||||
}>();
|
||||
|
||||
const route = useRoute();
|
||||
const token = useToken();
|
||||
const form = ref<Navigation>();
|
||||
const layout = ref<any>();
|
||||
const password2 = ref('');
|
||||
// 密码弹窗
|
||||
const dialogVisible = ref(false)
|
||||
const company = useCompany();
|
||||
const navigation = ref<Navigation[]>()
|
||||
|
||||
// 搜索表单
|
||||
const where = reactive<CompanyParam>({
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
// 验证密码
|
||||
const checkPassword = async () => {
|
||||
const {data: response} = await useServerRequest<ApiResult<unknown>>('/cms/cms-navigation/checkNavigationPassword', {
|
||||
query: {
|
||||
password: form.value?.password,
|
||||
password2: password2.value
|
||||
}
|
||||
})
|
||||
if (response.value?.code === 0) {
|
||||
dialogVisible.value = false;
|
||||
emit('done', true);
|
||||
console.log(response.value.message);
|
||||
} else {
|
||||
ElMessage.error(response.value?.message);
|
||||
}
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
useClientRequest<ApiResult<Navigation[]>>('/cms/cms-navigation', {
|
||||
params: {
|
||||
userId: route.params.userId
|
||||
}
|
||||
}).then(res => {
|
||||
navigation.value = res?.data?.map(d => {
|
||||
return {
|
||||
label: d.title,
|
||||
value: d.navigationId,
|
||||
children: null
|
||||
}
|
||||
});
|
||||
console.log(navigation.value)
|
||||
})
|
||||
// 校验密码
|
||||
const {data: nav} = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath', {
|
||||
query: {
|
||||
path: route.path
|
||||
}
|
||||
})
|
||||
if (nav.value?.data) {
|
||||
form.value = nav.value?.data;
|
||||
console.log('PageBanner.vue => ', form.value)
|
||||
// 页面布局
|
||||
if (form.value?.layout) {
|
||||
layout.value = JSON.parse(form.value?.layout)
|
||||
}
|
||||
// 允许直接访问
|
||||
if (form.value?.permission === 0) {
|
||||
emit('done', true);
|
||||
}
|
||||
// 要求登录可见
|
||||
if (form.value?.permission === 1) {
|
||||
navigateTo(`/passport/login`);
|
||||
if (token && token.value.length > 0) {
|
||||
emit('done', true);
|
||||
}
|
||||
}
|
||||
// 要求密码可见
|
||||
if (form.value?.permission === 2) {
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
reload();
|
||||
</script>
|
||||
Reference in New Issue
Block a user