搜索结果列表页面开发

This commit is contained in:
付民康
2025-03-12 18:41:20 +08:00
commit 7cebe8fc00
739 changed files with 88149 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
# DataPanel
### 说明
- 已全局声明
- 在EmptyData上进行了二次封装通过empty和loading字段自适应展示数据
- loading使用d-skeleton方案可以通过$slots.loading自定义。或者不传入loading自己加v-loading="loading"
- empty使用EmptyData方案可以通过$slots.empty自定义
``` ts
<DataPanel :empty="!dataList?length" :loading="loading">
<d-table :data="dataList" />
</DataPanel>
const dataList = ref([]);
const loading = ref(false);
```
### Props
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|-----------|------------------------|----------------------------|----------|-----------|
| loading? | 是否加载中。默认启动v-loading效果如果传入$slots.loading则v-loading失效 | boolean | - | false |
| empty? | 是否数据为空为空默认展示d-result | boolean | - | false |
| emptyText? | 数据为空的标题 | string | - | '暂无数据' |
| emptyDetail? | 数据为空标题下方的描述 | string | - | '暂无数据' |
| skeleton? | loading状态下显示骨架屏比animation优先级更高 | boolean | - | false |
| animation? | loading状态下显示定制的loading | boolean | - | false |
| card? | 是否使用g-card组件样式 | boolean | - | true |
### Slots
| 名称 | 说明 |
|---------------------|---------------------------------------------|
| default | 数据自定义区域 |
| empty? | 数据为空的定义 |
| loading? | loading效果如骨架屏。默认使用v-loading传入loading slot则v-loading失效 |

View File

@@ -0,0 +1,41 @@
<template>
<div class="g-border bg-white">
<slot name="tab"></slot>
<div class="g-data-panel">
<slot v-if="loading" name="loading">
<d-skeleton v-if="skeleton" class="p-20" :rows="skeletonRow"/>
<Animation v-else-if="animation" class="w-full h-full flex-center" />
</slot>
<slot v-else-if="empty" name="empty">
<EmptyData class="h-full flex-1" :hide-icon="hideIcon" :title="emptyText" :desc="emptyDetail" />
</slot>
<slot v-else></slot>
</div>
</div>
</template>
<script setup lang="ts">
import Animation from '@/components/Animation/index.vue';
interface IProps {
loading?: boolean,
empty?: boolean,
emptyText?: string,
emptyDetail?: string,
skeleton?: boolean,
hideIcon?: boolean,
skeletonRow?:number,
card?: boolean,
animation?: boolean
}
withDefaults(defineProps<IProps>(), {
emptyText: '暂无数据',
skeletonRow: 5,
card: true,
animation: false
});
</script>
<style lang="scss" scoped>
@import 'devui-theme/styles-var/devui-var.scss';
</style>

View File

@@ -0,0 +1,16 @@
<template>
<div v-if="number" class="g-number">
{{ number }}
</div>
</template>
<script setup lang="ts">
interface IProps {
number: number
}
withDefaults(defineProps<IProps>(), {
number: 0
});
</script>

View File

@@ -0,0 +1,12 @@
# NavSidebar
### 说明
- 布局中使用的侧边栏菜单
### Slots
| 名称 | 说明 |
|---------------------|---------------------------------------------|
| sidebarInfo | sidebar顶部信息 |

View File

