123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- <template>
- <!-- 通用列表页详情 -->
- <div class="content mb-2">
- <!-- 搜素栏 -->
- <div class="row mt-3 align-items-center">
- <!-- 左栏 -->
- <div class="col-sm-12 col-md-6 col-lg-6">
- <!-- 分类 -->
- <TagBar
- :tags="tagsData || []"
- :margin="[30, 70]"
- v-model:selectedTag="selectedTag"
- />
- <!-- 标题 -->
- <div v-if="showNav" class="nav-back-title">
- <img src="@/assets/images/BackArrow.png" alt="返回" @click="back" />
- <h2>{{ title }}</h2>
- </div>
- <!-- 标题 -->
- <div v-if="showTotal" class="nav-back-title">
- 共有 {{ newsLoader.total }} 个{{ title }}
- </div>
- </div>
- <!-- 右栏 -->
- <div class="col-sm-12 col-md-6 col-lg-6 d-flex flex-row justify-content-end align-items-start" style="gap:5px">
- <Dropdown
- v-for="(drop, k) in dropDownNames" :key="k"
- :selectedValue="dropDownValues[k]"
- :options="drop.options"
- labelKey="name"
- valueKey="id"
- style="max-width: 150px"
- @update:selectedValue="(v) => handleChangeDropDownValue(k, v)"
- />
- <SimpleInput v-if="showSearch" v-model="searchText" placeholder="请输入关键词" @enter="handleSearch">
- <template #suffix>
- <IconSearch
- class="search-icon"
- src="@/assets/images/news/IconSearch.png"
- alt="搜索"
- @click="newsLoader.loadData(undefined, true)"
- />
- </template>
- </SimpleInput>
- <button class="tab-button" v-if="showTableSwitch" @click="tableListShow=!tableListShow">
- ▼ 清单
- </button>
- </div>
- </div>
- </div>
- <div
- :class="[
- 'content',
- 'news-list',
- rowCount === 1 ? '' : 'grid',
- ]"
- >
- <!-- 新闻列表 -->
- <SimplePageContentLoader :loader="newsLoader">
- <div v-if="tableListShow" class="table-list">
- <table>
- <thead>
- <tr>
- <th>序号</th>
- <th>{{ tableSwitchOptions.title ?? '标题'}}</th>
- <th v-for="(t, k) in newsLoader.list.value[0]?.addItems || []" :key="k">{{ t.name }}</th>
- </tr>
- </thead>
- <tbody>
- <tr v-for="(item, k) in newsLoader.list.value" :key="item.id">
- <td>{{ (newsLoader.page.value - 1) * 100 + k + 1 }}</td>
- <td>{{ item.title }}</td>
- <td v-for="(t, k) in item.addItems || []" :key="k">{{ t.text }}</td>
- </tr>
- </tbody>
- </table>
- </div>
- <div v-else class="list">
- <div
- v-for="(item, k) in newsLoader.list.value"
- :key="item.id"
- :class="'item user-select-none main-clickable row-type'+rowType"
- :style="{ width: rowWidth }"
- @click="handleShowDetail(item)"
- >
- <img
- :src="item.image || defaultImage" alt="新闻图片"
- />
- <TitleDescBlock
- :title="item.title"
- :desc="item.desc"
- >
- <template #addon>
- <div v-if="item.bottomTags" class="tags">
- <div
- v-for="(tag, k) in item.bottomTags"
- :key="k"
- :class="tag ? '' : 'd-none'"
- >{{ tag }}</div>
- </div>
- <div v-if="item.addItems" class="extra">
- <div
- v-for="(addItem, k) in item.addItems"
- :key="k"
- class="d-flex flex-row align-items-center"
- :class="[
- addItem.text ? '' : 'd-none',
- ]"
- >
- <span class="desc">{{ addItem.name }}:</span>
- <span>{{ addItem.text }}</span>
- </div>
- </div>
- </template>
- </TitleDescBlock>
- </div>
- <div
- v-for="count of placeholderItemCount"
- :key="count"
- class="item empty"
- :style="{ width: rowWidth }"
- />
- </div>
- </SimplePageContentLoader>
- </div>
- <!-- 分页 -->
- <Pagination
- v-model:currentPage="newsLoader.page.value"
- :totalPages="newsLoader.totalPages.value"
- />
- </template>
- <script setup lang="ts">
- import { computed, onMounted, ref, watch, type PropType } from 'vue';
- import { useSimplePagerDataLoader } from '@/composeable/SimplePagerDataLoader';
- import { usePageAction } from '@/composeable/PageAction';
- import DateUtils from '@/common/utils/DateUtils';
- import TagBar from '../content/TagBar.vue';
- import Dropdown from '../controls/Dropdown.vue';
- import SimpleInput from '../controls/SimpleInput.vue';
- import SimplePageContentLoader from '@/components/content/SimplePageContentLoader.vue';
- import Pagination from '../controls/Pagination.vue';
- import TitleDescBlock from '../parts/TitleDescBlock.vue';
- import IconSearch from '../icons/IconSearch.vue';
- const { navTo, back } = usePageAction();
- export interface DropdownCommonItem {
- id: number;
- name: string;
- }
- export interface DropDownNames {
- options: (string|DropdownCommonItem)[],
- label?: string,
- defaultSelectedValue: number|string,
- }
- const tableListShow = ref(false);
- const props = defineProps({
- title: {
- type: String,
- default: '',
- },
- showTableSwitch: {
- type: Boolean,
- default: false,
- },
- tableSwitchOptions: {
- type: Object,
- default: () => ({}),
- },
- showNav: {
- type: Boolean,
- default: false,
- },
- showTotal: {
- type: Boolean,
- default: false,
- },
- prevPage: {
- type: Object as PropType<{
- title: string,
- url?: string,
- }>,
- default: null,
- },
- dropDownNames: {
- type: Object as PropType<DropDownNames[]>,
- default: null,
- },
- showSearch: {
- type: Boolean,
- default: true,
- },
- tagsData: {
- type: Object as PropType<{
- id: number,
- name: string,
- }[]>,
- default: null,
- },
- pageSize: {
- type: Number,
- default: 8,
- },
- rowCount: {
- type: Number,
- default: 2,
- },
- rowType: {
- type: Number,
- default: 1,
- },
- defaultSelectTag: {
- type: Number,
- default: 1,
- },
- load: {
- type: Function as PropType<(
- page: number,
- pageSize: number,
- selectedTag: number,
- searchText: string,
- dropDownValues: number[],
- ) => Promise<{
- page: number,
- total: number,
- data: any[],
- }>>,
- required: true,
- },
- showDetail: {
- type: Function as PropType<(item: any) => void>,
- default: null,
- },
- /**
- * 点击详情跳转页面路径
- */
- detailsPage: {
- type: String,
- default: '/news/detail'
- },
- /**
- * 详情跳转页面参数
- */
- detailsParams: {
- type: Object as PropType<Record<string, any>>,
- default: () => ({})
- },
- defaultImage: {
- type: String,
- default: ''
- },
- })
- const realRowCount = computed(() => {
- if (window.innerWidth < 768)
- return 1;
- return props.rowCount;
- });
- const rowWidth = computed(() => {
- switch (realRowCount.value) {
- case 2:
- return `calc(50% - 25px)`;
- case 3:
- return `calc(33% - 25px)`;
- case 4:
- return `calc(25% - 25px)`;
- }
- });
- const placeholderItemCount = computed(() => {
- switch (realRowCount.value) {
- case 2:
- case 3:
- case 4:
- return newsLoader.list.value.length % realRowCount.value;
- }
- return 0;
- });
- const searchText = ref('');
- const dropDownValues = ref<any>([]);
- function handleSearch() {
- newsLoader.loadData(undefined, true);
- }
- function handleChangeDropDownValue(index: number, value: number) {
- dropDownValues.value[index] = value;
- newsLoader.loadData(undefined, true);
- }
- function handleShowDetail(item: any) {
- if (props.showDetail)
- return props.showDetail(item);
- navTo(props.detailsPage, {
- id: item.id,
- ...props.detailsParams,
- });
- }
- const pageSize = ref(props.pageSize);
- const newsLoader = useSimplePagerDataLoader(pageSize, (page, size) => props.load(
- page, size,
- selectedTag.value,
- searchText.value,
- dropDownValues.value,
- ));
- //子分类
- const selectedTag = ref(props.defaultSelectTag);
- watch(() => props.defaultSelectTag, (v) => {
- selectedTag.value = v;
- })
- watch(() => props.dropDownNames, () => {
- loadDropValues();
- })
- watch(selectedTag, () => {
- newsLoader.loadData(undefined, true);
- })
- watch(tableListShow, (v) => {
- pageSize.value = v ? 100 : props.pageSize;
- newsLoader.loadData(undefined, true);
- })
- function loadDropValues() {
- dropDownValues.value = [];
- if (props.dropDownNames)
- for (const element of props.dropDownNames)
- dropDownValues.value.push(element.defaultSelectedValue);
- newsLoader.loadData(undefined, true);
- }
- onMounted(() => {
- setTimeout(() => {
- loadDropValues();
- }, 400);
- })
- defineExpose({
- reload() {
- newsLoader.loadData(undefined, true);
- }
- })
- </script>
- <style lang="scss">
- @use "@/assets/scss/colors";
- .nav-back-title {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: flex-start;
- h2 {
- font-size: 20px;
- font-family: SourceHanSerifCNBold;
- margin: 0;
- }
- img {
- width: 25px;
- height: 25px;
- cursor: pointer;
- margin-right: 10px;
- }
- }
- .search-icon {
- width: 25px;
- height: 25px;
- cursor: pointer;
- color: colors.$primary-color;
- }
- </style>
|