Compare commits

...

2 Commits

Author SHA1 Message Date
科技小王子 9d9fdc48f4 Merge remote-tracking branch 'origin/master' 6 months ago
科技小王子 bca962f79d 修复已知问题 6 months ago
  1. 92
      api/cms/cmsNavigation/model/index.ts
  2. 103
      api/cms/cmsWebsite/model/index.ts
  3. 4
      api/layout/index.ts
  4. 18
      components/AppFooter.vue
  5. 303
      components/AppHeader.vue
  6. 20
      components/Banner.vue
  7. 12
      composables/useServerRequest.ts
  8. 61
      layouts/default.vue
  9. 139
      pages/components/AboutUs.vue
  10. 75
      pages/components/Carousel.vue
  11. 121
      pages/components/CompanyStyle.vue
  12. 121
      pages/components/Djzt.vue
  13. 167
      pages/components/ImportantInformation.vue
  14. 204
      pages/components/NewsCenter.vue
  15. 121
      pages/components/VideoCenter.vue
  16. 24
      pages/detail/components/LikeArticle.vue
  17. 37
      pages/detail/components/PageBanner.vue
  18. 128
      pages/index.vue

92
api/cms/cmsNavigation/model/index.ts

@ -0,0 +1,92 @@
import type {PageParam} from '@/api';
import type {CmsDesign} from "~/api/cms/cmsDesign/model";
/**
*
*/
export interface CmsNavigation {
// ID
navigationId?: number;
// 上级id, 0是顶级
parentId?: number;
// 菜单名称
title?: string;
// 模型
model?: string;
// 标识
code?: string;
// 菜单路由地址
path?: string;
// 菜单组件地址, 目录可为空
component?: string;
componentPath?: string;
// 打开位置
target?: string;
// 菜单图标
icon?: string;
// 图标颜色
color?: string;
// 是否隐藏, 0否, 1是(仅注册路由不显示在左侧菜单)
hide?: number;
// 可见类型 0所有人 1登录可见 2密码可见
permission?: number;
// 访问密码
password?: string;
// 位置 0不限 1顶部 2底部
position?: number;
// 仅在顶部显示
top?: number;
// 仅在底部显示
bottom?: number;
// 菜单侧栏选中的path
active?: string;
// 其它路由元信息
meta?: string;
// css样式
style?: string;
// 父级栏目路由
parentPath?: string;
// 父级栏目名称
parentName?: string;
// 模型名称
modelName?: string;
// 类型(已废弃)
type?: number;
// 绑定的页面(已废弃)
pageId?: number;
// 项目ID
itemId?: number;
// 是否微信小程序菜单
isMpWeixin?: string;
// 用户ID
userId?: number;
// 设为首页
home?: number;
// 排序(数字越小越靠前)
sortNumber?: number;
// 备注
comments?: string;
// 是否删除, 0否, 1是
deleted?: number;
// 状态, 0正常, 1冻结
status?: number;
// 租户id
tenantId?: number;
// 创建时间
createTime?: string;
children?: CmsNavigation[];
disabled?: boolean;
label?: string;
value?: number;
design?: CmsDesign;
childHeight?: number,
showChild?: boolean
}
/**
*
*/
export interface CmsNavigationParam extends PageParam {
navigationId?: number;
keywords?: string;
}

103
api/cms/cmsWebsite/model/index.ts

@ -0,0 +1,103 @@
import type { PageParam } from '@/api';
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
/**
*
*/
export interface CmsWebsite {
// 站点ID
websiteId?: number;
// 网站名称
websiteName?: string;
// 网站标识
websiteCode?: string;
// 网站LOGO
websiteIcon?: string;
// 网站LOGO
websiteLogo?: string;
// 网站LOGO(深色模式)
websiteDarkLogo?: string;
// 网站类型
websiteType?: string;
// 网站关键词
keywords?: string;
// 域名前缀
prefix?: string;
// 绑定域名
domain?: string;
// 全局样式
style?: string;
// 后台管理地址
adminUrl?: string;
// 应用版本 10免费版 20专业版 30永久授权
version?: number;
// 服务到期时间
expirationTime?: string;
// 模版ID
templateId?: number;
// 行业类型(父级)
industryParent?: string;
// 行业类型(子级)
industryChild?: string;
// 企业ID
companyId?: number;
// 所在国家
country?: string;
// 所在省份
province?: string;
// 所在城市
city?: string;
// 所在辖区
region?: string;
// 经度
longitude?: string;
// 纬度
latitude?: string;
// 街道地址
address?: string;
// 联系电话
phone?: string;
// 电子邮箱
email?: string;
// ICP备案号
icpNo?: string;
// 公安备案
policeNo?: string;
// 备注
comments?: string;
// 是否推荐
recommend?: number;
// 状态 0未开通 1运行中 2维护中 3已关闭 4已欠费停机 5违规关停
status?: number;
// 维护说明
statusText?: string;
// 关闭说明
statusClose?: string;
// 全局样式
styles?: string;
// 排序号
sortNumber?: number;
// 用户ID
userId?: number;
// 是否删除, 0否, 1是
deleted?: number;
// 租户id
tenantId?: number;
// 创建时间
createTime?: string;
// 修改时间
updateTime?: string;
// 网站配置
config?: any;
topNavs?: CmsNavigation[];
bottomNavs?: CmsNavigation[];
}
/**
*
*/
export interface CmsWebsiteParam extends PageParam {
websiteId?: number;
status?: number;
keywords?: string;
}

4
api/layout/index.ts

