32 changed files with 1759 additions and 653 deletions
@ -0,0 +1,90 @@ |
|||
<template> |
|||
<PageBanner /> |
|||
<CardList :list="list" :disabled="disabled" @done="onSearch" /> |
|||
|
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult, PageResult} from "~/api"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useWebsite} from "~/composables/configState"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
import type {CompanyParam} from "~/api/system/company/model"; |
|||
import type {Article} from "~/api/cms/article/model"; |
|||
import PageBanner from './components/PageBanner.vue'; |
|||
import CardList from './components/CardList.vue'; |
|||
|
|||
const route = useRoute(); |
|||
|
|||
// 页面信息 |
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const list = ref<Article[]>([]); |
|||
const page = ref<number>(1); |
|||
const resultText = ref(''); |
|||
const layout = ref<any>(); |
|||
const disabled = ref<boolean>(false); |
|||
const activeName = ref(undefined) |
|||
|
|||
// 获取状态 |
|||
const form = ref<Navigation>(); |
|||
const website = useWebsite(); |
|||
|
|||
// 搜索表单 |
|||
const where = reactive<CompanyParam>({ |
|||
keywords: '' |
|||
}); |
|||
|
|||
const handleClick = (e:any) => { |
|||
console.log(e.index) |
|||
} |
|||
|
|||
const onSearch = () => { |
|||
if(!disabled.value){ |
|||
page.value++; |
|||
reload(route.path); |
|||
} |
|||
} |
|||
|
|||
// 请求数据 |
|||
const reload = async (path: string) => { |
|||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: { |
|||
page: page.value,limit: 8,categoryId: route.params.categoryId, keywords: where.keywords |
|||
}}) |
|||
if(response.value?.data){ |
|||
if (list.value.length < response.value?.data.count) { |
|||
disabled.value = false; |
|||
if (response.value?.data.list) { |
|||
list.value = list.value.concat(response.value?.data.list); |
|||
} |
|||
}else { |
|||
disabled.value = true; |
|||
} |
|||
if(response.value.data.count == 0){ |
|||
resultText.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; |
|||
} |
|||
// 页面布局 |
|||
if(form.value?.layout){ |
|||
layout.value = JSON.parse(form.value?.layout) |
|||
} |
|||
|
|||
useHead({ |
|||
title: `社区 - ${website.value.websiteName}`, |
|||
bodyAttrs: { |
|||
class: "page-container", |
|||
} |
|||
}); |
|||
|
|||
watch( |
|||
() => route.path, |
|||
(path) => { |
|||
reload(path); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
@ -1,156 +0,0 @@ |
|||
<template> |
|||
<!-- Banner --> |
|||
<Banner :layout="layout" /> |
|||
|
|||
<div class="container flex flex-col w-full md:w-screen-xl m-auto my-3"> |
|||
|
|||
<Breadcrumb :data="form" /> |
|||
|
|||
<!-- 左右结构 --> |
|||
<div class="news-box sm:mt-0 mt-2 flex sm:flex-row flex-col justify-between sm:space-x-4"> |
|||
<div class="left sm:w-18/24"> |
|||
<!-- 文章列表 --> |
|||
<div class="news-list"> |
|||
<ul class="infinite-list px-3" v-infinite-scroll="load" :infinite-scroll-disabled="disabled"> |
|||
<li v-for="item in list"> |
|||
<div class="item flex justify-between py-3 px-4 gap-xl mb-5 rounded-lg mb-3 bg-white hover:shadow"> |
|||
<div class="item-info py-2 flex flex-col justify-between"> |
|||
<div class="title line-clamp-2 overflow-hidden text-ellipsis group-hover:group-hover:text-ellipsis"> |
|||
<a @click="openSpmUrl(`https://websoft.top/detail`,item,item.articleId)" class="text-xl cursor-pointer">{{ item.title }}</a> |
|||
</div> |
|||
<div class="desc max-w-22/24 text-gray-5 sm:block hidden" v-html="item.comments"></div> |
|||
<div class="actions text-gray-4 text-sm flex gap-2xl"> |
|||
<span href="#">{{ item.updateTime }}</span> |
|||
<span href="#">浏览:{{ item.actualViews }}</span> |
|||
</div> |
|||
</div> |
|||
<div class="item-image flex items-center" @click="navigateTo(`/article/detail/${item.articleId}`)"> |
|||
<el-image :src="item.image" lazy class="sm:w-[140px] sm:h-[140px] w-[110px] h-[110px] bg-gray-50 cursor-pointer transition rounded-lg sm:rounded-lg ease-in-out delay-150 hover:-translate-y-1 hover:scale-110 duration-300" fit="contain" /> |
|||
</div> |
|||
</div> |
|||
</li> |
|||
</ul> |
|||
<div class="text-center text-gray-4"> |
|||
<text v-if="disabled">没有更多了</text> |
|||
<text @click="load" v-else>加载更多</text> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="right sm:mt-0 mt-4 sm:w-6/24"> |
|||
<!-- 推荐文章 --> |
|||
<div class="category-item bg-white rounded-lg p-3 hover:shadow"> |
|||
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2"> |
|||
<el-icon> |
|||
<ElIconLink/> |
|||
</el-icon> |
|||
<text class="ml-2">推荐文章</text> |
|||
</div> |
|||
<div class="flex flex-wrap px-2 py-4"> |
|||
<div v-for="(item,index) in links" class="flex items-center"> |
|||
<a :href="item.url" target="_blank">{{ item.name }}</a> |
|||
<el-divider v-if="index + 1 != links.length" direction="vertical" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="category-item mt-4 bg-white rounded-lg p-3 hover:shadow"> |
|||
<div class="category-name text-lg text-gray-600 border-b border-gray-200 border-b-solid pb-2 mx-2"> |
|||
<el-icon> |
|||
<ElIconLink/> |
|||
</el-icon> |
|||
<text class="ml-2">点击排行</text> |
|||
</div> |
|||
<div class="flex flex-wrap px-2 py-4"> |
|||
<div v-for="(item,index) in links" class="flex items-center"> |
|||
<a :href="item.url" target="_blank">{{ item.name }}</a> |
|||
<el-divider v-if="index + 1 != links.length" direction="vertical" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- <el-divider />--> |
|||
<!-- <div v-if="!list">--> |
|||
<!-- <el-empty description="404 页面不存在"></el-empty>--> |
|||
<!-- </div>--> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult, PageResult} from "~/api"; |
|||
import type {Article} from "~/api/cms/article/model"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import Breadcrumb from "~/components/Breadcrumb.vue"; |
|||
import {useForm, useWebsite} from "~/composables/configState"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
import {getIdByParam, getPath, openSpmUrl} from "~/utils/common"; |
|||
|
|||
const route = useRoute(); |
|||
|
|||
// 页面信息 |
|||
const list = ref<Article[]>([]); |
|||
const title = ref(); |
|||
const categoryName = ref(); |
|||
const count = ref() |
|||
const links = ref<any[]>(); |
|||
const page = ref<number>(0); |
|||
const disabled = ref<boolean>(false); |
|||
const newList = ref<Article[]>(); |
|||
const layout = ref<any>(); |
|||
|
|||
// 获取状态 |
|||
const form = ref<Navigation>(); |
|||
const website = useWebsite(); |
|||
|
|||
const load = () => { |
|||
if(!disabled.value){ |
|||
page.value++; |
|||
// reload(); |
|||
} |
|||
} |
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
// 获取文章分页 |
|||
const { data: articleList } = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{ |
|||
params: { |
|||
page: page.value, |
|||
categoryId: getIdByParam() |
|||
} |
|||
}) |
|||
if(articleList.value?.data){ |
|||
list.value = articleList.value.data.list |
|||
} |
|||
|
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: getPath()}}) |
|||
if(nav.value?.data){ |
|||
form.value = nav.value?.data; |
|||
} |
|||
// 页面布局 |
|||
if(form.value?.layout){ |
|||
layout.value = JSON.parse(form.value?.layout) |
|||
useHead({ |
|||
title: `${form.value?.title} - ${website.value.websiteName}`, |
|||
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }], |
|||
bodyAttrs: { |
|||
class: "page-container", |
|||
}, |
|||
script: [ |
|||
{ |
|||
children: "console.log('Hello World')", |
|||
}, |
|||
], |
|||
}); |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
watch( |
|||
() => route, |
|||
() => { |
|||
reload(); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
|
|||
</script> |
@ -0,0 +1,48 @@ |
|||
<template> |
|||
<div class="md:w-screen-xl m-auto relative sm:flex" v-infinite-scroll="load"> |
|||
<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 cursor-pointer" @click="openSpmUrl(`https://websoft.top/detail`,item,item.articleId)"> |
|||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" /> |
|||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4"> |
|||
<p 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> |
|||
</p> |
|||
<p class="flex items-center gap-1.5 py-2 text-gray-500"> |
|||
<el-avatar :src="item.avatar" :size="20" /> |
|||
<span>{{ item.nickname }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span> |
|||
</p> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</template> |
|||
</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 dayjs from "dayjs"; |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
list?: any[]; |
|||
disabled?: boolean; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done'): void; |
|||
}>(); |
|||
|
|||
const load = () => { |
|||
if(!props.disabled){ |
|||
emit('done') |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,104 @@ |
|||
<template> |
|||
<div 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"> |
|||
<Breadcrumb :data="form" /> |
|||
<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 lg:items-center" v-if="form"> |
|||
<div class="w-full"> |
|||
<h1 |
|||
class="text-2xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-3xl lg:text-4xl"> |
|||
<span v-if="form.title">{{ form.title }}</span> |
|||
</h1> |
|||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400"> |
|||
{{ form.description }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import {useConfigInfo} from "~/composables/configState"; |
|||
import Breadcrumb from "~/components/Breadcrumb.vue"; |
|||
import type {Article} from "~/api/cms/article/model"; |
|||
import type {ArticleCategory} from "~/api/cms/category/model"; |
|||
import useFormData from "~/utils/use-form-data"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {ApiResult} from "~/api"; |
|||
|
|||
|
|||
const route = useRoute(); |
|||
const token = useToken(); |
|||
const sysDomain = useSysDomain(); |
|||
|
|||
withDefaults( |
|||
defineProps<{ |
|||
form?: Article; |
|||
title?: string; |
|||
desc?: string; |
|||
buyUrl?: string; |
|||
}>(), |
|||
{ |
|||
title: 'Templates', |
|||
desc: 'Explore community templates to get up and running in a few seconds.', |
|||
demoUrl: '/product/website', |
|||
buyUrl: 'https://github.com/websoft9/ansible-templates' |
|||
} |
|||
); |
|||
|
|||
const config = useConfigInfo(); |
|||
|
|||
|
|||
|
|||
// 配置信息 |
|||
const { form, assignFields } = useFormData<ArticleCategory>({ |
|||
categoryId: undefined, |
|||
categoryName: undefined, |
|||
parentId: undefined, |
|||
parentName: undefined, |
|||
parentPath: undefined, |
|||
parentStatus: undefined, |
|||
categoryPath: undefined, |
|||
currentTitle: undefined, |
|||
style: undefined, |
|||
title: undefined, |
|||
description: undefined |
|||
}); |
|||
|
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
const {data: response} = await useServerRequest<ApiResult<Navigation>>(`/cms/cms-article-category/${route.params.categoryId}`) |
|||
if(response.value?.data){ |
|||
assignFields(response.value.data) |
|||
form.categoryName = response.value.data.title |
|||
form.description = response.value.data.title |
|||
} |
|||
} |
|||
reload(); |
|||
|
|||
</script> |
@ -1,53 +0,0 @@ |
|||
<template> |
|||
<!-- Banner --> |
|||
<Banner :data="form" /> |
|||
|
|||
<div v-if="form" class="flex flex-col w-full md:w-screen-xl m-auto my-3"> |
|||
|
|||
<Breadcrumb :data="layout" title="文章详情" /> |
|||
|
|||
<div class="m-3 bg-white p-3 mt-4 flex flex-col gap-xl rounded-lg"> |
|||
<div class="article-title-box p-4"> |
|||
<div class="sm:text-3xl text-xl sm:text-left text-center">{{ form.title }}</div> |
|||
<div class="text-sm pt-2 text-gray-4 flex gap-xl sm:text-left sm:justify-start justify-center"> |
|||
<span>{{ form.createTime }}</span> |
|||
<span>浏览:{{ form.actualViews }}</span> |
|||
</div> |
|||
</div> |
|||
<div class="content leading-8 sm:text-xl px-3 pb-5 text-gray-700" v-html="form.content"></div> |
|||
</div> |
|||
</div> |
|||
<div v-if="!form"> |
|||
<el-empty description="404 页面不存在"></el-empty> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {Article} from "~/api/cms/article/model"; |
|||
import Breadcrumb from "~/components/Breadcrumb.vue"; |
|||
import {getIdByParam} from "~/utils/common"; |
|||
|
|||
const route = useRoute(); |
|||
|
|||
|
|||
// 页面信息 |
|||
const form = ref<Article | any>(); |
|||
const layout = ref<any>({}); |
|||
|
|||
// 请求数据 |
|||
const { data: info } = await useServerRequest<ApiResult<Article>>('/cms/cms-article/' + getIdByParam()) |
|||
form.value = info.value?.data; |
|||
form.value.num = 1; |
|||
form.value.radio = '0' |
|||
|
|||
layout.value.parentName = form.value?.categoryName; |
|||
layout.value.categoryName = form.value?.categoryName; |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.content *{ |
|||
max-width: 100%; |
|||
overflow: hidden; |
|||
} |
|||
</style> |
@ -0,0 +1,48 @@ |
|||
<template> |
|||
<div class="md:w-screen-xl m-auto relative sm:flex" v-infinite-scroll="load"> |
|||
<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 cursor-pointer" @click="openSpmUrl(`https://websoft.top/detail`,item,item.articleId)"> |
|||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" /> |
|||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4"> |
|||
<p 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> |
|||
</p> |
|||
<p class="flex items-center gap-1.5 py-2 text-gray-500"> |
|||
<el-avatar :src="item.avatar" :size="20" /> |
|||
<span>{{ item.nickname }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span> |
|||
</p> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</template> |
|||
</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 dayjs from "dayjs"; |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
list?: any[]; |
|||
disabled?: boolean; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done'): void; |
|||
}>(); |
|||
|
|||
const load = () => { |
|||
if(!props.disabled){ |
|||
emit('done') |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,52 @@ |
|||
<template> |
|||
<div class="md:w-screen-xl m-auto relative sm:flex" v-infinite-scroll="load"> |
|||
<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 cursor-pointer"> |
|||
<el-image :src="item.companyLogo" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" /> |
|||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4"> |
|||
<p 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">{{ item.tenantName }}</span> |
|||
</p> |
|||
<p class="flex items-center gap-1.5 py-2 text-gray-500 justify-between"> |
|||
<span class="text-gray-500">{{ item.comments }} </span> |
|||
</p> |
|||
<div class="button-group flex justify-center mt-3"> |
|||
<el-button class="w-full" :icon="ElIconView" @click="openSpmUrl(`https://websoft.top/case/${item.companyId}`,item,item.companyId)">查看详情</el-button> |
|||
<el-button v-if="item.price > 0" class="w-full" :icon="ElIconShoppingCart">购买</el-button> |
|||
<el-button v-else class="w-full" :icon="ElIconShoppingCart">下载</el-button> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</template> |
|||
</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 dayjs from "dayjs"; |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
list?: any[]; |
|||
disabled?: boolean; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done'): void; |
|||
}>(); |
|||
|
|||
const load = () => { |
|||
if(!props.disabled){ |
|||
emit('done') |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,96 @@ |
|||
<template> |
|||
<div 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"> |
|||
<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" v-if="layout"> |
|||
<div class=""> |
|||
<h1 |
|||
class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl"> |
|||
<span v-if="layout.title">{{ layout.title }}</span> |
|||
<span v-if="layout.name">{{ layout.name }}</span> |
|||
</h1> |
|||
|
|||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400"> |
|||
{{ layout.description }} |
|||
</div> |
|||
|
|||
<el-space class="mt-4"> |
|||
<el-button |
|||
:icon="ElIconView" |
|||
size="large" |
|||
v-if="layout.demoUrl" |
|||
@click="openSpmUrl(layout.demoUrl)" |
|||
> |
|||
演示地址 |
|||
</el-button> |
|||
<el-button |
|||
v-if="layout.buyUrl" |
|||
:icon="ElIconBottom" |
|||
size="large" |
|||
@click="openSpmUrl(layout.buyUrl)" |
|||
> |
|||
下载模版 |
|||
</el-button> |
|||
<el-button |
|||
:icon="ElIconMemo" |
|||
size="large" |
|||
v-if="layout.docUrl" |
|||
@click="openSpmUrl(layout.docUrl)" |
|||
> |
|||
帮助文档 |
|||
</el-button> |
|||
</el-space> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</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; |
|||
title?: string; |
|||
desc?: string; |
|||
buyUrl?: string; |
|||
}>(), |
|||
{ |
|||
title: 'Templates', |
|||
desc: 'Explore community templates to get up and running in a few seconds.', |
|||
demoUrl: '/product/website', |
|||
buyUrl: 'https://github.com/websoft9/ansible-templates' |
|||
} |
|||
); |
|||
|
|||
const config = useConfigInfo(); |
|||
|
|||
</script> |
@ -0,0 +1,97 @@ |
|||
<template> |
|||
<div class="text-center flex flex-col items-center py-10"> |
|||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl"> |
|||
文章推荐 |
|||
</h2> |
|||
<div class="sub-title"> |
|||
<p class="text-gray-500 dark:text-gray-400 py-3"> |
|||
拥抱开源、坚守品质;致力于打造安全稳定高可用的WEB应用! |
|||
</p> |
|||
</div> |
|||
</div> |
|||
<div class="md:w-screen-xl m-auto relative sm:flex" v-infinite-scroll="load"> |
|||
<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 cursor-pointer" @click="openSpmUrl(`https://websoft.top/detail`,item,item.articleId)"> |
|||
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" /> |
|||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4"> |
|||
<p 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> |
|||
</p> |
|||
<p class="flex items-center gap-1.5 py-2 text-gray-500"> |
|||
<el-avatar :src="item.avatar" :size="20" /> |
|||
<span>{{ item.nickname }} · {{ dayjs(item.createTime).format('MM-DD hh:mm') }}</span> |
|||
</p> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</template> |
|||
</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 dayjs from "dayjs"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {ApiResult, PageResult} from "~/api"; |
|||
import type {Article} from "~/api/cms/article/model"; |
|||
import type {CompanyParam} from "~/api/system/company/model"; |
|||
const route = useRoute(); |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
list?: any[]; |
|||
disabled?: boolean; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done'): void; |
|||
}>(); |
|||
|
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const list = ref<Article[]>([]); |
|||
const page = ref<number>(1); |
|||
const resultText = ref(''); |
|||
const disabled = ref(false); |
|||
|
|||
// 搜索表单 |
|||
const where = reactive<CompanyParam>({ |
|||
keywords: '' |
|||
}); |
|||
|
|||
const load = () => { |
|||
if(!props.disabled){ |
|||
page.value++; |
|||
reload(); |
|||
} |
|||
} |
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: { |
|||
page: page.value,limit: 8, keywords: where.keywords |
|||
}}) |
|||
if(response.value?.data){ |
|||
if (list.value.length < response.value?.data.count) { |
|||
disabled.value = false; |
|||
if (response.value?.data.list) { |
|||
list.value = list.value.concat(response.value?.data.list); |
|||
} |
|||
}else { |
|||
disabled.value = true; |
|||
} |
|||
if(response.value.data.count == 0){ |
|||
resultText.value = '暂无相关结果' |
|||
} |
|||
} |
|||
} |
|||
|
|||
reload(); |
|||
</script> |
@ -0,0 +1,96 @@ |
|||
<template> |
|||
<div 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"> |
|||
<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" v-if="layout"> |
|||
<div class=""> |
|||
<h1 |
|||
class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl"> |
|||
<span>WEBSOFT</span> |
|||
<span v-if="layout.name">{{ layout.name }}</span> |
|||
</h1> |
|||
|
|||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400"> |
|||
{{ layout.description }} |
|||
</div> |
|||
|
|||
<el-space class="mt-4"> |
|||
<el-button |
|||
:icon="ElIconView" |
|||
size="large" |
|||
v-if="layout.demoUrl" |
|||
@click="openSpmUrl(layout.demoUrl)" |
|||
> |
|||
演示地址 |
|||
</el-button> |
|||
<el-button |
|||
v-if="layout.buyUrl" |
|||
:icon="ElIconBottom" |
|||
size="large" |
|||
@click="openSpmUrl(layout.buyUrl)" |
|||
> |
|||
下载模版 |
|||
</el-button> |
|||
<el-button |
|||
:icon="ElIconMemo" |
|||
size="large" |
|||
v-if="layout.docUrl" |
|||
@click="openSpmUrl(layout.docUrl)" |
|||
> |
|||
帮助文档 |
|||
</el-button> |
|||
</el-space> |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</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; |
|||
title?: string; |
|||
desc?: string; |
|||
buyUrl?: string; |
|||
}>(), |
|||
{ |
|||
title: 'Templates', |
|||
desc: 'Explore community templates to get up and running in a few seconds.', |
|||
demoUrl: '/product/website', |
|||
buyUrl: 'https://github.com/websoft9/ansible-templates' |
|||
} |
|||
); |
|||
|
|||
const config = useConfigInfo(); |
|||
|
|||
</script> |
@ -0,0 +1,52 @@ |
|||
<template> |
|||
<div class="md:w-screen-xl m-auto relative sm:flex" v-infinite-scroll="load"> |
|||
<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 cursor-pointer"> |
|||
<el-image :src="item.companyLogo" fit="fill" :lazy="true" class="w-full cursor-pointer" /> |
|||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4"> |
|||
<p 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">{{ item.tenantName }}</span> |
|||
</p> |
|||
<p class="flex items-center gap-1.5 py-2 text-gray-500 justify-between"> |
|||
<span class="text-gray-500">{{ item.comments }} </span> |
|||
</p> |
|||
<div class="button-group flex justify-center mt-3"> |
|||
<el-button class="w-full" :icon="ElIconView" @click="openSpmUrl(`https://websoft.top/case/${item.companyId}`,item,item.companyId)">查看详情</el-button> |
|||
<el-button v-if="item.price > 0" class="w-full" :icon="ElIconShoppingCart">购买</el-button> |
|||
<el-button v-else class="w-full" :icon="ElIconShoppingCart">下载</el-button> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</template> |
|||
</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 dayjs from "dayjs"; |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
list?: any[]; |
|||
disabled?: boolean; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done'): void; |
|||
}>(); |
|||
|
|||
const load = () => { |
|||
if(!props.disabled){ |
|||
emit('done') |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,80 @@ |
|||
<template> |
|||
<div class="text-center flex flex-col items-center py-10"> |
|||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl"> |
|||
插件市场 |
|||
</h2> |
|||
<div class="sub-title"> |
|||
<p class="text-gray-500 dark:text-gray-400 py-3"> |
|||
安装插件,几秒钟内即可启动并运行 |
|||
</p> |
|||
</div> |
|||
</div> |
|||
<div class="md:w-screen-xl m-auto relative sm:flex"> |
|||
<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 cursor-pointer"> |
|||
<el-image |
|||
:src="`https://oss.wsdns.cn/20240925/e5e47100f4b6471395b3b81f834d2902.jpg?x-oss-process=image/resize,m_fixed,w_750/quality,Q_90`" |
|||
fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer"/> |
|||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4"> |
|||
<p 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">{{ item.plugName }}</div> |
|||
<div class="text-red-500">¥{{ item.price }}</div> |
|||
</p> |
|||
<p v-if="item.price > 0" class="flex items-center gap-1.5 py-2 text-gray-500 justify-between"> |
|||
<div class="text-gray-500">{{ item.comments }}</div> |
|||
</p> |
|||
<div class="button-group flex justify-center mt-3"> |
|||
<el-button class="w-full" :icon="ElIconView">查看详情</el-button> |
|||
<el-button type="primary" v-if="item.price > 0" class="w-full" :icon="ElIconShoppingCart">购买 |
|||
</el-button> |
|||
<el-button v-else class="w-full" :icon="ElIconShoppingCart">下载</el-button> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</template> |
|||
</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 dayjs from "dayjs"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {ApiResult, PageResult} from "~/api"; |
|||
import type {Plug} from "~/api/system/plug/model"; |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
disabled?: boolean; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done'): void; |
|||
}>(); |
|||
|
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const list = ref<Plug[]>([]); |
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
const {data: response} = await useServerRequest<ApiResult<PageResult<Plug>>>('/system/plug/page', { |
|||
baseURL: runtimeConfig.public.apiServer, params: { |
|||
limit: 8 |
|||
} |
|||
}) |
|||
if (response.value?.data) { |
|||
if (response.value?.data.list) { |
|||
list.value = response.value?.data.list; |
|||
} |
|||
} |
|||
} |
|||
reload(); |
|||
</script> |
@ -0,0 +1,80 @@ |
|||
<template> |
|||
<div class="text-center flex flex-col items-center pb-10"> |
|||
<h2 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl"> |
|||
产品服务 |
|||
</h2> |
|||
<div class="sub-title"> |
|||
<p class="text-gray-500 dark:text-gray-400 py-3"> |
|||
拥抱开源、坚守品质;致力于打造安全稳定高可用的WEB应用! |
|||
</p> |
|||
</div> |
|||
</div> |
|||
<div class="md:w-screen-xl m-auto relative sm:flex"> |
|||
<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 cursor-pointer"> |
|||
<el-image |
|||
:src="`https://oss.wsdns.cn/20240925/e5e47100f4b6471395b3b81f834d2902.jpg?x-oss-process=image/resize,m_fixed,w_750/quality,Q_90`" |
|||
fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer"/> |
|||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4"> |
|||
<p 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">{{ item.goodsName }}</div> |
|||
<div class="text-red-500">¥{{ item.price }}</div> |
|||
</p> |
|||
<p v-if="item.price > 0" class="flex items-center gap-1.5 py-2 text-gray-500 justify-between"> |
|||
<div class="text-gray-500">{{ item.comments }}</div> |
|||
</p> |
|||
<div class="button-group flex justify-center mt-3"> |
|||
<el-button class="w-full" :icon="ElIconView">查看详情</el-button> |
|||
<el-button type="primary" v-if="item.price > 0" class="w-full" :icon="ElIconShoppingCart">购买 |
|||
</el-button> |
|||
<el-button v-else class="w-full" :icon="ElIconShoppingCart">下载</el-button> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</template> |
|||
</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 dayjs from "dayjs"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import type {ApiResult, PageResult} from "~/api"; |
|||
import type {Goods} from "~/api/shop/goods/model"; |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
disabled?: boolean; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done'): void; |
|||
}>(); |
|||
|
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const list = ref<Goods[]>([]); |
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
const {data: response} = await useServerRequest<ApiResult<PageResult<Goods>>>('/shop/shop-goods/page', { |
|||
baseURL: runtimeConfig.public.apiServer, params: { |
|||
limit: 8 |
|||
} |
|||
}) |
|||
if (response.value?.data) { |
|||
if (response.value?.data.list) { |
|||
list.value = response.value?.data.list; |
|||
} |
|||
} |
|||
} |
|||
reload(); |
|||
</script> |
@ -1,105 +0,0 @@ |
|||
<template> |
|||
<!-- Banner --> |
|||
<Banner :layout="layout" /> |
|||
|
|||
<div class="container md:w-screen-xl m-auto"> |
|||
<div class="flex flex-col" v-if="app"> |
|||
<Breadcrumb :data="form" :title="`应用详情`" /> |
|||
<div class="page-main w-full bg-white rounded-lg"> |
|||
<div class="p-4 leading-7"> |
|||
<div class="goods-info flex"> |
|||
<el-avatar :src="app.appIcon" class="mr-2" size="large" /> |
|||
<div class="flex flex-col ml-2"> |
|||
<div class="text-lg">{{ app.appName }}</div> |
|||
<div class="text-gray-400 w-screen-lg">{{ app.comments }}</div> |
|||
<div>应用ID: <span class="text-blue-500">{{ app.appId }}</span></div> |
|||
<div>后台演示: <span class="text-blue-500">{{ app.adminUrl }}</span></div> |
|||
<div>前端演示: <span class="text-blue-500">{{ app.appUrl }}</span></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 空白页 --> |
|||
<div class="mt-[60px]" v-if="!app"> |
|||
<el-empty description="404 页面不存在"></el-empty> |
|||
</div> |
|||
|
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { Search } from '@element-plus/icons-vue' |
|||
import type {ApiResult, PageResult} from "~/api"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useWebsite} from "~/composables/configState"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
import {getIdByParam, getPath} from "~/utils/common"; |
|||
import type {Company, CompanyParam} from "~/api/system/company/model"; |
|||
import {navigateTo} from "#imports"; |
|||
import Breadcrumb from "~/components/Breadcrumb.vue"; |
|||
import type {App} from "~/api/oa/app/model"; |
|||
|
|||
const route = useRoute(); |
|||
|
|||
// 页面信息 |
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const resultText = ref(''); |
|||
const layout = ref<any>(); |
|||
|
|||
// 获取状态 |
|||
const form = ref<Navigation>(); |
|||
const website = useWebsite(); |
|||
const app = ref<App>() |
|||
|
|||
// 搜索表单 |
|||
const where = reactive<CompanyParam>({ |
|||
keywords: '' |
|||
}); |
|||
|
|||
const navTo = (item: Company) => { |
|||
navigateTo(`/market/${item.companyId}`) |
|||
} |
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
|
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: getPath()}}) |
|||
if(nav.value?.data){ |
|||
form.value = nav.value?.data; |
|||
} |
|||
// 页面布局 |
|||
if(form.value?.layout){ |
|||
layout.value = JSON.parse(form.value?.layout) |
|||
} |
|||
useHead({ |
|||
title: `搜索结果 - ${website.value.websiteName}`, |
|||
bodyAttrs: { |
|||
class: "page-container", |
|||
} |
|||
}); |
|||
await handleClick() |
|||
} |
|||
|
|||
// 搜索结果 |
|||
const handleClick = async () => { |
|||
const {data: response} = await useServerRequest<ApiResult<App>>(`/oa/oa-app/${getIdByParam()}`,{baseURL: runtimeConfig.public.apiServer}) |
|||
if(response.value?.data){ |
|||
if (response.value.data) { |
|||
app.value = response.value.data |
|||
} |
|||
if(!response.value){ |
|||
resultText.value = '暂无相关结果' |
|||
} |
|||
} |
|||
} |
|||
|
|||
watch( |
|||
() => route, |
|||
() => { |
|||
reload(); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
|
|||
</script> |
@ -0,0 +1,53 @@ |
|||
<template> |
|||
<div class="md:w-screen-xl m-auto relative sm:flex" v-infinite-scroll="load"> |
|||
<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 cursor-pointer"> |
|||
<el-image :src="`https://oss.wsdns.cn/20240925/e5e47100f4b6471395b3b81f834d2902.jpg?x-oss-process=image/resize,m_fixed,w_750/quality,Q_90`" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" /> |
|||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4"> |
|||
<p 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">{{ item.plugName }}</span> |
|||
<div class="text-red-500" v-if="item.price > 0">¥{{ item.price }} </div> |
|||
</p> |
|||
<p class="flex items-center gap-1.5 py-2 text-gray-500 justify-between"> |
|||
<div class="text-gray-500">{{ item.comments }} </div> |
|||
</p> |
|||
<div class="button-group flex justify-center mt-3"> |
|||
<el-button class="w-full" @click="navigateTo(`/market/detail/${item.plugId}`)">查看详情</el-button> |
|||
<el-button type="primary" v-if="item.price > 0" class="w-full" :icon="ElIconShoppingCart">购买</el-button> |
|||
<el-button v-else class="w-full">下载插件</el-button> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</template> |
|||
</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 dayjs from "dayjs"; |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
list?: any[]; |
|||
disabled?: boolean; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done'): void; |
|||
}>(); |
|||
|
|||
const load = () => { |
|||
if(!props.disabled){ |
|||
emit('done') |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,55 @@ |
|||
<template> |
|||
<div class="md:w-screen-xl m-auto relative sm:flex" v-infinite-scroll="load"> |
|||
<el-row :gutter="24" class="flex"> |
|||
<template v-for="(item,index) in list" :key="index"> |
|||
<el-col :span="24" class="mb-5 min-w-xs"> |
|||
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50 cursor-pointer" @click="openSpmUrl(`https://websoft.top/market/detail`,item,item.articleId)"> |
|||
<div class="flex"> |
|||
<el-image :src="item.appIcon" fit="fill" :lazy="true" class="w-[150px] h-[150px] cursor-pointer" /> |
|||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4"> |
|||
<p 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">{{ item.appName }}</span> |
|||
</p> |
|||
<p class="flex flex-col gap-1.5 py-2 text-gray-500 justify-between"> |
|||
<span class="text-red-500">¥{{ item.price }} </span> |
|||
<span class="text-gray-500">{{ item.comments }} </span> |
|||
</p> |
|||
<div class="button-group flex mt-3"> |
|||
<el-button class="">演示</el-button> |
|||
<el-button class="">购买</el-button> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</template> |
|||
</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 dayjs from "dayjs"; |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
list?: any[]; |
|||
disabled?: boolean; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done'): void; |
|||
}>(); |
|||
|
|||
const load = () => { |
|||
if(!props.disabled){ |
|||
emit('done') |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,125 @@ |
|||
<template> |
|||
<div 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"> |
|||
<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" v-if="layout"> |
|||
<div class=""> |
|||
<h1 |
|||
class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-4xl lg:text-5xl"> |
|||
<span v-if="layout.title">{{ layout.title }}</span> |
|||
<span v-if="layout.name">{{ layout.name }}</span> |
|||
</h1> |
|||
|
|||
<div class="mt-4 text-lg text-gray-500 dark:text-gray-400"> |
|||
{{ layout.description }} |
|||
</div> |
|||
<div class="flex justify-between w-full items-center mt-4"> |
|||
<el-space> |
|||
<el-button |
|||
:icon="ElIconView" |
|||
size="large" |
|||
v-if="layout.demoUrl" |
|||
@click="openSpmUrl(layout.demoUrl)" |
|||
> |
|||
演示地址 |
|||
</el-button> |
|||
<el-button |
|||
v-if="layout.buyUrl" |
|||
size="large" |
|||
@click="openSpmUrl(`https://${sysDomain}/token-login`)" |
|||
> |
|||
创建应用 |
|||
</el-button> |
|||
<el-input |
|||
v-model="where.keywords" |
|||
class="w-full" |
|||
style="width: 360px" |
|||
size="large" |
|||
placeholder="搜索" |
|||
:prefix-icon="Search" |
|||
@keydown.enter="handleClick" |
|||
> |
|||
<template #append> |
|||
<el-button size="large" type="primary" @click="handleClick">搜索</el-button> |
|||
</template> |
|||
</el-input> |
|||
</el-space> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { Search } from '@element-plus/icons-vue' |
|||
import {useConfigInfo} from "~/composables/configState"; |
|||
import {openSpmUrl} from "~/utils/common"; |
|||
import type {CompanyParam} from "~/api/system/company/model"; |
|||
|
|||
const token = useToken(); |
|||
const sysDomain = useSysDomain(); |
|||
|
|||
withDefaults( |
|||
defineProps<{ |
|||
layout?: any; |
|||
title?: string; |
|||
desc?: string; |
|||
buyUrl?: string; |
|||
}>(), |
|||
{ |
|||
title: 'Templates', |
|||
desc: 'Explore community templates to get up and running in a few seconds.', |
|||
demoUrl: '/product/website', |
|||
buyUrl: 'https://github.com/websoft9/ansible-templates' |
|||
} |
|||
); |
|||
|
|||
// 搜索表单 |
|||
const where = reactive<CompanyParam>({ |
|||
keywords: '' |
|||
}); |
|||
|
|||
const config = useConfigInfo(); |
|||
// 搜索结果 |
|||
const handleClick = async () => { |
|||
if (where.keywords == '') { |
|||
return false; |
|||
} |
|||
if(activeName.value == 'web'){ |
|||
const {data: response} = await useServerRequest<ApiResult<PageResult<Website>>>('/cms/cms-website/page',{baseURL: runtimeConfig.public.apiServer, params: { |
|||
page: page.value, keywords: where.keywords |
|||
}}) |
|||
if(response.value?.data){ |
|||
if (response.value?.data.list) { |
|||
websites.value = response.value?.data.list; |
|||
} |
|||
if(response.value.data.count == 0){ |
|||
resultText.value = '暂无相关结果' |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,114 @@ |
|||
<!-- 文章详情 --> |
|||
<template> |
|||
<PageContainer :form="form" /> |
|||
|
|||
<div class="page-main md:w-screen-xl m-auto bg-white rounded-lg"> |
|||
<div class="p-4 leading-7" v-html="form?.content"> |
|||
</div> |
|||
</div> |
|||
|
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useWebsite} from "~/composables/configState"; |
|||
import type {BreadcrumbItem} from "~/types/global"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
import {getIdBySpm} from "~/utils/common"; |
|||
import type {Article} from "~/api/cms/article/model"; |
|||
import useFormData from "~/utils/use-form-data"; |
|||
|
|||
// 引入状态管理 |
|||
const route = useRoute(); |
|||
const website = useWebsite(); |
|||
const breadcrumb = ref<BreadcrumbItem>(); |
|||
|
|||
|
|||
// 配置信息 |
|||
const { form, assignFields } = useFormData<Article>({ |
|||
// 文章id |
|||
articleId: undefined, |
|||
// 文章标题 |
|||
title: undefined, |
|||
// 分类类型 |
|||
type: undefined, |
|||
// 展现方式 |
|||
showType: undefined, |
|||
// 文章类型 |
|||
categoryId: undefined, |
|||
// 文章分类 |
|||
categoryName: undefined, |
|||
parentId: undefined, |
|||
parentName: undefined, |
|||
// 封面图 |
|||
image: undefined, |
|||
// 附件 |
|||
files: undefined, |
|||
// 缩列图 |
|||
thumbnail: undefined, |
|||
// 视频地址 |
|||
video: undefined, |
|||
// 上传的文件类型 |
|||
accept: undefined, |
|||
// 来源 |
|||
source: undefined, |
|||
// 文章内容 |
|||
content: undefined, |
|||
// 虚拟阅读量 |
|||
virtualViews: undefined, |
|||
// 实际阅读量 |
|||
actualViews: undefined, |
|||
// 用户ID |
|||
userId: undefined, |
|||
// 用户昵称 |
|||
nickname: undefined, |
|||
// 账号 |
|||
username: undefined, |
|||
// 用户头像 |
|||
userAvatar: undefined, |
|||
// 所属门店ID |
|||
shopId: undefined, |
|||
// |
|||
likes: undefined, |
|||
// 排序 |
|||
sortNumber: undefined, |
|||
// 备注 |
|||
comments: undefined, |
|||
// 状态 |
|||
status: undefined, |
|||
// 创建时间 |
|||
createTime: undefined, |
|||
// 更新时间 |
|||
updateTime: undefined |
|||
}); |
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
|
|||
// 存在spm(优先级高) |
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-article/' + getIdBySpm(5)) |
|||
if (nav.value?.data) { |
|||
assignFields(nav.value.data) |
|||
} |
|||
|
|||
// seo |
|||
useHead({ |
|||
title: `${form.title} - ${website.value.websiteName}`, |
|||
bodyAttrs: { |
|||
class: "page-container", |
|||
} |
|||
}); |
|||
// 面包屑 |
|||
breadcrumb.value = form |
|||
} |
|||
|
|||
watch( |
|||
() => route.path, |
|||
(path) => { |
|||
console.log(path,'=>Path') |
|||
|
|||
reload(); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
@ -0,0 +1,99 @@ |
|||
<template> |
|||
<div 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"> |
|||
<Breadcrumb :data="form" /> |
|||
<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 lg:items-center" v-if="form"> |
|||
<div class="w-full"> |
|||
<h1 |
|||
class="text-2xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-3xl lg:text-4xl"> |
|||
<span v-if="form.title">{{ form.title }}</span> |
|||
</h1> |
|||
|
|||
<div class="mt-4 text-gray-500 dark:text-gray-400"> |
|||
<span>{{ form.nickname }}</span> · {{ form.createTime }} |
|||
</div> |
|||
|
|||
<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> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import {useConfigInfo} from "~/composables/configState"; |
|||
import {openSpmUrl} from "~/utils/common"; |
|||
import Breadcrumb from "~/components/Breadcrumb.vue"; |
|||
import type {Article} from "~/api/cms/article/model"; |
|||
|
|||
|
|||
const token = useToken(); |
|||
const sysDomain = useSysDomain(); |
|||
|
|||
withDefaults( |
|||
defineProps<{ |
|||
form?: Article; |
|||
title?: string; |
|||
desc?: string; |
|||
buyUrl?: string; |
|||
}>(), |
|||
{ |
|||
title: 'Templates', |
|||
desc: 'Explore community templates to get up and running in a few seconds.', |
|||
demoUrl: '/product/website', |
|||
buyUrl: 'https://github.com/websoft9/ansible-templates' |
|||
} |
|||
); |
|||
|
|||
const config = useConfigInfo(); |
|||
|
|||
</script> |
@ -0,0 +1,76 @@ |
|||
<template> |
|||
<!-- Banner --> |
|||
<Banner :layout="layout" /> |
|||
|
|||
<!-- 简单模式(常规单页面) --> |
|||
<PageContainer :form="form" :layout="layout" /> |
|||
|
|||
<!-- 高级模式(自定义组件) --> |
|||
<div class="flex flex-col" v-if="layout?.showLayout"> |
|||
{{ layout }} |
|||
</div> |
|||
|
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult} from "~/api"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/configState"; |
|||
import type {BreadcrumbItem} from "~/types/global"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
import {getIdBySpm} from "~/utils/common"; |
|||
import PageContainer from "~/components/PageContainer.vue"; |
|||
|
|||
// 引入状态管理 |
|||
const route = useRoute(); |
|||
const website = useWebsite(); |
|||
const layout = ref<any>(); |
|||
const config = useConfigInfo(); |
|||
const token = useToken(); |
|||
const form = useForm(); |
|||
const breadcrumb = ref<BreadcrumbItem>(); |
|||
|
|||
// 请求数据 |
|||
const reload = async () => { |
|||
|
|||
// 存在spm(优先级高) |
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/' + getIdBySpm(5)) |
|||
if (nav.value?.data) { |
|||
form.value = nav.value.data |
|||
}else{ |
|||
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}}) |
|||
if(nav.value?.data){ |
|||
form.value = nav.value?.data; |
|||
} |
|||
} |
|||
// 页面布局 |
|||
if(form.value?.layout){ |
|||
layout.value = JSON.parse(form.value?.layout) |
|||
} |
|||
|
|||
// seo |
|||
useHead({ |
|||
title: `${form.value.title} - ${website.value.websiteName}`, |
|||
meta: [{ name: form.value.design?.keywords, content: form.value.design?.description }], |
|||
bodyAttrs: { |
|||
class: "page-container", |
|||
}, |
|||
script: [ |
|||
{ |
|||
children: `console.log(${JSON.stringify(form.value)})`, |
|||
}, |
|||
], |
|||
}); |
|||
// 面包屑 |
|||
breadcrumb.value = form.value |
|||
} |
|||
|
|||
watch( |
|||
() => route.path, |
|||
(path) => { |
|||
console.log(path,'=>Path') |
|||
|
|||
reload(); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
@ -0,0 +1,53 @@ |
|||
<template> |
|||
<div class="md:w-screen-xl m-auto relative sm:flex" v-infinite-scroll="load"> |
|||
<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 cursor-pointer"> |
|||
<el-image :src="`https://oss.wsdns.cn/20240925/e5e47100f4b6471395b3b81f834d2902.jpg?x-oss-process=image/resize,m_fixed,w_750/quality,Q_90`" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" /> |
|||
<div class="flex-1 px-4 py-5 sm:p-6 !p-4"> |
|||
<p 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">{{ item.goodsName }}</div> |
|||
<div class="text-red-500">¥{{ item.price }} </div> |
|||
</p> |
|||
<p v-if="item.price > 0" class="flex items-center gap-1.5 py-2 text-gray-500 justify-between"> |
|||
<div class="text-gray-500">{{ item.comments }}</div> |
|||
</p> |
|||
<div class="button-group flex justify-center mt-3"> |
|||
<el-button class="w-full" :icon="ElIconView">查看详情</el-button> |
|||
<el-button type="primary" v-if="item.price > 0" class="w-full" :icon="ElIconShoppingCart">购买</el-button> |
|||
<el-button v-else class="w-full" :icon="ElIconShoppingCart">下载</el-button> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</template> |
|||
</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 dayjs from "dayjs"; |
|||
|
|||
const props = withDefaults( |
|||
defineProps<{ |
|||
list?: any[]; |
|||
disabled?: boolean; |
|||
}>(), |
|||
{} |
|||
); |
|||
|
|||
const emit = defineEmits<{ |
|||
(e: 'done'): void; |
|||
}>(); |
|||
|
|||
const load = () => { |
|||
if(!props.disabled){ |
|||
emit('done') |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,92 @@ |
|||
<template> |
|||
<PageBanner :layout="layout" /> |
|||
<CardList :list="list" :disabled="disabled" @done="onSearch" /> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import type {ApiResult, PageResult} from "~/api"; |
|||
import {useServerRequest} from "~/composables/useServerRequest"; |
|||
import {useWebsite} from "~/composables/configState"; |
|||
import type {Navigation} from "~/api/cms/navigation/model"; |
|||
import type {CompanyParam} from "~/api/system/company/model"; |
|||
import type {Tenant} from "~/api/system/tenant/model"; |
|||
import type {Article} from "~/api/cms/article/model"; |
|||
import CardList from './components/CardList.vue'; |
|||
import type {Goods} from "~/api/shop/goods/model"; |
|||
|
|||
const route = useRoute(); |
|||
|
|||
// 页面信息 |
|||
const runtimeConfig = useRuntimeConfig(); |
|||
const list = ref<Goods[]>([]); |
|||
const page = ref<number>(1); |
|||
const resultText = ref(''); |
|||
const layout = ref<any>(); |
|||
const disabled = ref<boolean>(false); |
|||
const activeName = ref(undefined) |
|||
|
|||
// 获取状态 |
|||
const form = ref<Navigation>(); |
|||
const website = useWebsite(); |
|||
|
|||
// 搜索表单 |
|||
const where = reactive<CompanyParam>({ |
|||
keywords: '' |
|||
}); |
|||
|
|||
const handleClick = (e:any) => { |
|||
console.log(e.index) |
|||
} |
|||
|
|||
const onSearch = () => { |
|||
if(!disabled.value){ |
|||
page.value++; |
|||
reload(route.path); |
|||
} |
|||
} |
|||
|
|||
// 请求数据 |
|||
const reload = async (path: string) => { |
|||
const {data: response} = await useServerRequest<ApiResult<PageResult<Goods>>>('/shop/shop-goods/page',{baseURL: runtimeConfig.public.apiServer, params: { |
|||
page: page.value, |
|||
limit: 8, |
|||
keywords: where.keywords |
|||
}}) |
|||
if(response.value?.data){ |
|||
if (list.value.length < response.value?.data.count) { |
|||
disabled.value = false; |
|||
if (response.value?.data.list) { |
|||
list.value = list.value.concat(response.value?.data.list); |
|||
} |
|||
}else { |
|||
disabled.value = true; |
|||
} |
|||
if(response.value.data.count == 0){ |
|||
resultText.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; |
|||
} |
|||
// 页面布局 |
|||
if(form.value?.layout){ |
|||
layout.value = JSON.parse(form.value?.layout) |
|||
} |
|||
|
|||
useHead({ |
|||
title: `产品 - ${website.value.websiteName}`, |
|||
bodyAttrs: { |
|||
class: "page-container", |
|||
} |
|||
}); |
|||
|
|||
watch( |
|||
() => route.path, |
|||
(path) => { |
|||
reload(path); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
</script> |
Binary file not shown.
Loading…
Reference in new issue