搜索结果列表页面开发

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,61 @@
# BranchItem
### 说明
- 分支列表里面的分支卡片信息
``` js
import { mock } from 'mockjs';
// 数据定义为 @/components/BranchItem/type.ts
const branch = ref({
name: mock('@word'),
behindCount: mock('@natural(0, 500)'),
aheadCount: mock('@natural(0, 500)'),
repo: {
name: mock('@word')
namespace: mock('@first')
},
lastCommit: {
id: mock('@id'),
message: mock('@paragraph'),
username: mock('@first'),
createTime: mock('@datetime')
}
});
// 此处使用v-bind统一prop可以分开传入如 <BranchItem :name="xxx" ...... />
<BranchItem v-bind="branch" />
```
### Props
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|-----------|------------------------|----------------------------|----------|-----------|
| name | 分支名称 | string | - | - |
| behindCount | 落后默认分支commit个数 | number | - | - |
| aheadCount | 领先默认分支commit个数 | number | - | - |
| repo | 分支所属的仓库信息 | Repo | - | - |
| lastCommit | 分支下的最后一次commit | Commit | - | - |
#### Repo
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|-----------|------------------------|----------------------------|----------|-----------|
| name | 仓库名称 | string | - | - |
| namespace | 仓库所属命名空间用户或组织的namespace | string | - | - |
#### Commit
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|-----------|------------------------|----------------------------|----------|-----------|
| id | commit的hash Id | string | - | - |
| message | 提交描述 | string | - | - |
| username | commit提交人 | string | - | - |
| createTime | 提交时间 | string | - | - |
### Emits
| 方法 | 说明 | 返回值 |
|-----------------|------------------------------------|---------------------|
| onView | 查看分支统计 | props整体数据 |
| onEdit | 编辑分支事件 | props整体数据 |
| onDelete | 删除分支事件 | props整体数据 |

View File