@ -3,13 +3,13 @@ import type { ApiResult } from '@/api';
import type { User } from '@/api/system/user/model';
import type { UpdatePasswordParam } from './model';
import { MODULES_API_URL, SERVER_API_URL } from '~/config';
import type {Website} from "~/api/cms/website/model";
import type {CmsWebsite} from "~/api/cms/cmsWebsite/model";
/**
*
*/
export async function getSiteInfo() {
const res = await request.get<ApiResult<Website>>(
const res = await request.get<ApiResult<CmsWebsite>>(
MODULES_API_URL + '/cms/website/getSiteInfo'
);
if (res.data.code === 0 && res.data.data) {

18
components/AppFooter.vue

@ -67,11 +67,11 @@
<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">-->
<!-- <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 class="tools flex gap-xl items-center opacity-80 hover:opacity-90">
<el-tooltip :content="`管理后台`" v-if="config.showAdminIcon">
<a :href="`https://website.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>
@ -91,16 +91,24 @@
import {getPath, openSpmUrl, useConfigInfo} from "#imports";
import {useSubMenu, useWebsite} from "~/composables/configState";
import { StarFilled } from '@element-plus/icons-vue'
import type {ApiResult} from "~/api";
import type {Config} from "~/types/global";
const config = useConfigInfo();
const website = useWebsite();
const subMenu = useSubMenu();
const SysDomain = localStorage.getItem('SysDomain');
console.log('---------config---------',config.value)
console.log('---------website---------',website.value)
console.log('---------subMenu---------',subMenu.value)
const activeNames = ref(['1'])
// TODO
const { data: fields } = await useServerRequest<ApiResult<Config>>('/cms/cms-website-field/config', {baseURL: 'https://server.gxwebsoft.com/api',});
if (fields.value?.data) {
config.value = fields.value?.data;
}
</script>
<style lang="scss">

303
components/AppHeader.vue

@ -1,125 +1,130 @@
<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>
<header
class="header fixed w-full bg-[#3f3e3c] opacity-90 border-b border-gray-200 dark:border-gray-800 -mb-px top-0 z-50 lg:mb-0 lg:border-0">
<div class="xl:w-screen-xl xl:p-0 px-4 flex items-center between w-full m-auto bg-[#3f3e3c]">
<div class="header___left flex items-center">
<div class="logo mt-1 sm:w-[250px] 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="w-[190px]"
:alt="config.siteName"
:title="config.siteName"
/>
</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>
</nuxt-link>
<nuxt-link v-else to="/">
<text>{{ config?.siteName }}</text>
</nuxt-link>
</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 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 class="text-white">{{ 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 v-if="sub.icon" :src="sub.icon" shape="square" size="small"></el-avatar>-->
<!-- <span class="font-bold">{{ sub.title }}</span>-->
<!-- </el-space>-->
<!-- </el-menu-item>-->
<!-- </el-sub-menu>-->
<div v-if="item?.children && item.children.length > 0">
<div class="px-[20px] relative h-[60px] w-[95px] flex justify-center items-center">
<h3 class="text-white cursor-pointer" @mouseenter="mouseenter(index)" @mouseleave="mouseleave(index)">
{{
item.title
}}</h3>
<div v-if="item.showChild"
class="absolute top-[60px] bg-white p-2 shadow border-t-2 border-[#409eff] left-0 overflow-hidden"
:style="{height: `${item.childHeight}px`}">
<div v-for="(sub,subIndex) in item.children" :key="index" class="w-[200px] p-1 cursor-pointer hover:text-[#409eff]">
<el-space
@click="openSpmUrl(`${item.path}`,sub,sub.navigationId)">
<el-avatar v-if="sub.icon" :src="sub.icon" shape="square"
size="small"></el-avatar>
<span class="font-bold">{{ sub.title }}</span>
</el-space>
</div>
</div>
</div>
</div>
<el-menu-item v-else :index="`${item.path}`"><h3
@mouseenter="mouseenter(index)"
@mouseleave="mouseleave(index)"
@click="openSpmUrl(`${item.path}`,item,item.navigationId)" class="text-white">{{ item.title }}</h3>
</el-menu-item>
</template>
</el-menu>
</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>
</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>
<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="logOut" @click="openSpmUrl('/user/logout')">退出登录
</el-dropdown-item>
</el-dropdown-menu>
</template>
<el-dropdown-item v-if="!token" divided @click="navigateTo(`/passport/login`)">登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-dropdown>
</div>
<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="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>
<Passport :visible="showPassport" @done="reload"/>
</header>
<div class="header__height__placeholder"></div>
</el-affix>
</div>
<Passport :visible="showPassport" @done="reload"/>
</header>
</template>
<script setup lang="ts">
@ -152,6 +157,70 @@ const config = useConfigInfo();
const affix = useProductAffix();
const sysDomain = useSysDomain();
const setNav = () => {
navigations.value = navigations.value.map(item => {
item.showChild = false
item.childHeight = 0;
return item
})
}
setNav()
const time = 10
let interval: NodeJS.Timeout
const mouseenter = async (index: number) => {
let showIndex = -1;
for (let i = 0; i < navigations.value.length; i++) {
console.log(navigations.value[i].showChild)
if (navigations.value[i].showChild) {
showIndex = i
break
}
}
if (showIndex === index) return
const item = navigations.value[index];
const childHeight = item.children ? parseInt((item.children?.length * 31).toString()) : 0;
await hideAll()
item.showChild = true
if (item.childHeight === undefined) item.childHeight = 0
interval = setInterval(() => {
if (item.childHeight !== undefined && item.childHeight >= childHeight && interval) clearInterval(interval)
if (item.childHeight !== undefined && item.children !== undefined && (item.childHeight < childHeight)) {
item.childHeight += parseInt(((childHeight / item.children?.length) / 5).toString())
} else {
// item.childHeight = childHeight
if (interval) clearInterval(interval)
}
console.log(item.childHeight, childHeight)
// console.log(item.childHeight)
}, time)
}
const mouseleave = () => {
if (interval) clearInterval(interval)
}
const hideAll = () => {
return new Promise(resolve => {
navigations.value = navigations.value.map(item => {
if (item.childHeight === undefined) item.childHeight = 0
if (item.showChild) {
const childHeight = item.children ? parseInt((item.children?.length * 31).toString()) : 0;
const interval = setInterval(() => {
if (item.childHeight !== undefined && item.children !== undefined && (item.childHeight > 0)) {
item.childHeight -= parseInt(((childHeight / item.children?.length) / 5).toString())
} else {
item.showChild = false
clearInterval(interval)
}
}, time)
}
return item
})
resolve(true)
})
}
//
const visibleNumber = ref<number>(6);
config.value.elMenuMaxNumber = 8;
@ -192,7 +261,7 @@ const reload = async () => {
const token = localStorage.getItem('token');
// const domain = localStorage.getItem('SysDomain');
// if (domain) {
// sysDomain.value = domain;
// sysDomain.value = domain;
// }
//
if (token && token != '') {
@ -202,7 +271,7 @@ const reload = async () => {
}
//
const {data: companyInfo} = await useServerRequest<ApiResult<Company>>('/system/company/profile', {baseURL: runtimeConfig.public.apiServer})
if(companyInfo.value?.data){
if (companyInfo.value?.data) {
company.value = companyInfo.value?.data;
// sysDomain.value = company.value?.domain || undefined;
// user.value.tenantName = company.value?.tenantName;

20
components/Banner.vue

@ -1,18 +1,22 @@
<template>
<div class="banner m-auto relative sm:flex">
<div class="banner m-auto relative sm:flex mt-[60px] hidden-sm-and-down">
<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>
</div>
</template>
</div>
<div class="banner m-auto relative sm:flex mt-[48px] hidden-sm-and-up">
<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>

12
composables/useServerRequest.ts

@ -16,7 +16,7 @@ export const useServerRequest = <T>(url: string, opts?: UseFetchOptions<T, unkno
baseUrl.value = runtimeConfig.public.apiServer;
// 开发环境
if(process.env.NODE_ENV === 'development'){
baseUrl.value = `http://127.0.0.1:30000/api`
// baseUrl.value = `http://127.0.0.1:9002/api`
}
// 登录凭证token
const token = useToken();
@ -28,7 +28,9 @@ export const useServerRequest = <T>(url: string, opts?: UseFetchOptions<T, unkno
baseURL: baseUrl.value,
onRequest({ options }) {
options.headers = (options.headers || {}) as { [key: string]: string };
// options.headers.tenantid = runtimeConfig.public.tenantId;
if(process.env.NODE_ENV === 'development') {
options.headers.tenantid = `${runtimeConfig.public.tenantId}`;
}
if (token.value) {
options.headers.Authorization = token.value;
}
@ -37,11 +39,13 @@ export const useServerRequest = <T>(url: string, opts?: UseFetchOptions<T, unkno
const parts = domain.split('.');
const subDomain = parts[0];
if (isInteger(subDomain)) {
console.log(`${subDomain}`)
// options.headers.tenantid = `${subDomain}`;
}
// TODO 2 从绑定域名解构的租户ID
if(localStorage.getItem('TID_ADMIN')){
options.headers.TenantId = `5`;
// options.headers.tenantid = `5`;
if(localStorage.getItem('TenantId')){
options.headers.tenantid = `${localStorage.getItem('TenantId')}`;
}
},
onResponse({ response }) {

61
layouts/default.vue

@ -1,7 +1,7 @@
<template>
<div class="overflow-x-hidden">
<!-- 加载应用 -->
<template v-if="!loading" @scroll="handleScroll">
<template v-if="!loading">
<!-- 管理中心界面 -->
<template v-if="getPath().startsWith('/manage')">
<slot/>
@ -21,7 +21,6 @@
<script setup lang="ts">
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {Domain} from "~/api/cms/domain/model";
import {useConfigInfo, useForm, useMenu, useSubMenu, useToken, useWebsite} from "~/composables/configState";
import type {Website} from "~/api/cms/website/model";
import type {Navigation} from "~/api/cms/navigation/model";
@ -29,6 +28,7 @@ import type {Config} from "~/types/global";
import {getLoacl, getPath, setLocal} from "~/utils/common";
import UnderMaintenance from "~/components/UnderMaintenance.vue";
import {navigateTo} from "#imports";
import type {Domain} from "~/api/cms/domain/model";
//
const runtimeConfig = useRuntimeConfig();
@ -39,6 +39,7 @@ const menu = useMenu()
const token = useToken()
const subMenu = useSubMenu()
//
onMounted(() => {
//onMountedlocalwindow
@ -61,27 +62,18 @@ const reload = async () => {
if (domain !== 'localhost') {
//
// const runtimeConfig = useRuntimeConfig();
// const {data: domainInfo } = await useServerRequest<ApiResult<Domain>>('/cms/cms-domain/getTenantIdByDomain',{baseURL: runtimeConfig.public.apiServer,query: {
// domain
// }});
// const data = domainInfo.value?.data;
// console.log(data,',ID')
// if (data) {
// localStorage.setItem('TID_DOMAIN',`${data?.tenantId}`)
// }else {
// token.value = '';
// navigateTo('/passport/login')
// }
const {data: domainInfo } = await useServerRequest<ApiResult<Domain>>('/cms/cms-domain/getTenantIdByDomain',{baseURL: 'https://server.gxwebsoft.com/api',query: {
domain
}});
const data = domainInfo.value?.data;
console.log(data,'查询当前域名是否有绑定域名,绑定则解构出租户ID放入缓存')
if (data) {
localStorage.setItem('TenantId',`${data?.tenantId}`)
}
}
// TODO 2
const {data: websiteInfo} = await useServerRequest<ApiResult<Website>>('/cms/cms-website/getSiteInfo', {
baseURL: runtimeConfig.public.apiServer,
headers: {
TenantId: `5`
}
});
const {data: websiteInfo} = await useServerRequest<ApiResult<Website>>('/cms/cms-website/getSiteInfo');
if (!websiteInfo.value) {
ElMessage.error('require is not defined.')
return false;
@ -96,27 +88,26 @@ const reload = async () => {
if (websiteInfo.value?.data) {
console.log(websiteInfo.value.data,'-=======')
website.value = websiteInfo.value?.data;
config.value = website.value.config;
localStorage.setItem('TenantId',`${websiteInfo.value.data.tenantId}`);
if (website.value.topNavs) {
// if(localStorage.getItem('UserId')){
// website.value?.topNavs.push({
// title: ``,
// path: `/developer`
// })
// }
menu.value = website.value?.topNavs;
}
if (website.value.bottomNavs) {
subMenu.value = website.value?.bottomNavs;
}
//
if (domain !== 'localhost') {
console.log(domain, 'domain')
console.log(website.value.websiteCode)
console.log(`${website.value?.websiteCode}.wsdns.cn`, '二级域名')
if(website.value.config){
localStorage.setItem('Domain',website.value.config.Domain);
localStorage.setItem('SysDomain',website.value.config.SysDomain);
}
}
// TODO 3
const { data: fields } = await useServerRequest<ApiResult<Config>>('/cms/cms-website-field/config', {baseURL: 'https://server.gxwebsoft.com/api',});
if (fields.value?.data) {
config.value = fields.value?.data;
localStorage.setItem('SiteName',config.value.siteName);
}
// TODO 4
// const {data: websiteRealTime } = await useServerRequest<ApiResult<Website>>('/cms/cms-website/' + website.value.websiteId,{baseURL: runtimeConfig.public.apiServer});
// website.value = Object.assign({},website.value,websiteRealTime.value?.data)
@ -126,12 +117,6 @@ const reload = async () => {
// return false;
// }
// TODO 5
// const { data: fields } = await useServerRequest<ApiResult<Config>>('/cms/cms-website-field/config', {baseURL: runtimeConfig.public.apiServer});
// if (fields.value?.data) {
// config.value = fields.value?.data;
// }
// TODO 6
// const { data: menuInfo } = await useServerRequest<ApiResult<Navigation[]>>('/cms/cms-navigation/tree', {baseURL: runtimeConfig.public.apiServer,
// query: {

139
pages/components/AboutUs.vue

@ -0,0 +1,139 @@
<template>
<div class="w-full bg-white pb-20 mb-3 relative"
:style="`background: url('${config?.IndexAboutBg}') no-repeat center`">
<template v-if="showAboutUs">
<div class="text-center flex flex-col items-center py-15 relative ">
<div class="sub-title">
<p class="text-gray-200 text-6xl font-bold dark:text-gray-700 py-0 line-height-5">
{{ comments }}
</p>
</div>
<h2 class="text-3xl font-bold tracking-tight text-green-600 dark:text-white sm:text-4xl lg:text-5xl">
{{ title }}
</h2>
</div>
<div class="hidden-sm-and-down">
<div class="xl:w-screen-xl m-auto text-xl flex justify-between " v-if="config?.IndexAboutInfo">
<p class="indent-xl max-w-3/5 px-4 left-2-right" :class="config?.IndexAboutStyle"
style="font-family: Source Han Serif, SimSun,serif">{{ config?.IndexAboutInfo }}</p>
<div class="carousel px-3 text-center right-to-left">
<img :src="config?.IndexAboutImg" class="scale-img"/>
</div>
</div>
</div>
<div class="hidden-sm-and-up">
<div class="xl:w-screen-xl m-auto text-lg flex flex-col " v-if="config?.IndexAboutInfo">
<div class="carousel px-3 text-center right-to-left">
<img :src="config?.IndexAboutImg" class="scale-img"/>
</div>
<p class="p-3 left-2-right" :class="config?.IndexAboutStyle"
style="font-family: Source Han Serif, SimSun,serif">{{ config?.IndexAboutInfo }}</p>
</div>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult} from "~/api";
import type {Config} from "~/types/global";
const props = withDefaults(
defineProps<{
config?: any;
title?: string;
comments?: string;
scrollTop: number;
}>(),
{}
);
const emit = defineEmits<{
(e: 'done'): void;
}>();
const showAboutUs = ref(false)
watch(
() => props.scrollTop,
() => {
if (props.scrollTop > 700) showAboutUs.value = true
},
{immediate: true}
);
//
// const reload = async () => {
// const { data: fields } = await useServerRequest<ApiResult<Config>>('/cms/cms-website-field/config', {baseURL: 'https://server.gxwebsoft.com/api',});
// if (fields.value?.data) {
// config.value = fields.value?.data;
// }
// }
// reload();
</script>
<style lang="less">
/* 在全局样式表如 app.css 或 styles.css 中添加 */
.el-tabs__item {
font-size: 3em; /* 调整为你想要的字体大小 */
}
.custom-tabs .el-tabs__item {
font-size: 20px; /* 调整为你想要的字体大小 */
}
</style>
<style lang="less" scoped>
.scale-img {
width: 500px;
max-width: 100% !important;
}
.scale-img:hover {
animation: scale;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes scale {
from {
transform: scale(1);
}
to {
transform: scale(1.1);
}
}
.left-2-right {
animation: left-2-right-ani;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes left-2-right-ani {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
.right-to-left {
animation: right-to-left-ani;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes right-to-left-ani {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
</style>

75
pages/components/Carousel.vue

@ -0,0 +1,75 @@
<template>
<div>
<div class="w-full bg-white mt-[60px] mb-3 hidden-sm-and-down" v-if="ad">
<el-carousel indicator-position="none" :height="ad?.height">
<el-carousel-item v-for="(item,index) in ad?.imgArr" :key="index">
<div class="item relative">
<el-image :src="item.url" />
</div>
</el-carousel-item>
</el-carousel>
</div>
<!-- 移动端 -->
<div class="sm:hidden w-full bg-white mt-[48px] mb-3 hidden-sm-and-up" v-if="ad">
<el-carousel indicator-position="none" height="160">
<el-carousel-item v-for="(item,index) in ad?.imgArr" :key="index">
<el-image :src="item.url" />
</el-carousel-item>
</el-carousel>
</div>
</div>
</template>
<script setup lang="ts">
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult, PageResult} from "~/api";
import type {CompanyParam} from "~/api/system/company/model";
import type {CmsAd} from "~/api/cms/cmsAd/model";
const props = withDefaults(
defineProps<{
config?: any;
list?: any[];
disabled?: boolean;
title?: string;
comments?: string;
}>(),
{
title: '卡片标题',
comments: '卡片描述'
}
);
const emit = defineEmits<{
(e: 'done'): void;
}>();
const ad = ref<CmsAd>();
//
const where = reactive<CompanyParam>({
keywords: ''
});
//
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<PageResult<CmsAd>>>('/cms/cms-ad/page',{params: {adType: '幻灯片',pageName: '首页'}})
if (response.value?.data?.list) {
ad.value = response.value.data?.list[0];
}
}
watch(
() => props.config,
() => {
reload();
},
{immediate: true}
);
</script>
<style>
.hidden-sm-and-up .el-carousel{
height: 160px;
}
</style>

121
pages/components/CompanyStyle.vue

@ -0,0 +1,121 @@
<template>
<div class="w-full bg-gray-100 pb-20 bg-white">
<div class="text-center flex flex-col items-center py-15 relative ">
<div class="sub-title">
<p class="text-gray-200 text-5xl font-bold dark:text-gray-700 py-0 line-height-5">
{{ comments }}
</p>
</div>
<h2 class="text-3xl font-bold tracking-tight text-[#FF6E0CFF] dark:text-white sm:text-4xl lg:text-5xl">
{{ title }}
</h2>
</div>
<div class="xl:w-screen-xl m-auto text-xl right-to-left hidden-sm-and-down" v-if="show">
<el-carousel :interval="4000" type="card" height="350px" arrow="never" indicator-position="none">
<el-carousel-item class="relative" v-for="(item,index) in list" :key="index">
<el-image :src="item.image" class="cursor-pointer scale-img w-full" />
<div class="absolute bg-[#e65a01]/75 w-full py-2 z-100 text-center text-sm text-white bottom-0" v-if="item.comments" @click="openSpmUrl(`/detail`,item,item.articleId,true)">{{ item.comments }}</div>
</el-carousel-item>
</el-carousel>
</div>
<div class="xl:w-screen-xl m-auto text-xl right-to-left hidden-sm-and-up">
<el-carousel :interval="4000" type="card" height="350px" arrow="never" indicator-position="none">
<el-carousel-item class="relative" v-for="(item,index) in list" :key="index">
<el-image :src="item.image" class="cursor-pointer scale-img w-full" @click="openSpmUrl(`/detail`,item,item.articleId,true)" />
<div class="absolute bg-[#e65a01]/75 w-full py-2 z-100 text-center text-sm text-white bottom-0" v-if="item.comments" >{{ item.comments }}</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
</template>
<script setup lang="ts">
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult, PageResult} from "~/api";
import type {FileRecord} from "~/api/system/file/model";
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
const props = withDefaults(
defineProps<{
config?: any;
groupId?: number;
title?: string;
comments?: string;
scrollTop: number;
}>(),
{
title: '卡片标题',
comments: '卡片描述'
}
);
const emit = defineEmits<{
(e: 'done'): void;
}>();
const list = ref<CmsArticle[]>([])
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<PageResult<CmsArticle>>>('/cms/cms-article/page', {
params: {
categoryId: 989,
limit: 8
}
})
if(response.value?.data){
if(response.value?.data.list){
list.value = response.value?.data.list
}
}
// const {data: response} = await useServerRequest<ApiResult<PageResult<FileRecord>>>('/file/page',{
// baseURL: 'https://server.gxwebsoft.com/api',
// query: {
// groupId: props.groupId
// }
// })
}
const show = ref(false)
watch(
() => [props.groupId, props.scrollTop],
() => {
reload();
if (props.scrollTop >= 1580) show.value = true
},
{immediate: true}
);
</script>
<style lang="less" scoped>
.scale-img:hover {
animation: scale;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes scale {
from {
transform: scale(1);
}
to {
transform: scale(1.1);
}
}
.right-to-left {
animation: right-to-left-ani;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes right-to-left-ani {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
</style>

121
pages/components/Djzt.vue

@ -0,0 +1,121 @@
<template>
<div class="w-full bg-gray-100 pb-20">
<div class="text-center flex flex-col items-center py-15 relative ">
<div class="sub-title">
<p class="text-gray-200 text-5xl font-bold dark:text-gray-700 py-0 line-height-5">
{{ comments }}
</p>
</div>
<h2 class="text-3xl font-bold tracking-tight text-[#ff0000] dark:text-white sm:text-4xl lg:text-5xl">
{{ title }}
</h2>
</div>
<div class="xl:w-screen-xl m-auto text-xl right-to-left hidden-sm-and-down" v-if="show">
<el-carousel :interval="4000" type="card" height="450px" arrow="never" indicator-position="none">
<el-carousel-item class="relative" v-for="(item,index) in list" :key="index">
<el-image :src="item.image" class="cursor-pointer scale-img w-full" />
<div class="absolute bg-[#e65a01]/75 w-full py-2 z-100 text-center text-sm text-white bottom-0" v-if="item.comments" @click="openSpmUrl(`/detail`,item,item.articleId,true)">{{ item.comments }}</div>
</el-carousel-item>
</el-carousel>
</div>
<div class="xl:w-screen-xl m-auto text-xl right-to-left hidden-sm-and-up">
<el-carousel :interval="4000" type="card" height="350px" arrow="never" indicator-position="none">
<el-carousel-item class="relative" v-for="(item,index) in list" :key="index">
<el-image :src="item.image" class="cursor-pointer scale-img w-full" @click="openSpmUrl(`/detail`,item,item.articleId,true)" />
<div class="absolute bg-[#e65a01]/75 w-full py-2 z-100 text-center text-sm text-white bottom-0" v-if="item.comments" >{{ item.comments }}</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
</template>
<script setup lang="ts">
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult, PageResult} from "~/api";
import type {FileRecord} from "~/api/system/file/model";
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
const props = withDefaults(
defineProps<{
config?: any;
groupId?: number;
title?: string;
comments?: string;
scrollTop: number;
}>(),
{
title: '卡片标题',
comments: '卡片描述'
}
);
const emit = defineEmits<{
(e: 'done'): void;
}>();
const list = ref<CmsArticle[]>([])
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<PageResult<CmsArticle>>>('/cms/cms-article/page', {
params: {
categoryId: 990,
limit: 8
}
})
if(response.value?.data){
if(response.value?.data.list){
list.value = response.value?.data.list
}
}
// const {data: response} = await useServerRequest<ApiResult<PageResult<FileRecord>>>('/file/page',{
// baseURL: 'https://server.gxwebsoft.com/api',
// query: {
// groupId: props.groupId
// }
// })
}
const show = ref(false)
watch(
() => [props.groupId, props.scrollTop],
() => {
reload();
if (props.scrollTop >= 2240) show.value = true
},
{immediate: true}
);
</script>
<style lang="less" scoped>
.scale-img:hover {
animation: scale;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes scale {
from {
transform: scale(1);
}
to {
transform: scale(1.1);
}
}
.right-to-left {
animation: right-to-left-ani;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes right-to-left-ani {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
</style>

167
pages/components/ImportantInformation.vue

@ -0,0 +1,167 @@
<template>
<div class="xl:w-screen-2xl m-auto text-xl">
<el-row :gutter="20">
<el-col :span="12">
<el-card shadow="hover" class="bg-gray-50 w-full h-[150px]">
<template #header>
<div class="card-header">
<span class="font-bold text-[#ff0000]">重大信息公开</span>
</div>
</template>
<template v-for="(item,index) in list" :key="index">
<li class="flex justify-between py-2">
<a class="line-clamp-1 max-w-2xl" :class="`item-${index}`"
:href="getSpmUrl(`/detail`,item,item.articleId)" target="_blank">{{ item.title }}</a>
<span class="text-gray-400 font-200">{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</span>
</li>
</template>
</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="hover" class="bg-gray-50 w-full h-[150px]">
<template #header>
<div class="card-header">
<span class="font-bold text-[#ff0000]">党建专题</span>
</div>
</template>
<div class="flex justify-between">
<div class="flex flex-col">
<span class="text-[#ff0000]">2023关于2023年三重一大的启动通知</span>
<span class="text-sm text-gray-500">2023-03-07</span>
</div>
<div class="flex flex-col items-end">
<span class="text-[#ff0000]">2023关于2023年三重一大的启动通知</span>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import {getSpmUrl, openSpmUrl} from "~/utils/common";
import dayjs from "dayjs";
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult, PageResult} from "~/api";
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
import type {CompanyParam} from "~/api/system/company/model";
import {TENANT_ID} from "~/config";
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
const props = withDefaults(
defineProps<{
config?: any;
scrollTop: number;
parentId?: any;
disabled?: boolean;
title?: string;
comments?: string;
}>(),
{
title: '卡片标题',
comments: '卡片描述'
}
);
const emit = defineEmits<{
(e: 'done'): void;
}>();
const category = ref<ApiResult<CmsNavigation[]>>();
const list = ref<CmsArticle[]>([]);
const hotList = ref<CmsArticle[]>([]);
const categoryId = ref();
//
const where = reactive<CompanyParam>({
keywords: ''
});
const handleClick = () => {
reload();
}
//
const {data: hotResponse} = await useServerRequest<ApiResult<PageResult<CmsArticle>>>('/cms/cms-article/page', {
params: {
recommend: 1,
limit: 5
}
})
if (hotResponse.value?.data) {
hotList.value = hotResponse.value?.data.list
}
//
const getCategory = async () => {
const {data: categoryInfo} = await useServerRequest<ApiResult<CmsNavigation[]>>('/cms/cms-navigation', {
params: {
parentId: props.parentId
}
})
if (categoryInfo.value) {
category.value = categoryInfo.value
categoryId.value = categoryInfo.value?.data && categoryInfo.value?.data[0].navigationId
}
await reload();
}
//
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<PageResult<CmsArticle>>>('/cms/cms-article/page', {
params: {
categoryId: Number(categoryId.value),
limit: 8
}
})
if (response.value?.data) {
list.value = response.value?.data.list
}
}
const showNews = ref(false)
watch(
() => [props.parentId, props.scrollTop],
([parentId, scrollTop]) => {
getCategory();
if (props.scrollTop > 70) showNews.value = true
},
{immediate: true}
);
</script>
<style lang="less" scoped>
.left-2-right {
animation: left-2-right-ani;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes left-2-right-ani {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
.right-to-left {
animation: right-to-left-ani;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes right-to-left-ani {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
//.hidden-sm-and-down .carousel{display: none;}
</style>

204
pages/components/NewsCenter.vue

@ -0,0 +1,204 @@
<template>
<div class="w-full bg-white pb-20 mb-3 hidden-sm-and-down">
<div class="text-center flex flex-col items-center py-15 relative bg-white">
<div class="sub-title">
<p class="text-gray-200 text-6xl font-bold dark:text-gray-700 py-0 line-height-5">
{{ comments }}
</p>
</div>
<h2 class="text-3xl font-bold tracking-tight text-[#409eff] lg:text-5xl">
{{ title }}
</h2>
</div>
<div class="xl:w-screen-2xl m-auto text-xl">
<el-row :gutter="20" v-if="showNews">
<el-col :span="10">
<div class="carousel py-2 right-to-left">
<el-carousel indicator-position="none" height="400px">
<el-carousel-item class="relative" v-for="(item,index) in hotList" :key="index">
<el-image :src="item.image" @click="openSpmUrl(`/detail`,item,item.articleId,true)"
class="cursor-pointer"/>
<div class="absolute bg-[#e65a01]/75 w-full py-2 z-100 text-center text-sm text-white bottom-0">
{{ item.title }}
</div>
</el-carousel-item>
</el-carousel>
</div>
</el-col>
<el-col :span="13">
<div class="tabs px-10 w-full left-2-right">
<el-tabs class="custom-tabs" v-model="categoryId" @tab-change="handleClick">
<el-tab-pane v-for="(cate,index) in category?.data" :key="index" :label="cate.title"
:name="cate.navigationId" class="text-xl"/>
</el-tabs>
<template v-for="(item,index) in list" :key="index">
<li class="flex justify-between py-2">
<a class="line-clamp-1 max-w-2xl" :class="`item-${index}`"
:href="getSpmUrl(`/detail`,item,item.articleId)" target="_blank">{{ item.title }}</a>
<span class="text-gray-400 font-200">{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</span>
</li>
</template>
</div>
</el-col>
</el-row>
</div>
</div>
<div class="w-full bg-white pb-20 mb-3 hidden-sm-and-up">
<div class="text-center flex flex-col items-center py-15 relative bg-white">
<div class="sub-title">
<p class="text-gray-200 text-5xl font-bold dark:text-gray-700 py-0 line-height-5">
{{ comments }}
</p>
</div>
<h2 class="text-4xl font-bold tracking-tight text-[#409eff] lg:text-5xl">
{{ title }}
</h2>
</div>
<div class="xl:w-screen-2xl m-auto text-lg">
<div class="tabs px-3 w-full left-2-right">
<el-tabs class="custom-tabs" v-model="categoryId" @tab-change="handleClick">
<el-tab-pane v-for="(cate,index) in category?.data" :key="index" :label="cate.title"
:name="cate.navigationId" class="text-lg"/>
</el-tabs>
<template v-for="(item,index) in list" :key="index">
<li class="flex justify-between py-2">
<a class="" :class="`item-${index}`"
:href="getSpmUrl(`/detail`,item,item.articleId)" target="_blank">{{ item.title }}<span class="text-gray-400 font-200 px-1">{{ dayjs(item.createTime).format('MM-DD') }}</span></a>
</li>
</template>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {getSpmUrl, openSpmUrl} from "~/utils/common";
import dayjs from "dayjs";
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult, PageResult} from "~/api";
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
import type {CompanyParam} from "~/api/system/company/model";
import {TENANT_ID} from "~/config";
import type {CmsNavigation} from "~/api/cms/cmsNavigation/model";
const props = withDefaults(
defineProps<{
config?: any;
parentId?: number | string;
list?: any[];
disabled?: boolean;
title?: string;
comments?: string;
scrollTop: number;
}>(),
{
title: '卡片标题',
comments: '卡片描述'
}
);
const emit = defineEmits<{
(e: 'done'): void;
}>();
const category = ref<ApiResult<CmsNavigation[]>>();
const list = ref<CmsArticle[]>([]);
const hotList = ref<CmsArticle[]>([]);
const categoryId = ref();
//
const where = reactive<CompanyParam>({
keywords: ''
});
const handleClick = () => {
reload();
}
//
const {data: hotResponse} = await useServerRequest<ApiResult<PageResult<CmsArticle>>>('/cms/cms-article/page', {
params: {
recommend: 1,
limit: 5
}
})
if (hotResponse.value?.data) {
hotList.value = hotResponse.value?.data.list
}
//
const getCategory = async () => {
const {data: categoryInfo} = await useServerRequest<ApiResult<CmsNavigation[]>>('/cms/cms-navigation', {
params: {
parentId: props.parentId
}
})
if (categoryInfo.value) {
category.value = categoryInfo.value
categoryId.value = categoryInfo.value?.data && categoryInfo.value?.data[0].navigationId
}
await reload();
}
//
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<PageResult<CmsArticle>>>('/cms/cms-article/page', {
params: {
categoryId: Number(categoryId.value),
limit: 8
}
})
if (response.value?.data) {
list.value = response.value?.data.list
}
}
const showNews = ref(false)
watch(
() => [props.parentId, props.scrollTop],
([parentId, scrollTop]) => {
getCategory();
if (props.scrollTop > 70) showNews.value = true
},
{immediate: true}
);
</script>
<style lang="less" scoped>
.left-2-right {
animation: left-2-right-ani;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes left-2-right-ani {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
.right-to-left {
animation: right-to-left-ani;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes right-to-left-ani {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
//.hidden-sm-and-down .carousel{display: none;}
</style>

121
pages/components/VideoCenter.vue

@ -0,0 +1,121 @@
<template>
<div class="w-full bg-white pb-20 mb-3 hidden-sm-and-down">
<div class="text-center flex flex-col items-center py-15 relative ">
<div class="sub-title">
<p class="text-gray-200 text-5xl font-bold dark:text-gray-700 py-0 line-height-5">
{{ comments }}
</p>
</div>
<h2 class="text-3xl font-bold tracking-tight text-[#FF6E0CFF] lg:text-5xl">
{{ title }}
</h2>
</div>
<div class="xl:w-screen-xl m-auto text-xl scale" v-if="config && show">
<div class="carousel py-3 text-center" v-if="config?.IndexVideoCenter">
<video id="my-video" class="video-js vjs-default-skin" controls preload="auto" width="900" height="510"
:poster="config?.IndexVideoImg" data-setup="{}">
<source :src="config?.IndexVideoCenter" type="video/mp4">
<p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a web browser that
<a :href="config?.IndexVideoCenter" target="_blank">supports HTML5 video</a>
</p>
</video>
</div>
</div>
</div>
<div class="w-full bg-white pb-20 mb-3 hidden-sm-and-up">
<div class="text-center flex flex-col items-center py-15 relative ">
<div class="sub-title">
<p class="text-gray-200 text-6xl font-bold dark:text-gray-700 py-0 line-height-5">
{{ comments }}
</p>
</div>
<h2 class="text-3xl font-bold tracking-tight text-[#FF6E0CFF] lg:text-5xl">
{{ title }}
</h2>
</div>
<div class="xl:w-screen-xl m-auto text-xl scale" v-if="config && show">
<div class="hidden-sm-and-down carousel py-3 text-center" v-if="config?.IndexVideoCenter">
<video id="my-video" class="video-js vjs-default-skin" controls preload="auto" width="900" height="510"
:poster="config?.IndexVideoImg" data-setup="{}">
<source :src="config?.IndexVideoCenter" type="video/mp4">
<p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a web browser that
<a :href="config?.IndexVideoCenter" target="_blank">supports HTML5 video</a>
</p>
</video>
</div>
<div class="hidden-sm-and-up carousel text-center" v-if="config?.IndexVideoCenter">
<video id="my-video" class="video-js vjs-default-skin" controls preload="auto" width="350" height="210"
:poster="config?.IndexVideoImg" data-setup="{}">
<source :src="config?.IndexVideoCenter" type="video/mp4">
<p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a web browser that
<a :href="config?.IndexVideoCenter" target="_blank">supports HTML5 video</a>
</p>
</video>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const props = withDefaults(
defineProps<{
config?: any;
list?: any[];
disabled?: boolean;
title?: string;
comments?: string;
scrollTop: number;
}>(),
{
title: '卡片标题',
comments: '卡片描述'
}
);
const emit = defineEmits<{
(e: 'done'): void;
}>();
const indexVideo = ref();
const indexVideoImg = ref();
//
const reload = async () => {
}
const show = ref(false)
watch(
() => props.scrollTop,
() => {
if (props.scrollTop >= 1500) show.value = true
},
{immediate: true}
);
reload();
</script>
<style lang="less" scoped>
.scale{
animation: scale-ani;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
@keyframes scale-ani {
from{
transform: scale(1.5);
opacity: 0;
}
to{
transform: scale(1);
opacity: 1;
}
}
</style>

24
pages/detail/components/LikeArticle.vue

@ -1,5 +1,5 @@
<template>
<el-affix class="lg:p-0 p-3" v-if="form" :offset="offset" @change="onAffix">
<el-affix class="lg:p-0 p-3 hidden-sm-and-down" v-if="form" :offset="offset" @change="onAffix">
<el-button type="text" color="gray">
<div class="text-xl text-gray-600 ">你可能还喜欢</div>
<el-icon class="el-icon--right text-gray-600" color="#999999">
@ -22,6 +22,28 @@
</el-card>
</template>
</el-affix>
<div class="lg:p-0 p-3 hidden-sm-and-up">
<el-button type="text" color="gray">
<div class="text-xl text-gray-600 ">推荐文章</div>
<el-icon class="el-icon--right text-gray-600" color="#999999">
<ArrowRightBold/>
</el-icon>
</el-button>
<template v-for="(item,index) in list" :key="index">
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer mt-3"
@click="openSpmUrl(`/detail`,item,item.articleId,true)">
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full md:h-[150px] h-[199px] cursor-pointer"/>
<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 items-center gap-1.5">
<span class="flex-1 text-xl cursor-pointer max-h-[57px] overflow-hidden">{{ item.title }}</span>
</div>
<div class="flex items-center gap-1.5 py-2 text-gray-500">
<span>{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</span>
</div>
</div>
</el-card>
</template>
</div>
</template>
<script setup lang="ts">
import { ArrowRightBold } from '@element-plus/icons-vue'

37
pages/detail/components/PageBanner.vue

@ -1,5 +1,7 @@
<template>
<div class="banner m-auto relative sm:flex">
<div class="mt-[60px] hidden-sm-and-down"></div>
<div class="mt-[51px] hidden-sm-and-up"></div>
<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">
@ -31,37 +33,8 @@
<span v-if="form.title">{{ form.title }}</span>
</h1>
<el-space class="mt-4 text-gray-500 dark:text-gray-400">
<el-avatar size="small" :src="form.logo" />
<span class="cursor-pointer text-gray-500" @click="openSpmUrl(`/ask/${form.userId}`)">{{ form.tenantName }}</span> · {{ dayjs(form.createTime).format('YYYY-MM-DD hh:mm') }}
{{ dayjs(form.createTime).format('YYYY-MM-DD') }}
</el-space>
<!-- <el-space class="mt-4">-->
<!-- <el-button-->
<!-- :icon="ElIconView"-->
<!-- size="large"-->
<!-- v-if="form.demoUrl"-->
<!-- @click="openSpmUrl(form.demoUrl)"-->
<!-- >-->
<!-- 演示地址-->
<!-- </el-button>-->
<!-- <el-button-->
<!-- v-if="form.buyUrl"-->
<!-- :icon="ElIconBottom"-->
<!-- size="large"-->
<!-- @click="openSpmUrl(form.buyUrl)"-->
<!-- >-->
<!-- 下载模版-->
<!-- </el-button>-->
<!-- <el-button-->
<!-- :icon="ElIconMemo"-->
<!-- size="large"-->
<!-- v-if="form.docUrl"-->
<!-- @click="openSpmUrl(form.docUrl)"-->
<!-- >-->
<!-- 帮助文档-->
<!-- </el-button>-->
<!-- </el-space>-->
</div>
</div>
</div>
@ -72,7 +45,7 @@
<script setup lang="ts">
import {openSpmUrl} from "~/utils/common";
import Breadcrumb from "~/components/Breadcrumb.vue";
import type {Article} from "~/api/cms/article/model";
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
import dayjs from "dayjs";
@ -81,7 +54,7 @@ withDefaults(
title?: string;
desc?: string;
buyUrl?: string;
form?: Article;
form?: CmsArticle;
}>(),
{
title: 'Templates',

128
pages/index.vue

@ -1,35 +1,103 @@
<template>
<Flash/>
<!-- <Flash/>-->
<Carousel ref="CarouselRef" v-if="config"/>
<CompanyList :param="{official: true,recommend: true,limit: 4}" :fit="`cover`" />
<NewsCenter :scrollTop="scrollTop" ref="NewsCenterRef" title="新闻中心" v-if="config" :parentId="parentId" :config="config"
comments="News Center"/>
<!-- <ProductList :param="{type:0, official: true,limit: 4}" :fit="`cover`" title="产品服务" comments="拥抱开源、坚守品质;致力于打造安全稳定高可用的WEB应用!"/>-->
<AboutUs :scrollTop="scrollTop" title="关于我们" v-if="config" :config="config" comments="About Us"/>
<!-- <ArticleList title="开发者社区" comments="分享研发成果 交流技术经验"/>-->
<CompanyStyle :scrollTop="scrollTop" title="重大信息公开" v-if="config" :groupId="groupId" :config="config" comments="Important Information"/>
<Djzt :scrollTop="scrollTop" title="党建专题" v-if="config" :groupId="groupId" :config="config" comments="Party Building Topic"/>
<!-- <div class="xl:w-screen-2xl m-auto text-xl">-->
<!-- <el-row :gutter="30">-->
<!-- <el-col :span="12">-->
<!-- <el-card shadow="hover">-->
<!-- <template #header>-->
<!-- <div class="card-header">-->
<!-- <span class="font-bold">重大信息公开</span>-->
<!-- </div>-->
<!-- </template>-->
<!-- <template v-for="(item,index) in list989" :key="index">-->
<!-- <li class="flex justify-between py-2">-->
<!-- <a class="line-clamp-1 max-w-2xl" :class="`item-${index}`"-->
<!-- :href="getSpmUrl(`/detail`,item,item.articleId)" target="_blank">{{ item.title }}</a>-->
<!-- <span class="text-gray-400 font-200">{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</span>-->
<!-- </li>-->
<!-- </template>-->
<!-- </el-card>-->
<!-- </el-col>-->
<!-- <el-col :span="12">-->
<!-- <el-card shadow="hover">-->
<!-- <template #header>-->
<!-- <div class="card-header">-->
<!-- <span class="font-bold">党建专题</span>-->
<!-- </div>-->
<!-- </template>-->
<!-- <template v-for="(item,index) in list990" :key="index">-->
<!-- <li class="flex justify-between py-2">-->
<!-- <a class="line-clamp-1 max-w-2xl" :class="`item-${index}`"-->
<!-- :href="getSpmUrl(`/detail`,item,item.articleId)" target="_blank">{{ item.title }}</a>-->
<!-- <span class="text-gray-400 font-200">{{ dayjs(item.createTime).format('YYYY-MM-DD') }}</span>-->
<!-- </li>-->
<!-- </template>-->
<!-- </el-card>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- </div>-->
<!-- <VideoCenter :scrollTop="scrollTop" title="视频中心" v-if="config" :config="config" comments="Video Center"/>-->
<!-- <CompanyList :param="{official: true,recommend: true,limit: 4}" :fit="`cover`" title="产品服务" comments="拥抱开源、坚守品质;致力于打造安全稳定高可用的WEB应用!"/>-->
<!-- <ProductList :param="{type:0, official: true,limit: 4}" :fit="`cover`" title="产品服务" comments="拥抱开源、坚守品质;致力于打造安全稳定高可用的WEB应用!"/>-->
</template>
<script setup lang="ts">
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState";
import type {BreadcrumbItem} from "~/types/global";
import type {BreadcrumbItem, Config} from "~/types/global";
import Flash from './components/Flash.vue';
import ArticleList from './components/ArticleList.vue';
import NewsCenter from "~/pages/components/NewsCenter.vue";
import Information from '~/pages/components/ImportantInformation.vue';
import AboutUs from "~/pages/components/AboutUs.vue";
import VideoCenter from "~/pages/components/VideoCenter.vue";
import CompanyStyle from "~/pages/components/CompanyStyle.vue";
import Djzt from "~/pages/components/Djzt.vue";
import dayjs from "dayjs";
import Carousel from "~/pages/components/Carousel.vue";
import type {ApiResult, PageResult} from "~/api";
import {getSpmUrl} from "~/utils/common";
import type {CmsArticle} from "~/api/cms/cmsArticle/model";
import {useServerRequest} from "~/composables/useServerRequest";
//
const route = useRoute();
const layout = ref<any>();
const token = useToken();
const form = useForm();
const config = useConfigInfo();
const groupId = ref()
const parentId = ref()
const breadcrumb = ref<BreadcrumbItem>();
const list989 = ref<CmsArticle[]>([]);
const list990 = ref<CmsArticle[]>([]);
const scrollTop = ref(0)
window.onscroll = e => {
scrollTop.value = window.document.documentElement.scrollTop
}
//
const reload = async () => {
//
if (form.value?.layout) {
layout.value = JSON.parse(form.value?.layout)
}
// if (form.value?.layout) {
// layout.value = JSON.parse(form.value?.layout)
// }
// ()
if (!token.value || token.value == '') {
@ -39,6 +107,36 @@ const reload = async () => {
// }
}
//
const {data: fields} = await useServerRequest<ApiResult<Config>>('/cms/cms-website-field/config', {baseURL: 'https://server.gxwebsoft.com/api',});
if (fields.value?.data) {
config.value = fields.value?.data;
groupId.value = config.value?.IndexPhotoGroupId;
parentId.value = config.value?.IndexNewsCenterParentId;
}
//
// const {data: response989} = await useServerRequest<ApiResult<PageResult<CmsArticle>>>('/cms/cms-article/page', {
// params: {
// categoryId: 989,
// limit: 8
// }
// })
// if (response989.value?.data) {
// list989.value = response989.value?.data.list
// }
//
// const {data: response990} = await useServerRequest<ApiResult<PageResult<CmsArticle>>>('/cms/cms-article/page', {
// params: {
// categoryId: 990,
// limit: 8
// }
// })
//
// if (response990.value?.data) {
// list990.value = response990.value?.data.list
// }
// seo
useHead({
title: `构建现代WEB应用 · WEBSOFT`,
@ -57,12 +155,12 @@ const reload = async () => {
}
watch(
() => route.path,
(path) => {
console.log(path, '=>Path')
() => route.path,
(path) => {
console.log(path, '=>Path')
reload();
},
{immediate: true}
reload();
},
{immediate: true}
);
</script>

Loading…
Cancel
Save