搜索结果列表页面开发
This commit is contained in:
442
src/views/Mobile/Repo/Merge/components/DetailFileDiff.vue
Normal file
442
src/views/Mobile/Repo/Merge/components/DetailFileDiff.vue
Normal file
@@ -0,0 +1,442 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="!diffs" class="p-5"><d-skeleton></d-skeleton></div>
|
||||
<d-splitter v-if="diffs" class="splitter-border" style="height: 100%">
|
||||
<template v-slot:DSplitterPane>
|
||||
<d-splitter-pane
|
||||
collapseDirection="before"
|
||||
size="320px"
|
||||
minSize="320px"
|
||||
:collapsible="true"
|
||||
@size-change="setSplitterCollapse"
|
||||
@collapsed-change="collapsedChange"
|
||||
>
|
||||
<div class="pane-content">
|
||||
<div v-if="diffTree" style="min-width: 320px" class="left-tree pl-5 pr-5 pt-5">
|
||||
<d-search
|
||||
class="mb10"
|
||||
style="width: 100%"
|
||||
is-keyup-search
|
||||
placeholder="查找文件"
|
||||
:delay="100"
|
||||
@search="onSearch"
|
||||
></d-search>
|
||||
<d-tree @node-click="onTreeClick" ref="treeRef" :data="diffTree" class="mt-2">
|
||||
<template #content="{ nodeData }">
|
||||
<div class="ml-2" :title="nodeData?.file_path">{{ nodeData?.file_path }}</div>
|
||||
</template>
|
||||
<template #icon="{ nodeData }">
|
||||
<span class="inline-block w-5">
|
||||
<Icon v-if="nodeData.isLeaf" name="gt-file-c" />
|
||||
<Icon v-else :name="nodeData.expanded ? 'gt-folder-open-c' : 'gt-folder-c'" />
|
||||
</span>
|
||||
</template>
|
||||
</d-tree>
|
||||
</div>
|
||||
</div>
|
||||
</d-splitter-pane>
|
||||
<d-splitter-pane minSize="15%">
|
||||
<div class="pane-content">
|
||||
<div class="flex-1 pt-5 bg-[#f6f6f8]">
|
||||
<div class="flex px-5 justify-between items-center">
|
||||
<div class="flex items-center">
|
||||
<StatusSelect v-model:value="selected" />
|
||||
<div class="flex items-center ml-4">
|
||||
<div class="text-[#2D2D2E] flex items-center">
|
||||
共
|
||||
<div class="text-[#517ca0] mx-1">{{ diffs.changes?.length }}</div>
|
||||
个文件变更
|
||||
</div>
|
||||
<p class="text-green-500 ml-4">+{{ diffs.added_lines }}</p>
|
||||
<p class="text-red-500 ml-1">-{{ diffs.removed_lines }}</p>
|
||||
</div>
|
||||
<span class="w-px h-5 bg-gray-300 mx-4"></span>
|
||||
<div class="flex items-center gap-2">
|
||||
<d-dropdown style="width: 150px" :position="['bottom-start']" align="start" close-scope="all">
|
||||
<div class="flex items-center justify-between gap-2 cursor-pointer">
|
||||
<div class="commitTitle text-[#707a87] hover:text-[#2d2d2e]">
|
||||
{{ preCommitName }}
|
||||
</div>
|
||||
<Icon name="gt-select-arrow-down-12" size="12px"></Icon>
|
||||
</div>
|
||||
<template #menu>
|
||||
<ul>
|
||||
<li
|
||||
class="p-2 cursor-pointer hover:bg-[#f6f6f8]"
|
||||
v-for="item in firstCommitOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:name="item.name"
|
||||
@click="
|
||||
preCommit = item.value;
|
||||
baseLineChange();
|
||||
"
|
||||
>
|
||||
<div class="px-2">
|
||||
<div style="font-weight: 700">{{ item.name }}</div>
|
||||
<div v-if="item.head_commit_sha">
|
||||
{{ item.head_commit_sha.slice(0, 8) }}
|
||||
</div>
|
||||
<div v-if="item.created_at">{{ formatTime(item.created_at) }}</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</d-dropdown>
|
||||
<Icon name="gt-exit" size="12px"></Icon>
|
||||
<d-dropdown style="width: 150px" :position="['bottom-start']" align="start">
|
||||
<div class="flex items-center justify-between gap-2 cursor-pointer">
|
||||
<div class="commitTitle text-[#707a87] hover:text-[#2d2d2e]">
|
||||
{{ afterCommitName }}
|
||||
</div>
|
||||
<Icon name="gt-select-arrow-down-12" size="12px"></Icon>
|
||||
</div>
|
||||
<template #menu>
|
||||
<ul>
|
||||
<li
|
||||
class="p-2 cursor-pointer hover:bg-[#f6f6f8]"
|
||||
v-for="item in lastCommitOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:name="item.name"
|
||||
@click="
|
||||
afterCommit = item.value;
|
||||
baseLineChange();
|
||||
"
|
||||
>
|
||||
<div class="px-2">
|
||||
<div style="font-weight: 700">{{ item.name }}</div>
|
||||
<div v-if="item.head_commit_sha">
|
||||
{{ item.head_commit_sha.slice(0, 8) }}
|
||||
</div>
|
||||
<div v-if="item.created_at">{{ formatTime(item.created_at) }}</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</d-dropdown>
|
||||
</div>
|
||||
<div class="ml-4"></div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<d-popover content="展开/收起全部文件" trigger="hover" :position="['top']">
|
||||
<span class="flex items-center cursor-pointer" @click="handleExpand">
|
||||
<Icon name="gt-frame-expand2"></Icon>
|
||||
</span>
|
||||
</d-popover>
|
||||
<span class="w-px h-5 bg-gray-300 mx-3"></span>
|
||||
<d-popover content="设置" trigger="hover" :position="['top']">
|
||||
<ShowFormat />
|
||||
</d-popover>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<div v-if="diffsloading" class="p-5 h-80 w-[100%]"><d-skeleton></d-skeleton></div>
|
||||
<DataPanel
|
||||
class="filter-diff-list"
|
||||
:loading="diffsloading"
|
||||
:empty="!filterDiffs?.length"
|
||||
:skeleton="diffsloading"
|
||||
:card="false"
|
||||
>
|
||||
<div class="flex-1 codeblock p-5 pt-0" :key="`${preCommit}-${afterCommit}`" v-show="!diffsloading">
|
||||
<div
|
||||
:class="{ selectItem: selectedFile === item.file_path }"
|
||||
class="mb-3 overflow-auto rounded-[8px]"
|
||||
style="box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.09)"
|
||||
v-for="(item, index) in filterDiffs"
|
||||
:key="item.file_path"
|
||||
@click="codelistClick(item.file_path)"
|
||||
>
|
||||
<DiffView
|
||||
:index="index"
|
||||
:repoId="repoId"
|
||||
:diffObj="item"
|
||||
:discussions="diss?.length ? diss.find((x) => x.path === item.file_path) : null"
|
||||
:showIssue="true"
|
||||
:allowComment="canComment"
|
||||
:showViewSource="true"
|
||||
:showReadBtn="true"
|
||||
@onMergeAble="$emit('onMergeAble')"
|
||||
@foldChange="(e) => (item.fold = e)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</DataPanel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</d-splitter-pane>
|
||||
</template>
|
||||
</d-splitter>
|
||||
<!-- 勿删 Status 枚举需要 -->
|
||||
<div
|
||||
class="hidden text-[#fa9841] bg-[#fa984133] bg-[#3ac29533] bg-[#f66f6a33] bg-[#71757f33] text-[#3ac295] text-[#f66f6a] text-[#71757f]"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, toValue, watch, reactive, onMounted, provide, inject } from 'vue';
|
||||
import { useReq } from '@/utils/hooks/useReq';
|
||||
import { changes } from '@/api/merge/index';
|
||||
import ShowFormat from './ShowFormat.vue';
|
||||
import StatusSelect from './StatusSelect.vue';
|
||||
import { changesTrees, versions, getDis } from '@/api/merge/index';
|
||||
import DiffView from './DiffView.vue';
|
||||
import { useAccountStore } from '@/stores/user';
|
||||
import { escapeResData } from '@/utils';
|
||||
import { diffFilter } from './support';
|
||||
import { repoInfoStore } from '@/stores/Repo';
|
||||
import { useMrChangeStore } from '@/stores/merge';
|
||||
import { formatTime } from './support';
|
||||
|
||||
const mrChangeStore = useMrChangeStore();
|
||||
const user = useAccountStore();
|
||||
const accountInfo = user.accountInfo;
|
||||
const arts_id = accountInfo?.arts_id;
|
||||
const { isPrivate, isVisitor } = repoInfoStore();
|
||||
const canComment = computed(() => {
|
||||
if (isPrivate) return isVisitor;
|
||||
else return !!arts_id;
|
||||
});
|
||||
|
||||
//
|
||||
const repoId = inject('repoId');
|
||||
const iid = inject('iid');
|
||||
provide('arts_id', arts_id);
|
||||
provide('accountInfo', accountInfo);
|
||||
const mrInfo = inject('mrInfo');
|
||||
provide('source_branch', mrInfo?.value?.source_branch);
|
||||
|
||||
// 筛选
|
||||
const selected = ref([]);
|
||||
const keyWord = ref('');
|
||||
const allFold = ref(false);
|
||||
const handleExpand = () => {
|
||||
diffs?.value?.changes?.forEach((x) => (x.collapsed = allFold.value));
|
||||
allFold.value = !allFold.value;
|
||||
};
|
||||
|
||||
// 基线
|
||||
const preCommit = ref(null);
|
||||
const afterCommit = ref(null);
|
||||
provide('preCommit', preCommit);
|
||||
provide('afterCommit', afterCommit);
|
||||
const preCommitName = computed(() => {
|
||||
return commitOptions?.value?.find((x) => x.value === preCommit.value)?.name || '';
|
||||
});
|
||||
const afterCommitName = computed(() => {
|
||||
return commitOptions?.value?.find((x) => x.value === afterCommit.value)?.name || '';
|
||||
});
|
||||
|
||||
const from_diff_id = computed(() => {
|
||||
if (!commitOptions?.value?.length) return null;
|
||||
if (preCommit.value === -1 && afterCommit.value === commitOptions.value[0].value) return null;
|
||||
return afterCommit.value;
|
||||
});
|
||||
const to_diff_id = computed(() => {
|
||||
if (preCommit.value === -1) return null;
|
||||
return preCommit.value;
|
||||
});
|
||||
const { data: commitOptions } = useReq(versions, reactive({ repoId, iid }), null, (res) => {
|
||||
const data = res.map((x, i) => ({ ...x, value: x.id }));
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (i === 0) data[i].name = '最新版本';
|
||||
else data[i].name = '版本' + (data.length - i);
|
||||
}
|
||||
return [...data, { value: -1, name: '基线' }];
|
||||
});
|
||||
watch(commitOptions, () => {
|
||||
if (!commitOptions.value?.length) return;
|
||||
//
|
||||
preCommit.value = -1;
|
||||
afterCommit.value = commitOptions.value[0].value;
|
||||
});
|
||||
const firstCommitOptions = computed(() => {
|
||||
if (!commitOptions.value?.length) return [];
|
||||
const firstOptions = commitOptions.value.filter((x) => x.value < afterCommit.value);
|
||||
return [...firstOptions];
|
||||
});
|
||||
const lastCommitOptions = computed(() => {
|
||||
if (!preCommit.value || !commitOptions.value?.length) return [];
|
||||
const afterOptions = commitOptions.value.filter((x) => x.value > preCommit.value);
|
||||
return [...afterOptions];
|
||||
});
|
||||
const baseLineDateNow = ref(+new Date());
|
||||
const baseLineChange = () => {
|
||||
loadDiffTree();
|
||||
loadDiff();
|
||||
baseLineDateNow.value = +new Date();
|
||||
};
|
||||
|
||||
// diffs files tree
|
||||
const formatTree = (childrenProp, pickProps) => (sourceList) => {
|
||||
if (!Array.isArray(sourceList)) return sourceList;
|
||||
return sourceList.map((node) => {
|
||||
return {
|
||||
...node,
|
||||
children: formatTree(childrenProp, pickProps)(node[childrenProp]),
|
||||
...pickProps(node)
|
||||
};
|
||||
});
|
||||
};
|
||||
const findTreeNode = (id, nodes) => {
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return node;
|
||||
if (node.children?.length) {
|
||||
const deepRes = findTreeNode(id, node.children);
|
||||
if (deepRes) return deepRes;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const diffTree = ref(null);
|
||||
const loadDiffTree = () => {
|
||||
changesTrees(
|
||||
reactive({
|
||||
repoId,
|
||||
iid,
|
||||
view: 'simple',
|
||||
from_diff_id,
|
||||
to_diff_id
|
||||
})
|
||||
)
|
||||
.then((res) => {
|
||||
const data = escapeResData(res);
|
||||
const pickProps = (node) => ({
|
||||
label: node.title,
|
||||
id: node.file_path,
|
||||
expanded: true,
|
||||
selected: false
|
||||
});
|
||||
diffTree.value = formatTree('items', pickProps)(data.tree);
|
||||
})
|
||||
.catch((e) => {});
|
||||
};
|
||||
//
|
||||
const treeRef = ref();
|
||||
const onSearch = (value) => {
|
||||
treeRef.value.treeFactory.searchTree(value, { isFilter: true });
|
||||
};
|
||||
const selectedFile = ref('');
|
||||
const onTreeClick = (node) => {
|
||||
selectedFile.value = node?.file_path;
|
||||
const thenode = diffs?.value?.changes?.find((x) => x.file_path === selectedFile.value);
|
||||
if (thenode) thenode.fold = false;
|
||||
|
||||
if (node.childNodeCount > 0) {
|
||||
node.expanded = !node.expanded;
|
||||
}
|
||||
//
|
||||
};
|
||||
const codelistClick = (file_path) => {
|
||||
selectedFile.value = file_path;
|
||||
//
|
||||
const thenode = findTreeNode(file_path, diffTree.value);
|
||||
//
|
||||
treeRef.value.treeFactory.selectNode(thenode);
|
||||
};
|
||||
|
||||
// diffs (文件)
|
||||
const diffs = ref();
|
||||
const diffsloading = ref(false);
|
||||
const loadDiff = () => {
|
||||
diffsloading.value = true;
|
||||
const diffsParams = reactive({
|
||||
repoId,
|
||||
iid,
|
||||
view: 'simple',
|
||||
ignore_whitespace_change: mrChangeStore.ignore_whitespace_change,
|
||||
from_diff_id,
|
||||
to_diff_id
|
||||
});
|
||||
changes(diffsParams)
|
||||
.then((res) => {
|
||||
diffsloading.value = false;
|
||||
const data = escapeResData(res);
|
||||
diffs.value = data;
|
||||
//
|
||||
})
|
||||
.catch((e) => {
|
||||
diffsloading.value = false;
|
||||
});
|
||||
};
|
||||
const filterDiffs = computed(() => {
|
||||
if (!diffs?.value?.changes) return [];
|
||||
return diffs.value.changes?.filter((m) => diffFilter(m, selected));
|
||||
});
|
||||
//
|
||||
|
||||
// diss
|
||||
const { data: diss } = useReq(getDis, reactive({ repoId, iid }));
|
||||
|
||||
watch(
|
||||
() => mrChangeStore.ignore_whitespace_change,
|
||||
() => {
|
||||
diffs.value = [];
|
||||
loadDiff();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
loadDiffTree();
|
||||
loadDiff();
|
||||
});
|
||||
|
||||
const collapsedChange = (e) => {
|
||||
if (e) setSplitterCollapse('4px');
|
||||
else setSplitterCollapse('320px');
|
||||
};
|
||||
const setSplitterCollapse = (left) => {
|
||||
setTimeout(() => {
|
||||
document.querySelector('.devui-splitter__collapse').style.left = left;
|
||||
}, 0);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import 'devui-theme/styles-var/devui-var.scss';
|
||||
.codeblock {
|
||||
// background-color: $devui-global-bg;
|
||||
min-height: calc(100vh - 144px - 104px - 52px);
|
||||
}
|
||||
|
||||
.selectItem {
|
||||
:deep(.devui-code-review__header) {
|
||||
background-image: linear-gradient(to right, rgba(103, 136, 242, 0.2), transparent);
|
||||
}
|
||||
}
|
||||
|
||||
.left-tree {
|
||||
border-right: 1px solid $devui-dividing-line;
|
||||
position: sticky;
|
||||
top: 10px;
|
||||
|
||||
min-height: calc(100vh - 144px - 104px);
|
||||
}
|
||||
|
||||
.splitter-border {
|
||||
:deep(.resizable .devui-splitter__collapse) {
|
||||
position: fixed;
|
||||
left: 320px;
|
||||
top: 50%;
|
||||
}
|
||||
:deep(.none-resizable .devui-splitter__collapse) {
|
||||
position: fixed;
|
||||
left: 4px;
|
||||
top: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.commitTitle {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.filter-diff-list {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user