Browse Source

后端多列排序;前端导出完善

master
wanggang 1 year ago
parent
commit
86746d655a
  1. 2
      code/WebApp/vanilla/components/form/form-input.js
  2. 175
      code/WebApp/vanilla/components/list/index.js
  3. 7
      code/WebApp/vanilla/models/centralized-control.js
  4. 24
      code/WebApp/vanilla/models/code-setting.js
  5. 4
      code/WebApp/vanilla/request/index.js
  6. 19
      code/WebApp/vanilla/router/routes.js
  7. 56
      code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/Repository/SettleAccountEfCoreRepository.cs

2
code/WebApp/vanilla/components/form/form-input.js

@ -56,6 +56,7 @@ export default {
</template> </template>
<template v-else> <template v-else>
<el-input <el-input
clearable
:disabled="getDisabled()" :disabled="getDisabled()"
:placeholder="schema.title" :placeholder="schema.title"
v-model="model[prop]" v-model="model[prop]"
@ -64,6 +65,7 @@ export default {
v-if="schema.input==='password'" v-if="schema.input==='password'"
/> />
<el-input <el-input
clearable
:disabled="getDisabled()" :disabled="getDisabled()"
:placeholder="schema.title" :placeholder="schema.title"
v-model="model[prop]" v-model="model[prop]"

175
code/WebApp/vanilla/components/list/index.js

@ -1,5 +1,5 @@
import html, { getProp } from "html"; import html, { getProp } from "html";
import request, { get, post } from "../../request/index.js"; import request, { get, getUrl, post } from "../../request/index.js";
import { defineAsyncComponent, ref, reactive, onMounted } from "vue"; import { defineAsyncComponent, ref, reactive, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
@ -54,7 +54,7 @@ export default {
<span>{{item.meta.title}}</span> <span>{{item.meta.title}}</span>
</el-button> </el-button>
</template> </template>
<el-button @click="click('filter',selectedRows)"> <el-button @click="click('filter',selectedRows)" v-if="config.query.hasFilter">
<el-icon><ep-filter /></el-icon> <el-icon><ep-filter /></el-icon>
<span>{{$t('筛选')}}</span> <span>{{$t('筛选')}}</span>
</el-button> </el-button>
@ -246,7 +246,7 @@ export default {
<el-option v-for="item in versions" :value="item.value" :label="item.label" /> <el-option v-for="item in versions" :value="item.value" :label="item.label" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="选择版本" label-width="80px"> <el-form-item label="选择期间" label-width="80px">
<el-select v-model="importModel.factory"> <el-select v-model="importModel.factory">
<el-option v-for="item in versions" :value="item.value" :label="item.label" /> <el-option v-for="item in versions" :value="item.value" :label="item.label" />
</el-select> </el-select>
@ -269,30 +269,30 @@ export default {
<el-form :model="filterList" inline class="filter"> <el-form :model="filterList" inline class="filter">
<el-row v-for="(item,index) in filterList"> <el-row v-for="(item,index) in filterList">
<el-col :span="6"> <el-col :span="6">
<el-select v-model="item.column" :placeholder="$t('字段')"> <el-select clearable :disabled="item.readOnly" v-model="item.column" :placeholder="$t('字段')">
<el-option <template v-for="(value, prop) in config.edit.schema.properties">
v-for="(value, prop) in config.edit.schema.properties"
:value="prop"
:label="value.title"
/>
</el-select>
</el-col>
<el-col :span="6">
<el-select v-model="item.operator" :placeholder="$t('操作符')">
<template v-if="item.column">
<el-option <el-option
v-for="item in getOperators(config.edit.schema.properties[item.column])" v-if="value.type!=='object'&&value.type!=='array'&&value.input!=='textarea'"
value="item.value" :value="prop"
:label="item.label" :label="value.title"
/> />
</template> </template>
</el-select> </el-select>
</el-col> </el-col>
<el-col :span="6" v-if="item.column"> <el-col :span="6" v-if="item.column">
<el-select clearable :disabled="item.readOnly" v-model="item.action" :placeholder="$t('操作符')">
<el-option
v-for="item in getOperators(config.edit.schema.properties[item.column])"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-col>
<el-col :span="8" v-if="item.column">
<app-form-input v-model="item" :schema="config.edit.schema.properties[item.column]" prop="value" /> <app-form-input v-model="item" :schema="config.edit.schema.properties[item.column]" prop="value" />
</el-col> </el-col>
<el-col :span="2"> <el-col :span="2" v-if="!item.readOnly&&item.action">
<el-button circle @click="filterList.splice(index, 1)" v-if="!item.readOnly"> <el-button circle @click="filterList.splice(index, 1)">
<template #icon> <template #icon>
<ep-close /> <ep-close />
</template> </template>
@ -405,7 +405,7 @@ export default {
const versions = ref([]); const versions = ref([]);
const fileList = ref([]); const fileList = ref([]);
const getSortModel = (model) => { const getSortModel = (model) => {
model.orderBy (model.sorting ?? "")
.split(",") .split(",")
.map((o) => o.trim()) .map((o) => o.trim())
.filter((o) => o) .filter((o) => o)
@ -418,7 +418,7 @@ export default {
const getColumns = (schema) => { const getColumns = (schema) => {
Object.keys(schema.properties).forEach((propertyName) => { Object.keys(schema.properties).forEach((propertyName) => {
const property = schema.properties[propertyName]; const property = schema.properties[propertyName];
if (property.type !== "object" && property.type !== "array" && !property.hidden && property.showForList) { if (property.showForList || (property.type !== "object" && property.type !== "array" && !property.hidden)) {
columns.value.push({ name: propertyName, title: property.title, checked: true }); columns.value.push({ name: propertyName, title: property.title, checked: true });
} }
}); });
@ -434,41 +434,21 @@ export default {
} else { } else {
sortColumns.value.set(prop, order); sortColumns.value.set(prop, order);
} }
queryModel.value.orderBy = Array.from(sortColumns.value) queryModel.value.sorting = Array.from(sortColumns.value)
.map((o) => capitalize(o[0]) + (o[1] === "ascending" ? "" : ` DESC`)) .map((o) => capitalize(o[0]) + (o[1] === "ascending" ? "" : ` DESC`))
.join(","); .join(",");
await load(); await load();
}; };
const showColumn = (item, prop) => { const showColumn = (item, prop) => {
if (item.showForList === true) { return columns.value.some((o) => o.name === prop && o.checked);
return true;
}
return (
item.type !== "object" &&
item.type !== "array" &&
!item.hidden &&
columns.value.findIndex((o) => o.name === prop && o.checked) >= 0
);
}; };
const handleSelectionChange = (rows) => (selectedRows.value = rows); const handleSelectionChange = (rows) => (selectedRows.value = rows);
const load = async () => { const load = async () => {
tableLoading.value = true; tableLoading.value = true;
try { try {
const url = config.query.url; const url = config.query.url;
const method = config.query.method; const method = config.query.method;
// const postData = buildQuery();
queryModel.value.maxResultCount = pageModel.pageSize;
queryModel.value.skipCount = (pageModel.pageIndex - 1) * pageModel.pageSize;
//
const postData = JSON.parse(JSON.stringify(queryModel.value));
postData.filters = filterList.value.filter((o) => o.property && o.value);
if (postData.items) {
delete postData["items"];
}
if (postData.query?.id) {
delete postData.query["id"];
}
const listData = (await request(url, postData, { method })).data; const listData = (await request(url, postData, { method })).data;
const items = listData.items; const items = listData.items;
if (tableSchema.value.isTree) { if (tableSchema.value.isTree) {
@ -570,8 +550,18 @@ export default {
await load(); await load();
} else if (item.path === "export") { } else if (item.path === "export") {
//export //export
editFormTitle.value = `${t(item.path)}${querySchema.value?.title}`; if ((item.pattern = "paged")) {
dialogVisible.value = true; const url = config.edit.exportUrl;
const method = config.edit.exportMethod;
const postData = buildQuery();
const response = await request(url, postData, { method });
if (!response.errors) {
window.open(getUrl(`settleaccount/getblobfile/download/${response.data}`));
}
} else {
editFormTitle.value = `${t(item.path)}${querySchema.value?.title}`;
dialogVisible.value = true;
}
} else if (item.path === "import") { } else if (item.path === "import") {
//import //import
try { try {
@ -682,9 +672,8 @@ export default {
subDrawer.value = true; subDrawer.value = true;
} }
}; };
const download = (response) => { const download = (url, filename) => {
const downloadUrl = window.URL.createObjectURL(response.data); const downloadUrl = window.URL.createObjectURL(url);
const filename = response.filename;
let link = document.createElement("a"); let link = document.createElement("a");
link.href = downloadUrl; link.href = downloadUrl;
link.download = filename; link.download = filename;
@ -694,7 +683,7 @@ export default {
const getImportTemplate = async () => { const getImportTemplate = async () => {
const url = `${baseUrl}/${editFormMode.value}`; const url = `${baseUrl}/${editFormMode.value}`;
const response = await get(url); const response = await get(url);
download(response); download(url, response.filename);
}; };
const handleChange = (uploadFile, uploadFiles) => { const handleChange = (uploadFile, uploadFiles) => {
fileList.value = uploadFiles; fileList.value = uploadFiles;
@ -708,22 +697,85 @@ export default {
}; };
const pushfilterList = () => { const pushfilterList = () => {
filterList.value.push({ filterList.value.push({
logic: "", logic: "and",
column: "", column: "",
action: "", action: "equal",
value: "", value: null,
}); });
}; };
const logic = [
{ value: "and", label: "且" },
{ value: "or", label: "或" },
];
const operators = [
{
value: "equal",
label: "等于",
},
{
value: "notEqual",
label: "不等于",
},
{
value: "biggerThan",
label: "大于",
},
{
value: "smallThan",
label: "小于",
},
{
value: "biggerThanOrEqual",
label: "大于等于",
},
{
value: "smallThanOrEqual",
label: "小于等于",
},
{
value: "like",
label: "类似于",
},
{
value: "notLike",
label: "不类似于",
},
{
value: "in",
label: "包含于",
},
{
value: "notIn",
label: "不包含于",
},
];
const getOperators = (schema) => { const getOperators = (schema) => {
if (schema.type === "int") { const values = ["equal", "notEqual"];
if (schema.type === "string") {
values.push("like", "notLike");
if (schema.input && ["year", "month", "date", "datetime"].includes(schema.input)) {
values.push("biggerThan", "smallThan", "biggerThanOrEqual", "smallThanOrEqual");
}
} else if (schema.type === "boolean") {
} else {
values.push("biggerThan", "smallThan", "biggerThanOrEqual", "smallThanOrEqual");
} }
return [ return operators.filter((o) => values.includes(o.value));
{
value: 0,
label: "等于",
},
];
}; };
function buildQuery() {
queryModel.value.maxResultCount = pageModel.pageSize;
queryModel.value.skipCount = (pageModel.pageIndex - 1) * pageModel.pageSize;
//
const postData = JSON.parse(JSON.stringify(queryModel.value));
postData.filters = filterList.value.filter((o) => o.column && o.action && (o.value || o.value === false));
if (postData.items) {
delete postData["items"];
}
if (postData.query?.id) {
delete postData.query["id"];
}
return postData;
}
onMounted(async () => { onMounted(async () => {
if (route.meta.children?.length) { if (route.meta.children?.length) {
for (const item of route.meta.children) { for (const item of route.meta.children) {
@ -734,7 +786,8 @@ export default {
} }
// //
queryModel.value = schemaToModel(config.query.schema); queryModel.value = schemaToModel(config.query.schema);
filterList.value = queryModel.value.filters; getSortModel(queryModel.value);
filterList.value = queryModel.value?.filters ?? [];
//pushfilterList(); //pushfilterList();
// if (!querySchema.value) { // if (!querySchema.value) {
// const vm = (await get(indexUrl)).data; // const vm = (await get(indexUrl)).data;

7
code/WebApp/vanilla/models/centralized-control.js

@ -70,6 +70,7 @@ export default function () {
query: { query: {
url: queryUrl, url: queryUrl,
method: queryMethod, method: queryMethod,
hasFilter: true,
schema: { schema: {
title: "通用代码", title: "通用代码",
type: "object", type: "object",
@ -96,10 +97,10 @@ export default function () {
}, },
default: [ default: [
{ {
logic: 0, logic: "and",
column: "year", column: "year",
action: 6, action: "like",
value: "", value: null,
readOnly: true, readOnly: true,
}, },
], ],

24
code/WebApp/vanilla/models/code-setting.js

@ -46,34 +46,29 @@ const createUrl = `${baseUrl}/create`;
const updateUrl = `${baseUrl}/update/%s`; const updateUrl = `${baseUrl}/update/%s`;
const deleteUrl = `${baseUrl}/delete-list`; const deleteUrl = `${baseUrl}/delete-list`;
const importUrl = `${baseUrl}/code-setting-upload-excel-import`; const importUrl = `${baseUrl}/code-setting-upload-excel-import`;
const exportUrl = `${baseUrl}/export`;
const queryMethod = "POST"; const queryMethod = "POST";
const detailsMethod = "POST"; const detailsMethod = "POST";
const createMethod = "POST"; const createMethod = "POST";
const updateMethod = "POST"; const updateMethod = "POST";
const deleteMethod = "POST"; const deleteMethod = "POST";
const importMethod = "POST"; const importMethod = "POST";
const exportMethod = "POST";
export default function () { export default function () {
return { return {
query: { query: {
url: queryUrl, url: queryUrl,
method: queryMethod, method: queryMethod,
hasFilter: true,
schema: { schema: {
title: "通用代码", title: "通用代码",
type: "object", type: "object",
properties: { properties: {
project: {
type: "string",
},
value: {
type: "string",
},
description: {
type: "string",
},
filters: { filters: {
title: "项目", title: "项目",
type: "array", type: "array",
hidden: true,
items: { items: {
type: "object", type: "object",
properties: { properties: {
@ -91,6 +86,15 @@ export default function () {
}, },
}, },
}, },
default: [
{
logic: "and",
column: "project",
action: "like",
value: null,
readOnly: true,
},
],
}, },
skipCount: { skipCount: {
hidden: true, hidden: true,
@ -115,11 +119,13 @@ export default function () {
updateUrl, updateUrl,
deleteUrl, deleteUrl,
importUrl, importUrl,
exportUrl,
detailsMethod, detailsMethod,
createMethod, createMethod,
updateMethod, updateMethod,
deleteMethod, deleteMethod,
importMethod, importMethod,
exportMethod,
schema: schema, schema: schema,
}, },
}; };

4
code/WebApp/vanilla/request/index.js

@ -52,6 +52,8 @@ const getResult = async (response) => {
const contentType = response.headers.get("Content-Type"); const contentType = response.headers.get("Content-Type");
if (contentType.indexOf("application/json") > -1) { if (contentType.indexOf("application/json") > -1) {
result.data = await response.json(); result.data = await response.json();
} else if (contentType.indexOf("text/plain") > -1) {
result.data = await response.text();
} else if (contentType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { } else if (contentType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") {
result.data = await response.blob(); result.data = await response.blob();
result.filename = getFileName(response.headers.get("Content-Disposition")); result.filename = getFileName(response.headers.get("Content-Disposition"));
@ -145,4 +147,4 @@ async function request(url, data, options, withoutToken = false) {
} }
export default request; export default request;
export { get, post }; export { get, post, getUrl };

19
code/WebApp/vanilla/router/routes.js

@ -180,6 +180,15 @@ export default [
title: "通用代码", title: "通用代码",
icon: "file", icon: "file",
children: [ children: [
{
path: "query",
meta: {
type: "button",
title: "查询",
icon: "file",
isTop: true,
},
},
{ {
path: "create", path: "create",
meta: { meta: {
@ -207,6 +216,16 @@ export default [
isTop: true, isTop: true,
}, },
}, },
{
path: "export",
meta: {
type: "button",
title: "导出",
icon: "file",
isTop: true,
pattern: "paged",
},
},
], ],
}, },
}, },

56
code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/Repository/SettleAccountEfCoreRepository.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -92,45 +93,46 @@ namespace Win.Sfs.SettleAccount.Repository
protected static IQueryable<TEntity> GetSortingQueryable(IQueryable<TEntity> entities, string sorting) protected static IQueryable<TEntity> GetSortingQueryable(IQueryable<TEntity> entities, string sorting)
{ {
if (string.IsNullOrEmpty(sorting)) return string.IsNullOrEmpty(sorting)?entities:DynamicQueryableExtensions.OrderBy(entities, sorting);
{ //if (string.IsNullOrEmpty(sorting))
entities = entities.OrderByDescending("Id"); //{
} // entities = entities.OrderByDescending("Id");
else //}
{ //else
//{
var sortParams = sorting?.Split(' '); // var sortParams = sorting?.Split(' ');
var sortName = sortParams[0]; // var sortName = sortParams[0];
Type t = typeof(TEntity); // Type t = typeof(TEntity);
var _first=t.GetProperties().Where(p => p.Name.ToUpper() == sortName.ToUpper()).FirstOrDefault(); // var _first=t.GetProperties().Where(p => p.Name.ToUpper() == sortName.ToUpper()).FirstOrDefault();
if (_first != null) // if (_first != null)
{ // {
sortName = _first.Name; // sortName = _first.Name;
} // }
bool isDesc; // bool isDesc;
if (sortParams.Length > 1) // if (sortParams.Length > 1)
{ // {
var sortDirection = sortParams[1]; // var sortDirection = sortParams[1];
isDesc = sortDirection.ToUpper().Contains("DESC")?true:false; // isDesc = sortDirection.ToUpper().Contains("DESC")?true:false;
} // }
else // else
{ // {
isDesc = true; // isDesc = true;
} // }
entities = isDesc ? entities.OrderByDescending(sortName) : entities.OrderBy(sortName); // entities = isDesc ? entities.OrderByDescending(sortName) : entities.OrderBy(sortName);
} //}
return entities; //return entities;
} }

Loading…
Cancel
Save