feat: new layout
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
<script setup lang="ts">
|
||||
import { isEmpty } from "lodash";
|
||||
import DynamicComponent from "~/components/dynamic-page/page-component/templates/index.vue";
|
||||
import { COLLECTION_PAGING_QUERY_DROP, getInputValue } from "@/utils/parseSQL";
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const emit = defineEmits(["dropData", "selectComponent"]);
|
||||
|
||||
const store = reactive({
|
||||
component: usePageComponentStore(),
|
||||
});
|
||||
|
||||
const _props = defineProps<{
|
||||
dataResult?: any[];
|
||||
dataQuery?: string;
|
||||
component?: any;
|
||||
label?: string;
|
||||
}>();
|
||||
|
||||
const SETTING_OPTIONS = {
|
||||
MAX_ELEMENT: 5,
|
||||
TEMPLATE: "Article",
|
||||
LAYOUT: "TYPE:Card",
|
||||
};
|
||||
|
||||
const page = ref(1);
|
||||
const limit = ref(5);
|
||||
const totals = ref(2);
|
||||
const type = "Article";
|
||||
const listArticlePaging = ref([]);
|
||||
|
||||
const listArticleByCategory = computed(() => {
|
||||
const data = getInputValue(_props.dataResult, "OBJECT");
|
||||
if (data && Object.keys(data)?.length > 0) {
|
||||
totals.value = data.Total;
|
||||
return data?.Data;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
const designObject = computed(() => {
|
||||
return _props.label ? getInputValue(_props.label, "OBJECT") : {};
|
||||
});
|
||||
|
||||
const dropData = (event: any) => {
|
||||
const queryBy = {
|
||||
Category: "Categories",
|
||||
};
|
||||
const { dataResult, dataType } = JSON.parse(event.dataTransfer.getData("category"));
|
||||
// const getDataQuery = `Get[${type}] Top[20] With[${queryBy[dataType]}:${dataResult.id}]`;
|
||||
const getDataQuery = COLLECTION_PAGING_QUERY_DROP(type, { key: queryBy[dataType], value: dataResult.id });
|
||||
emit("dropData", {
|
||||
dataResult: [],
|
||||
dataType,
|
||||
dataQuery: getDataQuery,
|
||||
});
|
||||
};
|
||||
|
||||
//?cpn_1=page:2&cpn_2=page:1
|
||||
// Get[Article] Top[5] With[Categories:1]
|
||||
const select = async (pageActive: number) => {
|
||||
const componentId = _props.component?.id;
|
||||
if (componentId) {
|
||||
router.push({
|
||||
query: {
|
||||
...route.query,
|
||||
[`cpn_${componentId}`]: `page:${pageActive}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
page.value = Number(pageActive);
|
||||
await loadPage(Number(pageActive));
|
||||
};
|
||||
const handleRouteChange = async (query: any) => {
|
||||
const param = query[`cpn_${_props.component?.id}`];
|
||||
if (param) {
|
||||
const [_, value] = param.split(":") || [];
|
||||
page.value = value;
|
||||
await loadPage(value);
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (route.query[`cpn_${_props.component?.id}`]) handleRouteChange(route.query);
|
||||
});
|
||||
|
||||
const loadPage = async (page: number) => {
|
||||
let newDataQuery = "";
|
||||
const regex = /Page\[(.*?)\]/;
|
||||
const match = _props.component?.settings?.dataQuery?.match(regex);
|
||||
if (match) {
|
||||
const [pageData] = match;
|
||||
newDataQuery = _props.component?.settings?.dataQuery.replace(pageData, `Page[${page}]`);
|
||||
} else {
|
||||
newDataQuery = _props.component?.settings?.dataQuery + ` Page[${page}]`;
|
||||
}
|
||||
const res = await store.component.getOverviewPageComponentById(Number(_props.component?.id), newDataQuery);
|
||||
const data = getInputValue(res?.settings?.dataResult, "OBJECT");
|
||||
if (Object.keys(data).length > 0) {
|
||||
totals.value = data.Total;
|
||||
listArticlePaging.value = data?.Data || [];
|
||||
}
|
||||
};
|
||||
|
||||
const handleNextPrev = (type: "+" | "-") => {
|
||||
if (listArticleByCategory.value?.length > 0) {
|
||||
if (type === "+") {
|
||||
page.value += 1;
|
||||
} else if (type === "-") {
|
||||
page.value -= 1;
|
||||
}
|
||||
select(page.value);
|
||||
}
|
||||
};
|
||||
|
||||
const selectComponent = () => {
|
||||
emit("selectComponent");
|
||||
};
|
||||
const mapActivesToItems = (index: number) => {
|
||||
if (designObject.value && designObject.value.listCss) {
|
||||
return designObject.value.listCss[index] || {};
|
||||
}
|
||||
return {};
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section>
|
||||
<div class="section-container" @click="selectComponent" @dragover.prevent @drop.stop.prevent="dropData($event)" :class="[listArticleByCategory && listArticleByCategory?.length > 0 ? '' : 'noData']">
|
||||
<div class="section-layout" :style="designObject['div.section']">
|
||||
<template v-if="listArticleByCategory?.length > 0">
|
||||
<template v-for="(component, index) in listArticlePaging?.length > 0 ? listArticlePaging : listArticleByCategory">
|
||||
<DynamicComponent
|
||||
:key="index"
|
||||
v-if="!isEmpty(component)"
|
||||
:settings="{
|
||||
template: SETTING_OPTIONS.TEMPLATE,
|
||||
layout: SETTING_OPTIONS.LAYOUT,
|
||||
dataResult: { ...component },
|
||||
label: mapActivesToItems(Number(index)),
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="empty"><h6 class="px-2 text-center">Nội dung danh sách bài viết của danh mục sẽ ở đây</h6></div>
|
||||
</template>
|
||||
<div class="button-page flex">
|
||||
<a class="btn-page prev-page" @click.stop="() => handleNextPrev('-')" v-if="page > 1">
|
||||
<i class="el-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</i>
|
||||
</a>
|
||||
<a v-if="listArticleByCategory?.length > 0" :class="['btn-page', page === index + 1 && 'active']" @click.stop="() => select(index + 1)" v-for="(_, index) in Math.ceil(totals / limit)">{{ index + 1 }}</a>
|
||||
<a class="btn-page next-page" @click.stop="() => handleNextPrev('+')" v-if="page < Math.ceil(totals / limit)">
|
||||
<i class="el-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M340.864 149.312a30.592 30.592 0 0 0 0 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0 0 0 42.752 29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-44.672L382.592 149.376a29.12 29.12 0 0 0-41.728 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.section-container {
|
||||
.section-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* gap: 10px; */
|
||||
overflow-x: scroll;
|
||||
&.border-custom {
|
||||
border-color: #e5e5e5 !important;
|
||||
}
|
||||
&.borderLeft {
|
||||
border-left: 1px solid;
|
||||
}
|
||||
&.borderRight {
|
||||
border-right: 1px solid;
|
||||
}
|
||||
&.borderTop {
|
||||
border-top: 1px solid;
|
||||
}
|
||||
&.borderBottom {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
}
|
||||
.empty {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 50px;
|
||||
background-color: #409eff;
|
||||
display: flex;
|
||||
white-space: normal;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
h6 {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.basic-article {
|
||||
&.article {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
&.noData {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
justify-content: center;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.button-page {
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-page {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
border: 1px solid #409eff;
|
||||
border-radius: 3px;
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
background: #409eff;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-empty {
|
||||
padding: 12px 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user