Browse Source

修复已知问题

master
科技小王子 8 months ago
parent
commit
c535291391
  1. 4
      api/cms/category/model/index.ts
  2. 14
      components/CardList.vue
  3. 16
      components/PageBanner.vue
  4. 90
      pages/article/[categoryId].vue
  5. 156
      pages/article/[id].vue
  6. 48
      pages/article/components/CardList.vue
  7. 104
      pages/article/components/PageBanner.vue
  8. 53
      pages/article/detail/[id].vue
  9. 48
      pages/ask/components/CardList.vue
  10. 96
      pages/ask/index.vue
  11. 52
      pages/case/components/CardList.vue
  12. 96
      pages/case/components/Header.vue
  13. 128
      pages/case/index.vue
  14. 97
      pages/components/ArticleList.vue
  15. 96
      pages/components/Banner.vue
  16. 52
      pages/components/CardList.vue
  17. 80
      pages/components/PlugList.vue
  18. 80
      pages/components/ProductList.vue
  19. 34
      pages/detail/components/Header.vue
  20. 13
      pages/index.vue
  21. 105
      pages/market/[id].vue
  22. 53
      pages/market/components/CardList.vue
  23. 55
      pages/market/components/CardList2.vue
  24. 125
      pages/market/components/PageBanner.vue
  25. 114
      pages/market/detail/[plugId].vue
  26. 99
      pages/market/detail/components/Header.vue
  27. 97
      pages/market/index.vue
  28. 76
      pages/page/index.vue
  29. 186
      pages/product/[id].vue
  30. 53
      pages/product/components/CardList.vue
  31. 92
      pages/product/index.vue
  32. BIN
      归档.zip

4
api/cms/category/model/index.ts