@@ -0,0 +1,81 @@
export const attachOrgSettingMenu = [
{
icon: 'icon-manage',
label: '管理',
id: '',
children: [
{ children: [], icon: '', label: '内容管理', id: '/organization/setting/content' },
{ children: [], icon: '', label: '专栏管理', id: '/organization/setting/column' },
{ children: [], icon: '', label: '静态页面管理', id: '/organization/setting/static-page' },
{ children: [], icon: '', label: '收录管理', id: '/organization/setting/included' },
{ children: [], icon: '', label: '直播与活动', id: '/organization/setting/live' },
{ children: [], icon: '', label: '文档中心', id: '/organization/setting/docs' },
{ children: [], icon: '', label: '评论管理', id: '/organization/setting/comment-manage' },
{ children: [], icon: '', label: '分类管理', id: '/organization/setting/category' }
]
}, {
icon: 'icon-operate',
label: '运营',
id: '',
children: [
{ children: [], icon: '', label: 'Banner位', id: '/organization/setting/rotation' },
{ children: [], icon: '', label: '自定义导航', id: '/organization/setting/navigation' },
{ children: [], icon: '', label: '产品推荐', id: '/organization/setting/market' },
{ children: [], icon: '', label: '运营活动', id: '/organization/setting/rotation-right' },
{ children: [], icon: '', label: '热门标签', id: '/organization/setting/hot-tags' },
{ children: [], icon: '', label: '悬浮窗设置', id: '/organization/setting/float-window' },
{ children: [], icon: '', label: '模块配置', id: '/organization/setting/module-config' },
{ children: [], icon: '', label: '服务商信息', id: '/organization/setting/facilitator-config' }
]
}, {
icon: 'icon-growth',
label: '成长体系',
id: '',
children: [
{ children: [], icon: '', label: '功能设置', id: '/organization/setting/growth' },
{ children: [], icon: '', label: '任务中心', id: '/organization/setting/growth-task' },
{ children: [], icon: '', label: '成长等级', id: '/organization/setting/growth-level' }
]
}, {
icon: 'icon-marketing',
label: '营销组件',
id: '',
children: [
{ children: [], icon: '', label: '加入社区', id: '/organization/setting/join' },
{ children: [], icon: '', label: '触达消息', id: '/organization/setting/push-msg' },
{ children: [], icon: '', label: '留资表单', id: '/organization/setting/retained-capital' },
{ children: [], icon: '', label: '抽奖工具', id: '/organization/setting/draw' },
{ children: [], icon: '', label: '自定义弹窗', id: '/organization/setting/custom' },
{ children: [], icon: '', label: '文章内容设置', id: '/organization/setting/content-setting' }
]
}, {
icon: 'icon-edu',
label: '课程',
id: '',
children: [
{ children: [], icon: '', label: '课程管理', id: '/organization/setting/edu-course' },
{ children: [], icon: '', label: '课程数据', id: '/organization/setting/data-edu' }
]
}, {
icon: 'icon-data',
label: '数据',
id: '',
children: [
{ children: [], icon: '', label: '概览', id: '/organization/setting/data-board' },
{ children: [], icon: '', label: '内容分析', id: '/organization/setting/data-content' },
{ children: [], icon: '', label: '渠道来源', id: '/organization/setting/data-source' },
{ children: [], icon: '', label: '用户分析', id: '/organization/setting/data-user' },
{ children: [], icon: '', label: '作者分析', id: '/organization/setting/data-author' }
]
}, {
icon: 'icon-set',
label: '设置',
id: '',
children: [
{ children: [], icon: '', label: '社区信息', id: '/organization/setting/general' },
{ children: [], icon: '', label: '成员管理', id: '/organization/setting/people' },
{ children: [], icon: '', label: '页面设置', id: '/organization/setting/exterior' },
{ children: [], icon: '', label: '域名设置', id: '/organization/setting/domain' }
]
}
];

View File

