|
@@ -0,0 +1,189 @@
|
|
|
+<template>
|
|
|
+ <!-- 通用列表页 -->
|
|
|
+ <view class="d-flex flex-column bg-base">
|
|
|
+ <!-- 搜索 -->
|
|
|
+ <view v-if="showSearch" class="d-flex flex-col p-2">
|
|
|
+ <uni-search-bar
|
|
|
+
|
|
|
+ v-model="searchValue"
|
|
|
+ radius="100"
|
|
|
+ bgColor="#fff"
|
|
|
+ :placeholder="`输入关键词搜索${title}`"
|
|
|
+ clearButton="auto"
|
|
|
+ cancelButton="none"
|
|
|
+ @confirm="doSearch"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+ <!-- 下拉框 -->
|
|
|
+ <view
|
|
|
+ v-if="dropDownNames && dropDownNames.length > 0"
|
|
|
+ class="d-flex flex-row justify-around p-2 pt-0"
|
|
|
+ >
|
|
|
+ <SimpleDropDownPicker
|
|
|
+ v-for="(drop, k) in dropDownNames" :key="k"
|
|
|
+ :modelValue="dropDownValues[k] || drop.defaultSelectedValue"
|
|
|
+ :columns="drop.options"
|
|
|
+ @update:modelValue="(v) => handleChangeDropDownValue(k, v)"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+ <!-- 列表 -->
|
|
|
+ <view class="d-flex flex-row flex-wrap justify-between p-3 pt-0">
|
|
|
+ <view
|
|
|
+ v-for="item in listLoader.list.value"
|
|
|
+ :key="item.id"
|
|
|
+ :class="[
|
|
|
+ itemType.endsWith('-2') ? 'width-1-2' : 'w-100'
|
|
|
+ ]"
|
|
|
+ >
|
|
|
+ <Box2LineLargeImageUserShadow
|
|
|
+ v-if="itemType.startsWith('image-large')"
|
|
|
+ classNames="ml-2 mb-3"
|
|
|
+ titleColor="title-text"
|
|
|
+ :image="item.image"
|
|
|
+ :title="item.title"
|
|
|
+ :desc="item.desc"
|
|
|
+ @click="goDetails(item.id)"
|
|
|
+ />
|
|
|
+ <Box2LineImageRightShadow
|
|
|
+ v-else-if="itemType.startsWith('article-common')"
|
|
|
+ classNames="ml-2 mb-3"
|
|
|
+ titleColor="title-text"
|
|
|
+ :image="item.image"
|
|
|
+ :title="item.title"
|
|
|
+ :desc="item.desc"
|
|
|
+ :wideImage="true"
|
|
|
+ @click="goDetails(item.id)"
|
|
|
+ />
|
|
|
+ <Box2LineImageRightShadow
|
|
|
+ v-else-if="itemType.startsWith('article-character')"
|
|
|
+ classNames="ml-2 mb-3"
|
|
|
+ titleColor="title-text"
|
|
|
+ :image="item.image"
|
|
|
+ :title="item.title"
|
|
|
+ :tags="item.keywords"
|
|
|
+ :desc="item.desc"
|
|
|
+ @click="goDetails(item.id)"
|
|
|
+ />
|
|
|
+
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <SimplePageListLoader :loader="listLoader" />
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { onMounted, ref, type PropType } from 'vue';
|
|
|
+import { useSimplePageListLoader } from '@/common/composeabe/SimplePageListLoader';
|
|
|
+import { navTo } from '@/common/utils/PageAction';
|
|
|
+import SimplePageListLoader from '@/common/components/SimplePageListLoader.vue';
|
|
|
+import Box2LineLargeImageUserShadow from '@/pages/parts/Box2LineLargeImageUserShadow.vue';
|
|
|
+import Box2LineImageRightShadow from '@/pages/parts/Box2LineImageRightShadow.vue';
|
|
|
+import SimpleDropDownPicker, { type SimpleDropDownPickerItem } from '@/common/components/SimpleDropDownPicker.vue';
|
|
|
+
|
|
|
+export interface DropDownNames {
|
|
|
+ options: SimpleDropDownPickerItem[],
|
|
|
+ defaultSelectedValue: number|string,
|
|
|
+}
|
|
|
+export interface CommonListItem extends Record<string, any> {
|
|
|
+ id: number,
|
|
|
+ image: string,
|
|
|
+ title: string,
|
|
|
+}
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ /**
|
|
|
+ * 标题
|
|
|
+ */
|
|
|
+ title: {
|
|
|
+ type: String,
|
|
|
+ default: '通用列表页',
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 是否显示搜索框
|
|
|
+ */
|
|
|
+ showSearch: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true,
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 下拉框选项控制
|
|
|
+ */
|
|
|
+ dropDownNames: {
|
|
|
+ type: Object as PropType<DropDownNames[]>,
|
|
|
+ default: null,
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 列表项类型
|
|
|
+ */
|
|
|
+ itemType: {
|
|
|
+ type: String as PropType<'image-large-2'|'image-large'|'article-common'|'article-character'>,
|
|
|
+ default: 'article-common',
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 分页大小
|
|
|
+ */
|
|
|
+ pageSize: {
|
|
|
+ type: Number,
|
|
|
+ default: 8,
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 加载数据函数
|
|
|
+ * @param page 页码,从1开始
|
|
|
+ * @param pageSize 分页大小
|
|
|
+ * @param searchText 搜索文本
|
|
|
+ * @param dropDownValues 下拉框值
|
|
|
+ */
|
|
|
+ load: {
|
|
|
+ type: Function as PropType<(
|
|
|
+ page: number,
|
|
|
+ pageSize: number,
|
|
|
+ searchText: string,
|
|
|
+ dropDownValues: number[],
|
|
|
+ ) => Promise<CommonListItem[]>>,
|
|
|
+ required: true,
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 点击详情跳转页面路径
|
|
|
+ */
|
|
|
+ detailsPage: {
|
|
|
+ type: String,
|
|
|
+ default: '/pages/article/details'
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 详情跳转页面参数
|
|
|
+ */
|
|
|
+ detailsParams: {
|
|
|
+ type: Object as PropType<Record<string, any>>,
|
|
|
+ default: () => ({})
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const dropDownValues = ref<any>([]);
|
|
|
+const searchValue = ref('');
|
|
|
+const listLoader = useSimplePageListLoader(props.pageSize, async (page, pageSize) => await props.load(
|
|
|
+ page, pageSize,
|
|
|
+ searchValue.value,
|
|
|
+ dropDownValues.value,
|
|
|
+));
|
|
|
+
|
|
|
+function handleChangeDropDownValue(index: number, value: number) {
|
|
|
+ dropDownValues.value[index] = value;
|
|
|
+ listLoader.loadData(undefined, true);
|
|
|
+}
|
|
|
+function doSearch() {
|
|
|
+ listLoader.loadData(undefined, true);
|
|
|
+}
|
|
|
+function goDetails(id: number) {
|
|
|
+ navTo(props.detailsPage, {
|
|
|
+ ...props.detailsParams,
|
|
|
+ id
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ uni.setNavigationBarTitle({
|
|
|
+ title: props.title,
|
|
|
+ })
|
|
|
+ listLoader.loadData(undefined, true);
|
|
|
+});
|
|
|
+</script>
|