@@ -0,0 +1,355 @@
<template>
<div class="g-branch-item">
<div class="g-branch-item-icon">
<Icon v-if="isProtected" name="gt-shield-lock" size="14px" color="var(--color-light)" />
</div>
<div class="g-branch-item-meta">
<Copy class="g-branch-item-meta-name font-bold" always size="14px">
<GLink :to="branchLink">
<d-tooltip position="top" :content="name">
<span class="text-G900 name-text ellipsis">{{ name }}</span>
</d-tooltip>
</GLink>
</Copy>
<span class="overflow-hidden whitespace-nowrap inline-block ml-2 align-middle text-ellipsis" v-if="description">
<span class="overflow-hidden text-ellipsis inline-block max-w-[100px] align-middle">{{ description }}</span>
<span
class="inline-flex justify-center items-center align-middle rounded-sm ml-2 cursor-pointer bg-CG200 hover:bg-CG300 active:bg-CG300"
>
<Icon :name="isFold ? 'gt-chevron-up' : 'gt-chevron-down'" @click="isFold = !isFold"/>
</span>
</span>
<p v-if="description" class="text-ellipsis break-all whitespace-pre-wrap overflow-hidden my-1"
:style="{ transition: 'height linear 0.1s', height: isFold ? 'auto' : 0 }">{{ description }}</p>
<div v-if="lastCommit" class="g-branch-item-meta-last-commit">
<GLink :to="authorLink"><span class="text-light">{{ lastCommit?.author_name }}</span></GLink>
<span class="text-light">提交于<Time :time="lastCommit?.created_at" /></span>
<GLink :to="commitLink"><span class="text-light">{{ lastCommit.id?.slice(0,8) }}</span></GLink>
<d-tooltip :position="['right']" :content="lastCommit.message">
<span class="g-branch-item-meta-last-commit-message text-light">{{ lastCommit.message }}</span>
<template #content>
<div class="py-1 px-4 max-w-[400px] max-h-[200px] overflow-auto">
{{ lastCommit.message }}
</div>
</template>
</d-tooltip>
<!-- 分支最近一次commit不用添加 -->
<!-- <GpgTag :open_gpg_verified="lastCommit.open_gpg_verified" :gpg_primary_key_id="lastCommit.gpg_primary_key_id" :verification_status="lastCommit.verification_status" :name="lastCommit.name"></GpgTag> -->
</div>
</div>
<d-tooltip v-if="!isDefault" :content="diffSummary">
<div class="g-branch-item-git" >
<div class="count left">
<span :style="{ width: getWidth(behindCount) }"><i>{{ behindCount }}</i></span>
</div>
<div class="line"></div>
<div class="count right">
<span :style="{ width: getWidth(aheadCount) }"><i>{{ aheadCount }}</i></span>
</div>
</div>
</d-tooltip>
<div class="g-branch-item-default default-branch" v-else>
<span>默认分支</span>
</div>
<div class="g-branch-item-operation">
<div class="merge text-ellipsis" v-if="mergeRequestStatus">
<div class="merge-type">
<Icon :name="mergeRequestStatus.icon" size="12px" :color="mergeRequestStatus.color" />
<span :style="{ color: mergeRequestStatus.color }">{{ mergeRequestStatus.text }}</span>
</div>
<div class="merge-id">
<span>#{{ mergeRequestBasic.iid }}</span>
</div>
</div>
<div class="diff">
<GLink :to="compareLink" v-if="!isDefault">
<d-tooltip position="top" content="比较">
<div class="diff-branch">
<Icon :operable="true" name="gt-compare-branch" size="14px" color="var(--color-light)" />
</div>
</d-tooltip>
</GLink>
</div>
<div v-if="canProtect" class="ml-5 min-w-[14px] cursor-pointer">
<d-tooltip v-if="!isProtected" position="top" content="创建保护分支">
<Icon
name="gt-shield-lock-setting"
size="14px"
color="var(--color-light)"
:operable="true"
@click="handleProtect"
/>
</d-tooltip>
</div>
<div class="more ml-5">
<MoreList v-if="canDelete && props.moreOpts?.length > 1" :moreOpts="props.moreOpts" :item="props" />
<d-tooltip v-else-if="canDelete && props.moreOpts?.length" position="top" :content="props.moreOpts[0]?.text">
<Icon :operable="true" :name="props.moreOpts[0].icon" @click="props.moreOpts[0]?.handle(props)"></Icon>
</d-tooltip>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from 'vue';
import { Branch } from './type';
import MoreList from '@/components/MoreList/index.vue';
const props = defineProps<Branch>();
const authorLink = computed(() => ({
name: 'homepage',
params: {
namespace: props?.lastCommit?.author_name || ''
}
}));
const diffSummary = computed(() => {
if (props.isDefault) {
return null;
}
return `${props.defaultBranch || '默认'}分支落后${props.behindCount}个提交、超前${props.aheadCount}个提交`;
});
const mergeRequest = reactive({
status: [{
state: 'opened',
text: '开启',
icon: 'gt-merge-request',
color: 'var(--color-Y500)'
}, {
state: 'closed',
text: '关闭',
icon: 'gt-skip-merge',
color: 'var(--color-CG600)'
}, {
state: 'locked',
text: '锁定',
icon: 'gt-merge-request',
color: 'var(--color-Y500)'
}, {
state: 'merged',
text: '合入合并',
icon: 'gt-closed-merge',
color: 'var(--color-GN500)'
}, {
state: 'all',
text: '全部',
icon: 'gt-merge-request',
color: 'var(--color-Y500)'
}]
});
const mergeRequestStatus = computed(() => {
const stateItem = mergeRequest.status.find((item: any) => item.state === props?.mergeRequestBasic?.state);
return stateItem;
});
const commitLink = computed(() => ({
name: 'repoCommitDetail',
params: {
namespace: props.repo.namespace,
repoName: props.repo.name,
commitId: props.lastCommit.id
},
query: {
ref: props.name
}
}));
const compareLink = computed(() => {
try {
return {
name: 'repoCompare',
params: {
namespace: props.repo.namespace,
repoName: props.repo.name,
fromId: props.defaultBranch || 'main',
toId: props.name
}
};
} catch (err) {
return null;
}
});
const branchLink = computed(() => ({
name: 'repoDir',
params: {
branchName: props.name
}
}));
const MAX_WIDTH = 30;
const getWidth = (val: number): string => {
const relativeWidth = val * MAX_WIDTH / (props.aheadCount + props.behindCount);
const absoluteWidth = val * MAX_WIDTH / Math.max(props.aheadCount, props.behindCount);
return `${(relativeWidth + absoluteWidth)}px`;
};
const isFold = ref(false);
const emit = defineEmits(['onProtect']);
const handleProtect = () => {
emit('onProtect');
};
</script>
<style lang="scss" scoped>
.g-branch-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
border: 1px solid var(--color-border-light);
min-height: 72px;
&+.g-branch-item {
border-top: none;
}
&-icon {
width: 14px;
}
&-meta {
flex-basis: 45%;
&-name {
font-size: 16px;
line-height: 30px;
font-weight: 500;
vertical-align: middle;
.name-text {
display: block;
max-width: 400px;
overflow: hidden;
text-overflow: ellipsis;
}
}
&-last-commit {
display: flex;
gap: 8px;
color: #999;
line-height: 20px;
&-message {
display: inline-block;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-break: break-all;
}
}
}
&-check {
flex-basis: 5%;
display: flex;
justify-content: center;
}
&-git {
display: flex;
flex-basis: 20%;
align-items: center;
>.count {
width: 50%;
display: flex;
padding: 0 2px;
color: var(--color-G700);
transform: translateY(10px);
&.left {
text-align: right;
display: flex;
justify-content: flex-end;
margin-right: 4px;
i {
position: absolute;
transform: translate(-6px, -20px);
right: 0;
}
}
&.right {
text-align: left;
margin-left: 4px;
i {
position: absolute;
transform: translate(6px, -20px);
}
}
}
.line {
width: 1px;
height: 32px;
background-color: var(--color-G300);
}
span {
padding: 2px 2px 2px 2px;
border-bottom: 4px solid var(--color-G300);
position: relative;
i {
font-style: normal;
}
}
}
&-default {
flex-basis: 20%;
text-align: center;
justify-content: center;
color: var(--color-G700);
font-size: 14px;
}
&-operation {
flex-basis: 30%;
display: flex;
justify-content: flex-end;
align-items: center;
.merge {
display: flex;
align-items: center;
margin-right: 60px;
gap: 8px;
width: 150px;
.merge-type {
display: flex;
align-items: center;
span {
margin-left: 4px;
font-size: 14px;
}
}
.merge-id {
color: var(--color-CG600);
}
}
.diff-branch {
display: flex;
align-items: center;
>span {
margin-left: 5px;
font-size: 14px;
color: var(--color-CG600);
}
}
.more {
width: 40px;
}
i {
cursor: pointer;
margin-left: 16px;
}
}
}
</style>

View File

@@ -0,0 +1,44 @@
export interface Commit {
id: string,
message: string,
author_name: string,
created_at: string,
open_gpg_verified: boolean; // 是否需要验证
gpg_primary_key_id: string; // 密钥id
verification_status: number; // 验证状态
name: string; // 名称
}
export interface MergerRequest {
id: string,
iid: string,
merge_request_type: string,
project_id: string,
state: string,
title: string,
updated_at: string,
web_url: string
}
export interface Repo {
name: string, // 仓库名
namespace: string, // 用户名
}
export interface Branch {
name: string,
description?: string;
behindCount: number,
aheadCount: number,
defaultBranch: string,
repo: Repo,
lastCommit: Commit,
mergeRequestBasic: MergerRequest,
isDefault: boolean,
isProtected: boolean,
canDelete: boolean,
canProtect: boolean,
moreOpts: Array<object>,
}