@@ -0,0 +1,286 @@
<template>
<div class="navtabs-avatardiv pb-24 pr-24 pl-[10px]" v-if="showAvatar">
<GAvatar :src="avatarParam?.avatar" :style="isOrg?'border:.5px solid #d3d3d3;border-radius: var(--border-radius);':''" :name="avatarParam?.name" :width="48" :height="48" :is_round="isRound"></GAvatar>
<div class="navtabs-text ml-[16px]">
<div class="info-span text-base text-G900 navtabs-omit">{{ avatarParam?.name }}</div>
<div class="info-subtext text-sm text-CG600 navtabs-omit">{{ avatarParam?.infoSubText }}</div>
</div>
</div>
<div class="g-nav-sidebar w-[200px] pb-[48px]">
<slot name="sidebarInfo"></slot>
<!-- ui增加分类名和分割线 -->
<template v-if="menuShow">
<div v-for="(typeItem, index) in setMenuTypes(menulist)" :key="`${typeItem.label}_${index}`">
<div class="mt-[16px] mb-[8px] text-[#9C9DB4] pl-[16px]" v-if="typeItem.label">{{ typeItem.label }}</div>
<d-menu mode="vertical" :default-select-keys="[id]" :open-keys="openKeys" @select="handleNav">
<template v-for="item in typeItem.children" :key="item.id">
<d-menu-item v-if="!item.children" :key="item.id">
<template #icon v-if="item?.icon">
<Icon :name="item?.icon" class="common-icon"></Icon>
<Icon :name="item?.activeIcon" class="active-icon"></Icon>
</template>
{{ item.label }}</d-menu-item>
<d-sub-menu v-if="item.children" :title="item.label" :key="item.id">
<template #icon v-if="item?.icon">
<!-- <Icon :name="item?.icon"></Icon> -->
<div class="icon-div">
<Icon :name="item?.icon"></Icon>
<Icon v-if="item.isNew" class="icon-new" :name="'gt-new'" size="28px"></Icon>
</div>
</template>
<d-menu-item v-for="item1 in item.children" :key="item1.id">
<template #icon v-if="item1?.icon">
<Icon :name="item1?.icon"></Icon>
</template>
{{ item1.label }}</d-menu-item>
</d-sub-menu>
</template>
</d-menu>
<div class="row-line" v-if="setMenuTypes(menulist)?.length - 1 > index"></div>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { ref, watch, nextTick, computed } from 'vue';
import { onBeforeRouteUpdate, useRouter, useRoute } from 'vue-router';
import { type listType } from './types';
import menu from '@/constant/menu';
import { orgInfoStore, type OrgInfo } from '@/stores/Org/index';
import { useAccountStore } from '@/stores/user';
const { accountInfo } = useAccountStore();
interface IProps {
// 声明props内容
showAvatar?: boolean,
isRound?: boolean,
avatarParam?: {
avatar?: string,
name?: string,
infoSubText?: string, // 预留,暂时没有传值
[x: string]: any
}
}
const props = withDefaults(defineProps<IProps>(), {
showAvatar: true,
isRound: false,
avatarParam: () => {
return {
visibility: ''
};
}
});
const orgStore = orgInfoStore();
const route = useRoute();
const menulist = ref<listType[]>(menu[route.meta.sidebarType]);
const router = useRouter();
const nsId = computed(() => orgStore.communityInfo?.ns_id);
const isOrg = computed(() => /^\/org\//.test(route.path));
const devHost = '';
// const devHost = (import.meta as any).env.MODE === 'development' ? 'https://loc-devpress.csdn.net:444' : '';
const id = ref();
const openKeys = ref([]);
const setCurSelected = () => {
// 白名单处理Webhook创建、详情页要选中导航
id.value = router.currentRoute.value.name?.includes('repoWebhook') ? 'repoWebhook' : router.currentRoute.value.name;
menulist.value?.forEach(item => {
if (item?.children?.some(e => e.id === id.value)) {
openKeys.value.push(item.id);
}
});
};
// 判断社区是否显示
const menuShow = ref(true);
const onShowCom = () => {
menuShow.value = false;
menulist.value = menu[route.meta.sidebarType].filter((item: any) => {
if (item.label === '社区管理') {
return orgStore.orgInfo.visibility === 'public';
}
return true;
});
nextTick(() => {
menuShow.value = true;
});
};
const onShowPipeLine = async () => {
menuShow.value = false;
menulist.value = menu[route.meta.sidebarType].filter((item: any) => {
if (item.label === '流水线') {
return accountInfo.atomgit_username;
}
return true;
});
nextTick(() => {
menuShow.value = true;
});
};
watch(() => orgStore.orgInfo, () => {
onShowCom();
}, { deep: true });
watch(() => accountInfo.atomgit_username, () => {
onShowPipeLine();
}, { deep: true });
onBeforeRouteUpdate((to) => {
id.value = to.name; // 页面加载激活对应tab
menulist.value = menu[to.meta.sidebarType]; // 根据meta中的type确定导航内容
if (route.meta.sidebarType === 'userSetting') {
onShowPipeLine();
}
});
watch(() => router.currentRoute.value.path, (newValue, oldValue) => {
menulist.value = menu[route?.meta?.sidebarType];
if (route.meta.sidebarType === 'orgSetting') {
onShowCom();
}
if (route.meta.sidebarType === 'userSetting') {
onShowPipeLine();
}
setCurSelected();
}, { immediate: true });
// 点击tab跳转路由
const handleNav = (val) => {
if (/^\/organization/.test(val.key)) {
window.open(`${devHost}${val.key}/${nsId.value}`, '_self');
} else router.push({ name: val.key });
};
// 分类处理函数-增加分类名称显示
const setMenuTypes = (menu:any) => {
const res = []; const typeMap = new Map();
for (let i = 0; i < menu.length; ++i) {
const { menuType } = menu[i];
if (menuType) {
let type = typeMap.get(menuType);
if (!type) {
type = { label: menuType, children: [] };
res.push(type);
typeMap.set(menuType, type);
}
type.children.push(menu[i]);
} else {
if (res.length && res[res.length - 1].label === '') res[res.length - 1].children.push(menu[i]);
else res.push({ label: '', children: [menu[i]] });
}
}
return res;
};
</script>
<style lang="scss" scoped>
.g-nav-sidebar {
:deep(.devui-menu-vertical) {
border-right: 0;
background: none !important;
.devui-menu-item {
color: var(--devui-text);
.devui-menu-icon + span {
margin-left: 8px;
}
.active-icon {
display: none;
}
&:hover {
background: var(--tab-active-bg) !important;
border-radius: var(--devui-border-radius);
.active-icon {
display: inline-block;
}
.common-icon {
display: none;
}
}
}
.devui-menu-item-select {
background: var(--tab-active-bg) !important;
border-radius: var(--devui-border-radius);
.active-icon {
display: inline-block;
}
.common-icon {
display: none;
}
}
.layer_1 {
.devui-menu-item,
.devui-submenu-title {
padding: 0 16px !important;
}
}
.layer_2 {
.devui-menu-item {
padding: 0 50px !important;
}
.devui-submenu-title {
padding: 0 10px !important;
}
}
.layer_2>div.devui-submenu-title,
ul.devui-submenu .devui-menu-item {
background: none;
}
.icon-chevron-up {
color: var(--color-CG600);
}
}
.row-line {
width: calc(100% - 32px);
margin-left: 16px;
height: 1px;
background: var(--devui-dividing-line);
margin-top: 16px;
}
}
.navtabs-avatardiv {
width: 100%;
display: flex;
.navtabs-text {
flex: 1;
width: 0;
display: flex;
flex-direction: column;
justify-content: center;
}
.info-span {
font-weight: 500;
line-height: 24px;
}
.info-subtext {
// font-size: var(--text-sm);
font-weight: 400;
// color: var(--color-CG600);
line-height: 20px;
}
}
.navtabs-omit {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.icon-div {
position: relative;
.icon-new {
position: absolute;
top: -18px;
left: 8px;
}
}</style>

View File

@@ -0,0 +1,15 @@
export interface itemProps {
id: string; // 标识符,这里为路由别名
label: string; // 标题
number?: number; // 显示数量
icon?: string; // 图标
children?: itemProps[], // 子item
menuType?: string // 菜单分类
}
export interface propsType {
defaultActiveId?: string;
tabList: itemProps[];
}
export interface listType {
[index: number]: itemProps;
}