@ -6,6 +6,8 @@ import type { PageParam } from '@/api';
export interface ArticleCategory {
// 文章分类id
categoryId?: number;
// 文章分类名称
categoryName?: string;
// 分类类型 0列表 1页面 2链接
type?: number;
// 文章分类
@ -22,6 +24,8 @@ export interface ArticleCategory {
pageName?: string;
// 上级分类
parentId?: number;
// 上级分类名称
parentName?: string;
// 封面图
avatar?: string;
// 用户ID

14
components/CardList.vue

@ -3,17 +3,16 @@
<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' }">
<el-image src="https://nuxt.com/_vercel/image?url=%2Fassets%2Ftemplates%2Fwoonuxt.png&w=640&q=100"
class="w-full cursor-pointer" @click="navigateTo(`/product/website`)" />
<el-card shadow="hover" :body-style="{ padding: '0px' }" class="hover:bg-gray-50">
<el-image :src="item.image" fit="fill" :lazy="true" class="w-full h-[150px] cursor-pointer" @click="openSpmUrl(`https://websoft.top/detail`,item,item.articleId)" />
<div class="flex-1 px-4 py-5 sm:p-6 !p-4">
<p class="text-gray-900 dark:text-white text-base font-semibold truncate flex items-center gap-1.5">
<a class="flex-1 truncate cursor-pointer" @click="navigateTo(`/product/website`)">模版名称</a>
<p class="text-gray-900 dark:text-white text-base font-semibold flex items-center gap-1.5">
<a class="flex-1 cursor-pointer" @click="openSpmUrl(`https://websoft.top/detail`,item,item.articleId)">{{ item.title }}</a>
</p>
<div class="text-[15px] text-gray-500 dark:text-gray-400 mt-1 line-clamp-2 sm:min-h-[45px]">
使用NuxtVue 构建的web模版
{{ item.comments }}
</div>
<div class="button-group flex justify-center">
<div class="button-group flex justify-center mt-3">
<el-button class="w-full" :icon="ElIconView">演示</el-button>
<el-button class="w-full" :icon="ElIconShoppingCart">购买</el-button>
</div>
@ -22,7 +21,6 @@
</el-col>
</template>
</el-row>
</div>
</template>

16
components/PageBanner.vue

@ -52,14 +52,14 @@
>
下载模版
</el-button>
<el-button
:icon="ElIconMemo"
size="large"
v-if="layout.docUrl"
@click="openSpmUrl(layout.docUrl)"
>
帮助文档
</el-button>
<!-- <el-button-->
<!-- :icon="ElIconMemo"-->
<!-- size="large"-->
<!-- v-if="layout.docUrl"-->
<!-- @click="openSpmUrl(layout.docUrl)"-->
<!-- >-->
<!-- 帮助文档-->
<!-- </el-button>-->
</el-space>
</div>

90
pages/article/[categoryId].vue

@ -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>

156
pages/article/[id].vue

@ -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>

48
pages/article/components/CardList.vue

@ -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>

104
pages/article/components/PageBanner.vue

@ -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>

53
pages/article/detail/[id].vue

@ -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>

48
pages/ask/components/CardList.vue

@ -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>

96
pages/ask/index.vue

@ -1,29 +1,38 @@
<template>
<PageBanner :layout="layout" />
<CardList :list="list" />
<el-tabs v-model="activeName" class="md:w-screen-xl m-auto relative sm:flex pb-2" @tab-click="handleClick">
<el-tab-pane label="轻松一刻" name="happy"></el-tab-pane>
<el-tab-pane label="企业官网" name="website"></el-tab-pane>
<el-tab-pane label="企业商城" name="weShop"></el-tab-pane>
<el-tab-pane label="内容管理" name="cms"></el-tab-pane>
<el-tab-pane label="点餐外卖" name="food"></el-tab-pane>
<el-tab-pane label="派单系统" name="task"></el-tab-pane>
<el-tab-pane label="办公OA" name="oa"></el-tab-pane>
<el-tab-pane label="问答" name="ask"></el-tab-pane>
<el-tab-pane label="分享" name="share"></el-tab-pane>
</el-tabs>
<CardList :list="list" :disabled="disabled" @done="onSearch" />
</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 {getPath} from "~/utils/common";
import type {CompanyParam} from "~/api/system/company/model";
import type {Tenant} from "~/api/system/tenant/model";
import type {Website} from "~/api/cms/website/model";
import type {Article} from "~/api/cms/article/model";
import CardList from './components/CardList.vue';
const route = useRoute();
//
const runtimeConfig = useRuntimeConfig();
const list = ref<Tenant[]>([]);
const page = ref<number>(0);
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>();
@ -34,59 +43,52 @@ const where = reactive<CompanyParam>({
keywords: ''
});
const navTo = (item: Website) => {
if(item.domain){
openSpmUrl(`https://${item.domain}`,item,item.tenantId)
return;
const handleClick = (e:any) => {
console.log(e.index)
}
const onSearch = () => {
if(!disabled.value){
page.value++;
reload(route.path);
}
openSpmUrl(`https://${item.websiteCode}.wsdns.cn`,item,item.tenantId)
}
//
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, keywords: where.keywords
page: page.value,limit: 8, keywords: where.keywords
}})
if(response.value?.data){
if (response.value?.data.list) {
list.value = response.value?.data.list;
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}})
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",
}
});
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
if(nav.value?.data){
form.value = nav.value?.data;
}
//
// //
// const handleClick = async () => {
// const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
// page: page.value, keywords: where.keywords
// }})
// if(response.value?.data){
// if (response.value?.data.list) {
// list.value = response.value?.data.list;
// }
// if(response.value.data.count == 0){
// resultText.value = ''
// }
// }
// }
//
if(form.value?.layout){
layout.value = JSON.parse(form.value?.layout)
}
useHead({
title: `社区 - ${website.value.websiteName}`,
bodyAttrs: {
class: "page-container",
}
});
watch(
() => route.path,
@ -95,6 +97,4 @@ watch(
},
{ immediate: true }
);
</script>

52
pages/case/components/CardList.vue

@ -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>

96
pages/case/components/Header.vue

@ -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>

128
pages/case/index.vue

