<template>
<q-page :key="$route.fullPath">
<!--表头-->
<q-banner class="q-py-sm q-px-xs">
<q-breadcrumbs>
<q-breadcrumbs-el v-for="item in breadcrumbs" :key="item" :label="item" icon="widgets" />
</q-breadcrumbs>
</q-banner>
<div class="row q-py-xs q-px-xs items-center">
<div class="col-2">
<q-select
dense
outlined
v-model="scriptListContext.currentView"
:options="scriptListContext.views"
option-label="name"
option-value="id"
emit-value
map-options
@update:model-value="changedView"
fit
:label="t('Views')"
/>
</div>
<div class="q-mx-sm">
<q-btn v-if="scriptListContext.currentView == scriptListContext.recordType?.id" color="brown-5" :label="t('Customise View')" @click="customiseView" />
<q-btn v-else color="brown-5" :label="t('Edit View')" @click="editView" />
</div>
<div><actions-bar-component v-model:script-list-context="scriptListContext" :actives="scriptListContext.buttons" :data="null"></actions-bar-component></div>
</div>
<q-card flat class="q-pl-xs q-pb-xs q-br-sm q-pt-sm">
<q-card-actions class="bg-grey-3" align="left" vertical>
<q-btn color="grey" flat dense size="xs" :icon="expanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down'" @click="expanded = !expanded"></q-btn>
</q-card-actions>
<q-slide-transition>
<div v-show="expanded">
<div class="q-gutter-xs q-pa-sm">
<q-btn color="primary" @click="handleSearch" :label="t('Search')" />
<q-btn color="primary" @click="resetSearch" :label="t('Reset')" />
</div>
<q-card-section v-show="search.searchAvailableFilters.length > 0" class="q-gutter-x-md items-center example-break-row">
<div class="row items-start example-container q-gutter-xs">
<template v-for="availableFilter in search.searchAvailableFilters" :key="availableFilter.id">
<template v-if="availableFilter.field.fieldViewType == 0">
<q-checkbox v-model="queryParams[availableFilter.fieldCustomId]" :label="availableFilter.field.name" left-label></q-checkbox>
</template>
<template v-else-if="availableFilter.field.fieldViewType == 7">
<q-input
v-model="queryParamsTemp[availableFilter.fieldCustomId]"
:label="availableFilter.field.name"
@update:model-value="
(val) => {
updateFilter(availableFilter.field, val);
}
"
dense
></q-input>
</template>
<template v-else-if="availableFilter.field.fieldViewType == 2">
<q-input
filled
v-model="queryParamsTemp[availableFilter.fieldCustomId + '_from']"
:label="availableFilter.field.name"
dense
@update:model-value="
(val) => {
updateFilterDate(availableFilter, 'from', val);
}
"
>
<template v-slot:append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
<q-date
v-model="queryParamsTemp[availableFilter.fieldCustomId + '_from']"
mask="YYYY-MM-DD"
@update:model-value="
(val, reason, details) => {
updateFilterDate(availableFilter, 'from', val, reason, details);
}
"
>
<div class="row items-center justify-end">
<q-btn v-close-popup label="Close" color="primary" flat></q-btn>
</div>
</q-date>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<span>至</span>
<q-input
filled
v-model="queryParamsTemp[availableFilter.fieldCustomId + '_to']"
:label="availableFilter.field.name"
dense
@update:model-value="
(val) => {
updateFilterDate(availableFilter, 'to', val);
}
"
>
<template v-slot:append>
<q-icon name="event" class="cursor-pointer">
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
<q-date
v-model="queryParamsTemp[availableFilter.fieldCustomId + '_to']"
mask="YYYY-MM-DD"
@update:model-value="
(val, reason, details) => {
updateFilterDate(availableFilter, 'to', val, reason, details);
}
"
>
<div class="row items-center justify-end">
<q-btn v-close-popup label="Close" color="primary" flat></q-btn>
</div>
</q-date>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
</template>
<template v-else-if="availableFilter.field.fieldViewType == 13">
<q-select
v-model="queryParams[availableFilter.fieldCustomId]"
:options="fieldOptions[availableFilter.fieldCustomId]"
:label="availableFilter.field.name"
use-input
:option-value="availableFilter.field.fieldListOrRecordTypeIsList ? 'value' : 'id'"
option-label="name"
emit-value
map-options
@filter="
(val, update, abort) => {
filterFn(availableFilter.field, val, update, abort);
}
"
@filter-abort="abortFilterFn"
@update:model-value="updateModelValue"
:loading="optionLoading"
dense
clearable
></q-select>
</template>
</template>
</div>
</q-card-section>
</div>
</q-slide-transition>
<q-card-section class="q-pl-xs q-pr-md q-pb-xs">
<q-table
class="my-sticky-header-last-column-table"
row-key="id"
separator="cell"
:rows="scriptListContext.items"
:columns="columns"
dense
v-model:pagination="scriptListContext.pagination"
:rows-per-page-options="pageOptions"
:loading="loading"
@request="onRequest"
>
<template v-slot:top="props">
<q-checkbox v-model="showInactives" :label="t('ShowInactives')"></q-checkbox>
<q-space></q-space>
<q-btn color="primary" icon-right="archive" :label="t('ExportToExcel')" no-caps @click="exportTable"></q-btn>
<q-btn flat round dense :icon="props.inFullscreen ? 'fullscreen_exit' : 'fullscreen'" @click="props.toggleFullscreen" class="q-ml-md"></q-btn>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td v-for="col in columns" :key="col.name" :props="props">
<div>
{{ props.row.value }}
<div v-if="col.name == 'index'">{{ props.rowIndex + 1 }}</div>
<template v-else-if="col.name == 'actions'">
<actions-bar-component v-model:script-list-context="scriptListContext" :actives="scriptListContext.buttonsRow" :data="props.row"></actions-bar-component>
</template>
<q-checkbox v-else-if="col.fieldModel?.fieldViewType == fieldViewTypeEnum.CheckBox" v-model="props.row[col.fieldModel.customId]" dense disable></q-checkbox>
<template v-else-if="col.fieldModel?.fieldViewType == fieldViewTypeEnum.ListOrRecord">
{{ props.row[col.name] }}
</template>
<template v-else>
{{ props.row[col.name] }}
</template>
</div>
</q-td>
</q-tr>
</template>
</q-table>
</q-card-section>
</q-card>
</q-page>
</template>
<script setup lang="ts">
import { fetchListResult } from "src/api/customization/search";
import ActionsBarComponent from "src/components/ViewContent/ActionsBarComponent.vue";
import useTableList from "src/composables/useTableList";
import { IActive } from "src/interfaces/IActive";
import { IField } from "src/interfaces/IField";
import { Iparams } from "src/interfaces/Iparams";
import { Icolumn } from "src/interfaces/Icolumn";
import { IScriptListContext } from "src/interfaces/IScriptListContext";
import { formateList } from "src/modules/common-functions/datetimeOpration";
import { exportExcel } from "src/modules/common-functions/excelOpration";
import { onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import { toRecordTypePage } from "src/utils/routeRedirection";
import { fieldViewTypeEnum } from "src/enums/fieldViewTypeEnum";
import { operatorType } from "src/enums/operatorType";
import { getAction } from "src/api/manage";
import { addLoadingTotal, getLoadingTotal, loadingOne, setQuasar } from "src/modules/common-functions/loadingStatus";
import { listPage } from "src/modules/listPageCS";
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const showInactives = ref(true);
const breadcrumbs: string[] = String(route.name || "").split(",");
const optionLoading = ref(false);
const expanded = ref(true);
const fieldOptions: Record<string, any> = ref({});
const queryParamsTemp: Record<string, any> = ref({});
//初始化查询参数
const queryParas = ref<Iparams>({
RecordTypeId: "",
IsInActive: showInactives.value,
SkipCount: 0,
MaxResultCount: 1000,
Filter: "",
});
//行按钮
const defaultRowActives: Array<IActive> = [
{
id: "btn-view",
name: "view",
label: "查看",
displayAS: 0,
function: "",
showInView: false,
showInEdit: false,
location: "row",
isStandard: true,
},
{
id: "btn-edit",
name: "edit",
label: "编辑",
displayAS: 0,
function: "",
showInView: false,
showInEdit: false,
location: "row",
isStandard: true,
},
{
id: "btn-delete",
name: "delete",
label: "删除",
displayAS: 0,
function: "",
showInView: false,
showInEdit: false,
location: "row",
isStandard: true,
},
];
//主表按钮
const defaultActives: Array<IActive> = [
{
id: "btn-new",
name: "new",
label: "新建",
displayAS: 0,
function: "handleCreate",
showInView: false,
showInEdit: false,
location: "main",
isStandard: true,
},
];
//上下文对象
const scriptListContext = ref<IScriptListContext>({
items: [],
recordType: { id: route.query.id as string },
fieldOptions: {},
title: "",
views: [],
currentView: "",
fields: [],
colsApi: "",
rowsApi: "/master-currency/paged",
pagination: {
sortBy: "",
descending: false,
page: 1,
rowsPerPage: 100,
rowsNumber: 0,
},
buttons: defaultActives,
buttonsRow: defaultRowActives,
addButton: (button: IActive) => addButton(button),
removeButton: (buttonId: string) => removeButton(buttonId),
});
//列属性
const columns = ref<Icolumn[]>([
{
name: "index",
label: "序号",
field: "index",
align: "center" as const,
headerStyle: "width: 60px",
sortable: false,
},
{
name: "curName",
required: true,
label: "币别名称",
field: "curName",
align: "left" as const,
sortable: true,
},
{
name: "isoCode",
align: "center" as const,
label: "货币ISO代码",
field: "isoCode",
sortable: true,
},
{
name: "formatSymbol",
label: "显示符号",
field: "formatSymbol",
sortable: true,
},
{
name: "isInActive",
label: "禁用",
field: "isInActive",
sortable: true,
},
{
name: "actions",
label: "操作",
field: "actions",
align: "center" as const,
headerStyle: "width: 100px",
sortable: false,
},
]);
const _listPage = new listPage(scriptListContext as Ref<IScriptListContext>);
// 加载数据
onMounted(async () => {
await _listPage.pageInit();
getTableData();
});
watch(
() => scriptListContext.value.items,
(newValue, oldValue) => {
formateList(newValue, columns.value);
},
{ deep: true }
);
const addButton = function (button: IActive) {
if (button.location?.toLowerCase() == "row") {
scriptListContext.value.buttonsRow.push(button);
} else {
scriptListContext.value.buttons.push(button);
}
};
const removeButton = function (buttonId: string) {
const rb = scriptListContext.value.buttonsRow.find((item: IActive) => item.id == buttonId);
if (rb) {
scriptListContext.value.buttonsRow.splice(scriptListContext.value.buttonsRow.indexOf(rb), 1);
}
const btn = scriptListContext.value.buttons.find((item: IActive) => item.id == buttonId);
if (btn) {
scriptListContext.value.buttons.splice(scriptListContext.value.buttons.indexOf(btn), 1);
}
};
const {
$q,
queryParams,
pageOptions,
loading,
onRequest, //服务器端分页
search,
getTableData, //初始化加载
handleSearch, //search按钮
resetSearch, //reset按钮
} = useTableList(scriptListContext as Ref<IScriptListContext>, t);
setQuasar($q);
/** ========== export excel ============== */
const exportTable = async () => {
const exportData = await getExportData();
exportExcel(exportData, columns.value);
};
const getExportData = async () => {
const params: Iparams = {
Sorting: scriptListContext.value.pagination.sortBy,
Descending: scriptListContext.value.pagination.descending,
SkipCount: 0,
MaxResultCount: 300,
RecordTypeId: "",
IsInActive: false,
Filter: "",
};
const filterParams = {
Filter: JSON.stringify(queryParams.value),
};
if (!scriptListContext.value.pagination.rowsNumber) return [];
const totalPage = scriptListContext.value.pagination.rowsNumber / params.MaxResultCount;
addLoadingTotal(totalPage);
let exportData: Array<any> = [];
for (let i = 0; i < totalPage; i++) {
const allParams = Object.assign({}, params, filterParams);
await getAction(scriptListContext.value.rowsApi, allParams).then((res) => {
exportData = exportData.concat(res.items);
params.SkipCount += params.MaxResultCount;
loadingOne();
});
}
addLoadingTotal(-1 * getLoadingTotal());
return exportData;
};
/** ============= 过滤条件 ================ */
const getFieldOptions = async (field: IField, query: Iparams) => {
optionLoading.value = true;
await fetchListResult(field.fieldListOrRecordTypeId, query)
.then((response) => {
fieldOptions.value[field.customId] = response.items;
})
.catch((res) => {
console.log("error res:", res);
})
.finally(() => {
optionLoading.value = false;
});
};
const filterFn = async (field: IField, val: string, update: any, abort: any) => {
const queryFilter: any | object = {};
queryFilter["keywords"] = `opt_${operatorType.Like} ${val}`; // 'opt_6 ' + val
queryParas.value.SkipCount = 0;
queryParas.value.MaxResultCount = 100;
queryParas.value.Filter = JSON.stringify(queryFilter);
if (!val) {
update(async () => {
await getFieldOptions(field, queryParas.value);
});
return;
}
update(async () => {
await getFieldOptions(field, queryParas.value);
});
};
const abortFilterFn = () => {
// console.log('delayed filter aborted')
};
const updateModelValue = (val: any) => {
// console.log('updateModelValue', val)
};
const updateFilterDate = (availableFilter: any, direction: string, val: any, reason = "", details: object = {}) => {
var values = [queryParamsTemp.value[availableFilter.fieldCustomId + "_from"], queryParamsTemp.value[availableFilter.fieldCustomId + "_to"]];
queryParams.value[availableFilter.fieldCustomId] = "opt_11" + " " + JSON.stringify(values);
};
const updateFilter = (field: IField, val: any) => {
queryParams.value[field.customId] = "opt_6" + " " + val;
};
const changedView = (val: string) => {
toRecordTypePage(router, scriptListContext.value.recordType?.customId as string, "list", "", val);
};
/** ============= 视图定义 ================ */
const editView = () => {
toRecordTypePage(router, "search", "edit", scriptListContext.value.currentView, "3a0eb999-3fa6-d262-4b4e-f85331d1ca7d", undefined, "_blank");
};
const customiseView = () => {
toRecordTypePage(router, "search", "create", scriptListContext.value.currentView, "3a0eb999-3fa6-d262-4b4e-f85331d1ca7d", { copy: "T" }, "_blank");
};
</script>
<style lang="sass">
.example-break-row
.flex-break
flex: 1 0 100% !important
height: 0 !important
.my-sticky-header-last-column-table
/* height or max-height is important */
height: 70vh
table
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
/* specifying max-width so the example can
highlight the sticky column on any browser window */
// max-width: 600px
td:last-child
/* bg color is important for td; just specify one */
background-color: #eeeeee
tr th
position: sticky
/* higher than z-index for td below */
z-index: 2
/* bg color is important; just specify one */
background: #eeeeee
/* this will be the loading indicator */
thead tr:last-child th
/* height of all previous header rows */
top: 48px
/* highest z-index */
z-index: 3
thead tr:first-child th
top: 0
z-index: 1
tr:last-child th:last-child
/* highest z-index */
z-index: 3
td:last-child
z-index: 1
td:last-child, th:last-child
position: sticky
right: 0
/* prevent scrolling behind sticky top row on focus */
tbody
/* height of all previous header rows */
scroll-margin-top: 48px
tbody tr:nth-child(even)
background-color:#fafafa
a
&:link, &:visited
color: blue
text-decoration: none
&:hover
color: purple
&:active
color: blue
.text-orignblue
color: red !important
.horizontal-items
display: flex
flex-wrap: nowrap
justify-content: space-between
align-items: center
> q-item
margin-right: 10px
&:last-child
margin-right: 0
// 如果需要为 q-item 添加更多样式,可以在这里继续嵌套
// 例如:
// &:hover
// background-color: lightgray
</style>
一句一句给我解释
最新发布