搜索结果列表页面开发

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,25 @@
### Props
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|----------------|---------|-----------------------|-----------|-------|
| data | 列表数据 | Record<string, any>[] | — | [] |
| showCheckbox? | 是否显示勾选框 | boolean | — | true |
| size? | 左上角图标尺寸 | string | — | '18px' |
| statusItems? | 各状态数据列表 | { icon?: string; count?: number; title: string; size?: string; color?: string; }[] | — | [] |
### Emits
注意:以下事件都是组件内部对应的按钮点击触发,如果用插槽自定义对应区域的内容的话,事件自行监听即可
| 方法 | 说明 | 返回值 |
|------|-------------|-----------------------|
| change | 当勾选项发生变化时抛出 | Record<string, any>[] |
### Slots
| 名称 | 说明 |
|--------------|------------------|
| default | 列表内容插槽,不会覆盖勾选框 |
| header-left | 头部左边区域部分的插槽 |
| header-right | 头部右边区域部分的插槽 |
| content | 完整内容区域插槽,可以覆盖勾选框 |

View File

@@ -0,0 +1,191 @@
<template>
<section class="g-issue-list-box">
<panel :blank="false">
<template #header>
<div class="g-issue-list-box-header">
<span v-if="showCheckbox" class="g-issue-list-box-checkbox">
<d-checkbox :half-checked="allCheckedHalf" v-model="allChecked" @change="onCheckChange" />
</span>
<slot name="header-left">
<div class="g-issue-list-box-header-left">
<div v-for="(item, index) in statusItems" :key="index" class="g-issue-list-box-status">
<span v-if="item.icon" class="g-issue-list-box-status-icon">
<d-icon :name="item.icon" :size="item.size || size" :style="{ color: item.color }"></d-icon>
</span>
<span class="g-issue-list-box-status-title" :style="{ color: item.color, fontWeight: item.color ? 'bold' : '' }">
{{ item.title }}
</span>
<custom-tag v-if="item.count" border-color="transparent" bg-color="#EAEAEA" color="#606060">
{{ item.count }}
</custom-tag>
</div>
</div>
</slot>
</div>
</template>
<template #headerRight>
<slot name="header-right">
右边插槽
</slot>
</template>
<slot name="content">
<div class="g-issue-list-box-content">
<div v-for="(item, index) in localData"
:key="`${item.id}_${index}`"
class="g-issue-list-box-item"
:class="{ 'is-last': index === localData.length - 1 }"
>
<span v-if="showCheckbox" class="g-issue-list-box-checkbox"><d-checkbox v-model="checkedList[index]" /></span>
<slot :data="item" :index="index">
列表内容插槽
</slot>
</div>
</div>
</slot>
</panel>
</section>
</template>
<script lang="ts">
export default {
name: 'issue-list-box'
};
</script>
<script setup lang="ts">
import { ref, watch, nextTick } from 'vue';
import Panel from '@/components/Panel/index.vue';
import CustomTag from '@/components/CustomTag/index.vue';
const props = withDefaults(defineProps<{
data: Record<string, any>[];
showCheckbox?: boolean;
size?: string;
statusItems?: { icon?: string; count?: number; title: string; size?: string; color?: string; }[]
}>(), {
data: () => [],
showCheckbox: true,
size: '18px',
statusItems: () => []
});
const emits = defineEmits<{(e: 'change', data: Record<string, any>[]): void
}>();
const allChecked = ref<boolean>(false);
const allCheckedHalf = ref<boolean>(false);
const checkedList = ref<boolean[]>([]);
const localData = ref<Record<string, any>[]>([...props.data]);
watch(() => props.data, (val) => {
if (JSON.stringify(val) !== JSON.stringify(localData.value)) {
checkedList.value = [];
}
localData.value = [...val];
}, {
deep: true,
flush: 'post'
});
watch(() => allChecked.value, (val, oldValue) => {
if (val === oldValue) return false;
if (val) {
nextTick(() => {
setChecked(true);
});
} else {
nextTick(() => {
setChecked(false);
});
}
});
watch(() => checkedList.value, (val, oldValue) => {
if (JSON.parse(JSON.stringify(val)) === JSON.parse(JSON.stringify(oldValue))) return false;
handlerData(val);
if ((checkedList.value).filter(item => item).length === localData.value.length) {
allChecked.value = true;
} else if (checkedList.value.length === 0 || checkedList.value.filter(item => !item).length === localData.value.length) {
allChecked.value = false;
} else {
allCheckedHalf.value = true;
}
}, {
deep: true
});
const setChecked = (enable: boolean) => {
if (!enable) {
checkedList.value = [];
allCheckedHalf.value = false;
return false;
}
allCheckedHalf.value = false;
const arr = [];
localData.value.forEach((item, index) => {
arr[index] = true;
});
checkedList.value = [...arr];
};
const onCheckChange = (val) => {
if (val) allCheckedHalf.value = false;
};
const handlerData = (val) => {
const arr = [];
val.forEach((item, index) => {
if (item) arr.push(index);
});
const data = localData.value.filter((item, index) => {
return arr.includes(index);
});
emits('change', data);
};
</script>
<style scoped lang="scss">
$g-issue-list-box-border-color: #D3D3D3;
$g-issue-list-box-color: #606060;
.g-issue-list-box {
&-header {
display: flex;
align-items: center;
}
&-checkbox {
margin-right: 16px;
}
&-content {
height: auto;
}
&-item {
display: flex;
align-items: center;
padding: 32px 16px 16px 16px;
border-bottom: 1px solid $g-issue-list-box-border-color;
&.is-last {
border-bottom: none;
}
}
&-header-left {
display: flex;
align-items: center;
}
&-status {
display: flex;
align-items: center;
margin-right: 32px;
color: $g-issue-list-box-color;
&:last-of-type {
margin-right: 0;
}
}
&-status-icon {
margin-bottom: -4px;
}
&-status-title {
padding: 0 8px 0 16px;
}
}
</style>