@ -1,74 +1,28 @@
<template>
<div v-infinite-scroll="load" class="login-layout mt-[100px] min-h-2xl m-auto sm:w-screen-sm w-full">
<div class="title text-xl text-gray-700 px-4 py-2 font-500 text-center">
<div class="sm:w-screen-sm w-full">
<el-input
v-model="where.keywords"
class="w-full"
size="large"
placeholder="搜索"
:prefix-icon="Search"
@keydown.enter="reload"
>
<template #append>
<el-button size="large" type="primary" @click="reload">搜索</el-button>
</template>
</el-input>
<el-tabs v-model="activeName" class="my-3" @tab-click="handleClick">
<el-tab-pane label="应用" name="web"></el-tab-pane>
<el-tab-pane label="公众号" name="mp-official"></el-tab-pane>
<el-tab-pane label="小程序" name="mp-weixin"></el-tab-pane>
<el-tab-pane label="移动应用" name="app"></el-tab-pane>
<el-tab-pane label="小商店" name="mp-shop"></el-tab-pane>
<el-tab-pane label="其他" name="other"></el-tab-pane>
</el-tabs>
</div>
</div>
<template v-if="activeName === 'web'">
<div class="search bg-white rounded-lg p-3 w-full" v-if="list.length > 0">
<div class="title text-gray-400 px-4 py-2 mb-4">搜索结果</div>
<div class="result px-3">
<div v-for="(item,index) in list" :key="index" class="app-item block border-solid rounded-lg border-gray-300 border-1 mb-4 p-3 flex flex-row items-center hover:border-blue-4 hover:border-2 cursor-pointer">
<el-space @click="navTo(item)">
<div class="avatar flex flex-col items-center justify-center">
<el-avatar :src="item.appIcon" style="background-color: #f3f3f3" size="large" />
</div>
<div class="app-info flex flex-col">
<div class="text-lg">{{ item.appName }}</div>
<div class="text-gray-400">{{ item.comments }}</div>
<div class="text-gray-300 text-xs-1">{{ item.companyName }}</div>
</div>
</el-space>
</div>
</div>
</div>
</template>
<div v-else class="px-1 text-center text-gray-500 min-h-xs">
{{ resultText }}
</div>
</div>
<Header :layout="layout" />
<CardList :list="list" :disabled="disabled" @done="onSearch" />
</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 {getPath} from "~/utils/common";
import type {CompanyParam} from "~/api/system/company/model";
import {navigateTo} from "#imports";
import type {App} from "~/api/oa/app/model";
import type {Tenant} from "~/api/system/tenant/model";
import type {Article} from "~/api/cms/article/model";
import CardList from './components/CardList.vue';
import Header from "./components/Header.vue";
const route = useRoute();
//
const runtimeConfig = useRuntimeConfig();
const list = ref<App[]>([]);
const disabled = ref<boolean>(false);
const activeName = ref('web');
const list = ref<Tenant[]>([]);
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>();
@ -79,48 +33,25 @@ const where = reactive<CompanyParam>({
keywords: ''
});
const load = () => {
const handleClick = (e:any) => {
console.log(e.index)
}
const onSearch = () => {
if(!disabled.value){
console.log('load>>>')
page.value++;
handleClick();
reload(route.path);
}
}
const navTo = (item: App) => {
navigateTo(`/market/${item.appId}`)
}
//
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",
}
});
page.value = 1;
list.value = [];
await handleClick()
}
//
const handleClick = async () => {
const {data: response} = await useServerRequest<ApiResult<PageResult<App>>>('/oa/oa-app/page',{baseURL: runtimeConfig.public.apiServer, params: {
const reload = async (path: string) => {
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/system/company/pageAll',{baseURL: runtimeConfig.public.apiServer, params: {
page: page.value,
limit: 8,
keywords: where.keywords
}})
if(response.value?.data){
console.log(response.value?.data.count,'count>')
if (list.value.length < response.value?.data.count) {
disabled.value = false;
if (response.value?.data.list) {
@ -135,12 +66,27 @@ const handleClick = async () => {
}
}
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
if(nav.value?.data){
form.value = nav.value?.data;
}
//
if(form.value?.layout){
layout.value = JSON.parse(form.value?.layout)
}
useHead({
title: `产品 - ${website.value.websiteName}`,
bodyAttrs: {
class: "page-container",
}
});
watch(
() => route,
() => {
reload();
() => route.path,
(path) => {
reload(path);
},
{ immediate: true }
);
</script>

97
pages/components/ArticleList.vue

@ -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>

96
pages/components/Banner.vue

@ -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>

52
pages/components/CardList.vue

@ -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>

80
pages/components/PlugList.vue

@ -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>

80
pages/components/ProductList.vue

@ -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>

34
pages/detail/components/Header.vue

@ -20,6 +20,7 @@
</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"
@ -62,6 +63,7 @@
</el-button>
</el-space>
<el-alert title="Warning alert" type="warning" />
</div>
</div>
</div>
@ -74,14 +76,18 @@ import {useConfigInfo} from "~/composables/configState";
import {openSpmUrl} from "~/utils/common";
import Breadcrumb from "~/components/Breadcrumb.vue";
import type {Article} from "~/api/cms/article/model";
import type {CompanyParam} from "~/api/system/company/model";
import type {Navigation} from "~/api/cms/navigation/model";
import {useServerRequest} from "~/composables/useServerRequest";
import type {ApiResult, PageResult} from "~/api";
import useFormData from "~/utils/use-form-data";
const route = useRoute();
const token = useToken();
const sysDomain = useSysDomain();
withDefaults(
defineProps<{
form?: Article;
title?: string;
desc?: string;
buyUrl?: string;
@ -94,6 +100,30 @@ withDefaults(
}
);
//
const { form, assignFields } = useFormData<Navigation>({
//
parentName: undefined,
parentPath: undefined,
parentStatus: undefined,
categoryName: undefined,
categoryPath: undefined,
currentTitle: undefined,
style: undefined
});
const config = useConfigInfo();
//
const reload = async () => {
const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-navigation/page',{params: {
categoryId: route.params.categoryId
}})
if(response.value?.data){
assignFields(response.value.data)
}
}
reload();
</script>

13
pages/index.vue

@ -2,13 +2,11 @@
<!-- Banner -->
<Banner :layout="layout" />
<!-- 简单模式(常规单页面) -->
<PageContainer :form="form" :layout="layout" />
<ProductList />
<!-- 高级模式(自定义组件) -->
<div class="flex flex-col" v-if="layout?.showLayout">
<PlugList />
</div>
<ArticleList />
</template>
<script setup lang="ts">
@ -18,8 +16,11 @@ import {useConfigInfo, useForm, useToken, useWebsite} from "~/composables/config
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";
import Banner from './components/Banner.vue';
import {navigateTo} from "#imports";
import ProductList from "./components/ProductList.vue";
import PlugList from './components/PlugList.vue';
import ArticleList from './components/ArticleList.vue';
//
const route = useRoute();

105
pages/market/[id].vue

@ -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>

53
pages/market/components/CardList.vue

@ -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>

55
pages/market/components/CardList2.vue

@ -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>

125
pages/market/components/PageBanner.vue

@ -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>

114
pages/market/detail/[plugId].vue

@ -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>

99
pages/market/detail/components/Header.vue

@ -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>

97
pages/market/index.vue

@ -1,29 +1,35 @@
<template>
<PageBanner :layout="layout" />
<CardList :list="list" />
<el-tabs v-model="activeName" class="md:w-screen-xl m-auto relative sm:flex pb-2" @tab-click="handleClick">
<el-tab-pane label="最新上架" name="happy"></el-tab-pane>
<el-tab-pane label="免费热榜" name="website"></el-tab-pane>
<el-tab-pane label="付费热榜" name="weShop"></el-tab-pane>
<el-tab-pane label="热门总榜" name="cms"></el-tab-pane>
<el-tab-pane label="我的收藏" name="food"></el-tab-pane>
</el-tabs>
<CardList :list="list" :disabled="disabled" @done="onSearch" />
</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 {getPath} from "~/utils/common";
import type {CompanyParam} from "~/api/system/company/model";
import type {Tenant} from "~/api/system/tenant/model";
import type {Website} from "~/api/cms/website/model";
import type {Article} from "~/api/cms/article/model";
import CardList from './components/CardList.vue';
import PageBanner from './components/PageBanner.vue';
import type {Plug} from "~/api/system/plug/model";
const route = useRoute();
//
const runtimeConfig = useRuntimeConfig();
const list = ref<Tenant[]>([]);
const page = ref<number>(0);
const list = ref<Plug[]>([]);
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>();
@ -34,59 +40,52 @@ const where = reactive<CompanyParam>({
keywords: ''
});
const navTo = (item: Website) => {
if(item.domain){
openSpmUrl(`https://${item.domain}`,item,item.tenantId)
return;
const handleClick = (e:any) => {
console.log(e.index)
}
const onSearch = () => {
if(!disabled.value){
page.value++;
reload(route.path);
}
openSpmUrl(`https://${item.websiteCode}.wsdns.cn`,item,item.tenantId)
}
//
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, keywords: where.keywords
const {data: response} = await useServerRequest<ApiResult<PageResult<Plug>>>('/system/plug/page',{baseURL: runtimeConfig.public.apiServer, params: {
page: page.value,limit: 8, keywords: where.keywords
}})
if(response.value?.data){
if (response.value?.data.list) {
list.value = response.value?.data.list;
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}})
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",
}
});
const { data: nav } = await useServerRequest<ApiResult<Navigation>>('/cms/cms-navigation/getNavigationByPath',{query: {path: route.path}})
if(nav.value?.data){
form.value = nav.value?.data;
}
//
// //
// const handleClick = async () => {
// const {data: response} = await useServerRequest<ApiResult<PageResult<Article>>>('/cms/cms-article/page',{baseURL: runtimeConfig.public.apiServer, params: {
// page: page.value, keywords: where.keywords
// }})
// if(response.value?.data){
// if (response.value?.data.list) {
// list.value = response.value?.data.list;
// }
// if(response.value.data.count == 0){
// resultText.value = ''
// }
// }
// }
//
if(form.value?.layout){
layout.value = JSON.parse(form.value?.layout)
}
useHead({
title: `市场 - ${website.value.websiteName}`,
bodyAttrs: {
class: "page-container",
}
});
watch(
() => route.path,
@ -95,6 +94,4 @@ watch(
},
{ immediate: true }
);
</script>

76
pages/page/index.vue

@ -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>

186
pages/product/[id].vue

@ -1,168 +1,102 @@
<template>
<!-- Banner -->
<Banner :data="form" />
<div class="container flex flex-col w-full md:w-screen-xl m-auto my-3">
<Breadcrumb :data="form" :title="form?.categoryName || '文章列表'" />
<!-- 左右结构 -->
<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 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 :href="openSpmUrl(`/product/detail/${item.goodsId}`,item,item?.goodsId)" target="_blank" class="text-xl">{{ item.goodsName }}</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(`/goods/detail/${item.goodsId}`)">
<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>-->
<Banner :layout="layout" />
333
<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>
<!-- <el-divider />-->
<!-- <div v-if="!list">-->
<!-- <el-empty description="404 页面不存在"></el-empty>-->
<!-- </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 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 {useWebsite} from "~/composables/configState";
import type {Navigation} from "~/api/cms/navigation/model";
import {getIdBySpm, getPath, openSpmUrl} from "~/utils/common";
import type {Goods} from "~/api/shop/goods/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 list = ref<Goods[]>([]);
const title = ref();
const categoryName = ref();
const count = ref()
const page = ref<number>(1);
const disabled = ref<boolean>(false);
const newList = ref<Article[]>();
const resultText = ref('');
const layout = ref<any>();
//
const form = useForm();
const form = ref<Navigation>();
const website = useWebsite();
const app = ref<App>()
const load = () => {
if(!disabled.value){
page.value++;
// reload();
}
//
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',{baseURL: runtimeConfig.public.apiServer, query: {path: getPath()}})
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 }],
title: `搜索结果 - ${website.value.websiteName}`,
bodyAttrs: {
class: "page-container",
},
script: [
{
children: "console.log('Hello World')",
},
],
}
});
await handleClick()
}
//
useServerRequest<ApiResult<PageResult<Goods>>>('/shop/goods/page',{
params: {
page: page.value,
categoryId: route.query.id
}
}).then(res => {
const data = res.data.value?.data;
count.value = data?.count;
if (data?.list.length == 0) {
disabled.value = true;
return false;
//
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 (page.value == 0) {
list.value = data?.list;
}else {
list.value = list.value.concat(data?.list);
if(!response.value){
resultText.value = '暂无相关结果'
}
// list.value = articleList.value.data?.list;
list.value.map((d,i)=>{
if(i === 0){
}
});
})
}
}
watch(
() => route.path,
(path) => {
console.log(path,'=>Path')
() => route,
() => {
reload();
},
{ immediate: true }

53
pages/product/components/CardList.vue

@ -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>

92
pages/product/index.vue

@ -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>

BIN
归档.zip

Binary file not shown.
Loading…
Cancel
Save