Browse Source

修复优化以下问题:

1、w-grid增加属性stripe,用于配置数据行背景是否使用斑马纹显示。
2、w-grid查询支持多选机构与多选用户进行查询。
3、w-info 查看属性界面,需要增加label宽度与value宽度。
4、w-grid 分组,需增加 group-by-title 属性,用于生成分组标题的函数。
5、w-grid如果设置了列宽,表头太长被隐藏,增加鼠标移上去显示完整内容。
6、修复w-grid按钮栏切换语言无效。
7、w-toolbar中的按钮修改成根据系统主要颜色进行显示。
main
likunming 4 months ago
parent
commit
41c8c9d025
  1. 6
      io.sc.platform.core.frontend/src/platform/components/form/FormField.ts
  2. 4
      io.sc.platform.core.frontend/src/platform/components/grid/Header.vue
  3. 13
      io.sc.platform.core.frontend/src/platform/components/grid/Top.vue
  4. 7
      io.sc.platform.core.frontend/src/platform/components/grid/Tr.vue
  5. 1
      io.sc.platform.core.frontend/src/platform/components/grid/WGrid.vue
  6. 5
      io.sc.platform.core.frontend/src/platform/components/grid/extra/config/AloneGroup.vue
  7. 2
      io.sc.platform.core.frontend/src/platform/components/grid/extra/group/GroupTr.vue
  8. 2
      io.sc.platform.core.frontend/src/platform/components/grid/ts/Init.ts
  9. 5
      io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/Operator.ts
  10. 4
      io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Criteria.ts
  11. 5
      io.sc.platform.core.frontend/src/platform/components/grid/ts/function/DragAndDrop.ts
  12. 70
      io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Operator.ts
  13. 15
      io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RequestApi.ts
  14. 4
      io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RowData.ts
  15. 8
      io.sc.platform.core.frontend/src/platform/components/grid/ts/types/PropsType.ts
  16. 12
      io.sc.platform.core.frontend/src/platform/components/html/WHtmlA.vue
  17. 3
      io.sc.platform.core.frontend/src/platform/components/index.ts
  18. 2
      io.sc.platform.core.frontend/src/platform/components/select/WGridSelect.vue
  19. 11
      io.sc.platform.core.frontend/src/platform/components/select/WOrgSelect.vue
  20. 295
      io.sc.platform.core.frontend/src/platform/components/select/WRoleSelect.vue
  21. 7
      io.sc.platform.core.frontend/src/platform/components/toolbar/Buttons.vue
  22. 7
      io.sc.platform.core.frontend/src/platform/components/toolbar/ChildrenBtn.vue
  23. 2
      io.sc.platform.core.frontend/src/platform/components/toolbar/Toolbar.ts
  24. 8
      io.sc.platform.core.frontend/src/platform/components/toolbar/WToolbar.vue
  25. 1
      io.sc.platform.core.frontend/src/platform/index.ts
  26. 27
      io.sc.platform.core.frontend/src/views/likm/Form.vue
  27. 34
      io.sc.platform.core.frontend/src/views/likm/Grid.vue
  28. 1
      io.sc.platform.lcdp/build.gradle
  29. 22
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/controller/UserSearchController.java
  30. 10
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/service/UserSearchService.java
  31. 133
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/service/impl/UserSearchServiceImpl.java
  32. 46
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/support/UserSearchQueryParameter.java
  33. 162
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/vo/UserSearchVo.java
  34. 4
      io.sc.platform.lcdp/src/main/resources/META-INF/platform/plugins/components.json
  35. 16
      io.sc.platform.system/src/main/java/io/sc/platform/system/user/controller/UserWebController.java
  36. 9
      io.sc.platform.system/src/main/java/io/sc/platform/system/user/service/UserService.java
  37. 50
      io.sc.platform.system/src/main/java/io/sc/platform/system/user/service/impl/UserServiceImpl.java

6
io.sc.platform.core.frontend/src/platform/components/form/FormField.ts

@ -105,7 +105,11 @@ export const getDefaultValue = (field) => {
} else if (field.type === 'w-checkbox-group') { } else if (field.type === 'w-checkbox-group') {
return []; return [];
} else if ( } else if (
(field.type === 'w-select' || field.type === 'w-user-select' || field.type === 'w-org-select' || field.type === 'w-grid-select') && (field.type === 'w-select' ||
field.type === 'w-user-select' ||
field.type === 'w-org-select' ||
field.type === 'w-grid-select' ||
field.type === 'w-role-select') &&
field.multiple field.multiple
) { ) {
return []; return [];

4
io.sc.platform.core.frontend/src/platform/components/grid/Header.vue

@ -26,7 +26,7 @@
:class="thClassHandle(c)" :class="thClassHandle(c)"
:props="tools.titleFM.multiTitleThPropsHandle(c, props.scope)" :props="tools.titleFM.multiTitleThPropsHandle(c, props.scope)"
style="font-weight: bold" style="font-weight: bold"
:title="c.title" :title="c.label"
> >
<span v-html="Tools.isUndefinedOrNull(c.label) ? '' : c.label"></span> <span v-html="Tools.isUndefinedOrNull(c.label) ? '' : c.label"></span>
</q-th> </q-th>
@ -56,7 +56,7 @@
:style="col.style + (col.name === '_sortNo_' ? 'padding: 0; min-width: 50px;width: 50px;max-width:50px' : '')" :style="col.style + (col.name === '_sortNo_' ? 'padding: 0; min-width: 50px;width: 50px;max-width:50px' : '')"
:class="thClassHandle(col)" :class="thClassHandle(col)"
style="font-weight: bold" style="font-weight: bold"
:title="col.title" :title="col.label"
> >
<span v-html="Tools.isUndefinedOrNull(col.label) ? '' : col.label"></span> <span v-html="Tools.isUndefinedOrNull(col.label) ? '' : col.label"></span>
</q-th> </q-th>

13
io.sc.platform.core.frontend/src/platform/components/grid/Top.vue

@ -25,7 +25,7 @@
<!-- 最右侧用户配置按钮 --> <!-- 最右侧用户配置按钮 -->
<div v-if="tools.props.configButton" class="flex-none pl-1"> <div v-if="tools.props.configButton" class="flex-none pl-1">
<q-btn round dense :size="denseToolbarComputed ? '13px' : undefined" icon="manage_accounts" unelevated outline> <q-btn round dense color="primary" :size="denseToolbarComputed ? '13px' : undefined" icon="manage_accounts" unelevated outline>
<q-popup-proxy v-model="tools.table.configStore.showConfigPanel"> <q-popup-proxy v-model="tools.table.configStore.showConfigPanel">
<ConfigPanel :scope="props.scope" :grid="tools.instance"></ConfigPanel> <ConfigPanel :scope="props.scope" :grid="tools.instance"></ConfigPanel>
</q-popup-proxy> </q-popup-proxy>
@ -35,9 +35,9 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject, ref, onBeforeMount } from 'vue'; import { computed, inject, ref, onBeforeMount, nextTick } from 'vue';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { Tools, $t } from '@/platform'; import { Tools, $t, eventBus } from '@/platform';
import ConfigPanel from './extra/config/ConfigPanel.vue'; import ConfigPanel from './extra/config/ConfigPanel.vue';
import AdvancedQuery from './extra/advanced-query/AdvancedQuery.vue'; import AdvancedQuery from './extra/advanced-query/AdvancedQuery.vue';
import { Constant, GridTools } from './ts/index'; import { Constant, GridTools } from './ts/index';
@ -61,6 +61,13 @@ const props = defineProps({
}); });
const buttons = tools.bm.getButtonConfig(); const buttons = tools.bm.getButtonConfig();
eventBus.on('onLocaleChanged', (local) => {
nextTick(() => {
//
handleToolbarActions();
});
});
const queryFormFieldsComputed = computed(() => { const queryFormFieldsComputed = computed(() => {
if (localeFlag.value) { if (localeFlag.value) {
return tools.table.queryFormDisplayFields; return tools.table.queryFormDisplayFields;

7
io.sc.platform.core.frontend/src/platform/components/grid/Tr.vue

@ -3,7 +3,10 @@
ref="trRef" ref="trRef"
:props="props.scope" :props="props.scope"
:no-hover="trNoHoverComputed" :no-hover="trNoHoverComputed"
:class="{ selected: trSelectedClassComputed, mouseDisabled: !scope.row[Constant.FIELD_NAMES.SELECTABLE] }" :class="{
selected: trSelectedClassComputed,
mouseDisabled: !scope.row[Constant.FIELD_NAMES.SELECTABLE],
}"
:draggable="draggableComputed" :draggable="draggableComputed"
@click.stop="tools.em.rowClick($event, props.scope.row, props.scope.rowIndex)" @click.stop="tools.em.rowClick($event, props.scope.row, props.scope.rowIndex)"
@dblclick.stop="tools.em.rowDbClick($event, scope.row, scope.rowIndex)" @dblclick.stop="tools.em.rowDbClick($event, scope.row, scope.rowIndex)"
@ -44,7 +47,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject, ref } from 'vue'; import { computed, inject, ref, onMounted } from 'vue';
import { Tools } from '@/platform'; import { Tools } from '@/platform';
import { Constant, GridTools } from './ts/index'; import { Constant, GridTools } from './ts/index';
import Td from './Td.vue'; import Td from './Td.vue';

1
io.sc.platform.core.frontend/src/platform/components/grid/WGrid.vue

@ -169,6 +169,7 @@ onMounted(async () => {
} else { } else {
tools.opFM.resetStyleVariableValue(); tools.opFM.resetStyleVariableValue();
} }
tools.opFM.setStripe();
}); });
const getTableRef = () => { const getTableRef = () => {

5
io.sc.platform.core.frontend/src/platform/components/grid/extra/config/AloneGroup.vue

@ -17,7 +17,7 @@
</q-expansion-item> </q-expansion-item>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { inject, ref } from 'vue'; import { inject, ref, nextTick } from 'vue';
import { GridTools, Constant } from '../../ts/index'; import { GridTools, Constant } from '../../ts/index';
const tools = <GridTools>inject('tools'); const tools = <GridTools>inject('tools');
@ -50,5 +50,8 @@ const groupByFieldChange = (col: string) => {
tools.table.configStore.aloneGroupByField = undefined; tools.table.configStore.aloneGroupByField = undefined;
} }
tools.dataFM.setExtraProperty(tools.table.rows); tools.dataFM.setExtraProperty(tools.table.rows);
nextTick(() => {
tools.opFM.resetStripeStyle();
});
}; };
</script> </script>

2
io.sc.platform.core.frontend/src/platform/components/grid/extra/group/GroupTr.vue

@ -1,7 +1,7 @@
<template> <template>
<template v-if="props.scope.pageIndex === 0 && tools.table.configStore.aloneGroupRecords.length > 0"> <template v-if="props.scope.pageIndex === 0 && tools.table.configStore.aloneGroupRecords.length > 0">
<template v-for="(record, index) in tools.table.configStore.aloneGroupRecords" :key="index"> <template v-for="(record, index) in tools.table.configStore.aloneGroupRecords" :key="index">
<q-tr ref="trRef" @click.stop="changeExpand(record)"> <q-tr ref="trRef" class="groupTr" @click.stop="changeExpand(record)">
<q-td :colspan="tools.cm.displayColumnNum.value"> <q-td :colspan="tools.cm.displayColumnNum.value">
<q-btn <q-btn
:size="tools.table.configStore.dense || tools.table.configStore.denseBody ? 'xs' : 'sm'" :size="tools.table.configStore.dense || tools.table.configStore.denseBody ? 'xs' : 'sm'"

2
io.sc.platform.core.frontend/src/platform/components/grid/ts/Init.ts

@ -25,6 +25,7 @@ export class Init {
addDataUrl: { type: String, default: '' }, // 新增数据url addDataUrl: { type: String, default: '' }, // 新增数据url
editDataUrl: { type: String, default: '' }, // 修改数据url editDataUrl: { type: String, default: '' }, // 修改数据url
removeDataUrl: { type: String, default: '' }, // 删除数据url removeDataUrl: { type: String, default: '' }, // 删除数据url
customFetch: { type: Function, default: undefined }, // 自定义获取数据函数,若表格内置的查询参数或方式(GET、POST)等不满足使用场景,可使用该函数自定义进行查询,只需返回的结果结构与内置查询一致即可。
dense: { type: Boolean, default: undefined }, // 表格整体紧凑模式 dense: { type: Boolean, default: undefined }, // 表格整体紧凑模式
denseHeader: { type: Boolean, default: undefined }, // 表格列标题紧凑模式 denseHeader: { type: Boolean, default: undefined }, // 表格列标题紧凑模式
denseBody: { type: Boolean, default: undefined }, // 表格数据行紧凑模式 denseBody: { type: Boolean, default: undefined }, // 表格数据行紧凑模式
@ -32,6 +33,7 @@ export class Init {
denseToolbar: { type: Boolean, default: true }, // 表格toolbar按钮栏紧凑模式 denseToolbar: { type: Boolean, default: true }, // 表格toolbar按钮栏紧凑模式
sortNo: { type: Boolean, default: false }, // 是否显示排序号 sortNo: { type: Boolean, default: false }, // 是否显示排序号
stickyNum: { type: Number, default: 0 }, // 从左侧开始冻结列数,复选框与排序列不计算在内,支持1-10列。 stickyNum: { type: Number, default: 0 }, // 从左侧开始冻结列数,复选框与排序列不计算在内,支持1-10列。
stripe: { type: [Boolean, String], default: false }, // 斑马纹,当传入布尔值时斑马纹颜色默认,当传入字符串颜色时使用传入的颜色,支持rgb颜色。
checkboxSelection: { type: Boolean, default: true }, // checkbox选择模式,默认启用 checkboxSelection: { type: Boolean, default: true }, // checkbox选择模式,默认启用
tickedField: { type: String, default: 'ticked' }, // 行数据中记录checkbox勾选状态的字段名 tickedField: { type: String, default: 'ticked' }, // 行数据中记录checkbox勾选状态的字段名
selectedField: { type: String, default: 'selected' }, // 行数据中记录行点击选中状态的字段名 selectedField: { type: String, default: 'selected' }, // 行数据中记录行点击选中状态的字段名

5
io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/Operator.ts

@ -26,10 +26,11 @@ export class Operator extends Base {
/** /**
* *
*/ */
refreshGrid() { async refreshGrid() {
await this.tools?.reqApiFM.fetchData({ pagination: this.table.store.pagination });
nextTick(() => { nextTick(() => {
this.tools?.reqApiFM.fetchData({ pagination: this.table.store.pagination });
this.table.store.headerTicked = false; this.table.store.headerTicked = false;
this.tools?.opFM.resetStripeStyle();
}); });
} }

4
io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Criteria.ts

@ -80,14 +80,17 @@ export class Criteria extends Base {
*/ */
buildURLSearchParams(reqParams) { buildURLSearchParams(reqParams) {
const urlSearchParams = new URLSearchParams(reqParams); const urlSearchParams = new URLSearchParams(reqParams);
reqParams['criteria'] = <any>[];
// 处理默认查询条件 // 处理默认查询条件
if (Object.keys(this.table.queryCriteria).length > 0) { if (Object.keys(this.table.queryCriteria).length > 0) {
urlSearchParams.append('criteria', JSON.stringify(this.table.queryCriteria)); urlSearchParams.append('criteria', JSON.stringify(this.table.queryCriteria));
reqParams['criteria'].push(this.table.queryCriteria);
} }
if (this.table.advancedQueryStatus) { if (this.table.advancedQueryStatus) {
const conditions = this.advancedQueryConditions(); const conditions = this.advancedQueryConditions();
if (conditions && conditions.criteria.length > 0) { if (conditions && conditions.criteria.length > 0) {
urlSearchParams.append('criteria', JSON.stringify(conditions)); urlSearchParams.append('criteria', JSON.stringify(conditions));
reqParams['criteria'].push(conditions);
} }
} else { } else {
const queryForm = this.table.componentRef.getTopRef()?.getQueryForm(); const queryForm = this.table.componentRef.getTopRef()?.getQueryForm();
@ -106,6 +109,7 @@ export class Criteria extends Base {
// 根据数据进行operator处理 // 根据数据进行operator处理
const criteria = this.buildCriteria(queryFormData[item], item); const criteria = this.buildCriteria(queryFormData[item], item);
urlSearchParams.append('criteria', JSON.stringify(criteria)); urlSearchParams.append('criteria', JSON.stringify(criteria));
reqParams['criteria'].push(criteria);
} }
} }
}); });

5
io.sc.platform.core.frontend/src/platform/components/grid/ts/function/DragAndDrop.ts

@ -1,5 +1,5 @@
import { Tools } from '@/platform'; import { Tools } from '@/platform';
import { toRaw, Ref, ref } from 'vue'; import { toRaw, Ref, ref, nextTick } from 'vue';
import { Constant, GridTools, PropsType, TableType } from '../index'; import { Constant, GridTools, PropsType, TableType } from '../index';
import { Base } from '../Base'; import { Base } from '../Base';
@ -241,6 +241,9 @@ export class DragAndDrop extends Base {
} }
this.tools?.em.afterDragAndDrop(this.updateOrderData); this.tools?.em.afterDragAndDrop(this.updateOrderData);
nextTick(() => {
this.tools?.opFM.resetStripeStyle();
});
} }
private updateDataResetOrder(waitHandleData: any) { private updateDataResetOrder(waitHandleData: any) {

70
io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Operator.ts

@ -98,6 +98,76 @@ export class Operator extends Base {
} }
} }
/**
*
* @param trArr
*/
private setStripeStyle(trArr: any) {
if (trArr.length > 0) {
for (let i = 0; i < trArr.length; i++) {
if (i % 2 !== 0) {
trArr[i].style['background-color'] = typeof this.props.stripe === 'boolean' ? '#fafafa' : this.props.stripe;
} else {
trArr[i].style['background-color'] = '';
}
}
}
}
private getTbodyDom() {
const tableElement = this.instance.getHtmlElement();
const tableDom = tableElement.getElementsByTagName('table')[0];
const tbodyDom = tableDom.getElementsByTagName('tbody')[0];
return tbodyDom;
}
private getStripeTrArr() {
const tbodyDom = this.getTbodyDom();
const result = <any>[];
for (let i = 0; i < tbodyDom.children.length; i++) {
if (!tbodyDom.children[i].classList.contains('groupTr')) {
result.push(tbodyDom.children[i]);
}
}
return result;
}
/**
*
*/
resetStripeStyle() {
if (!Tools.isEmpty(this.props.stripe) && this.props.stripe !== false) {
this.setStripeStyle(this.getStripeTrArr());
}
}
/**
*
*/
setStripe() {
if (!Tools.isEmpty(this.props.stripe) && this.props.stripe !== false) {
const trArr = this.getStripeTrArr();
// 初始化时设置一遍样式
this.setStripeStyle(trArr);
let previousRowCount = trArr.length;
// 创建观察者
const observer = new MutationObserver((mutations) => {
const currentTrArr = this.getStripeTrArr();
const currentRowCount = currentTrArr.length;
if (currentRowCount !== previousRowCount) {
previousRowCount = currentRowCount;
this.setStripeStyle(currentTrArr);
}
});
// 启动监听
const tbodyDom = this.getTbodyDom();
observer.observe(tbodyDom, {
childList: true, // 监视子节点增删
subtree: false, // 若tr嵌套在更深层级需启用
});
}
}
/** /**
* *
* @returns * @returns

15
io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RequestApi.ts

@ -234,11 +234,24 @@ export class RequestApi extends Base {
operator: Constant.CRITERIA_OPERATOR.isNull, operator: Constant.CRITERIA_OPERATOR.isNull,
}; };
urlSearchParams.append('criteria', JSON.stringify(criteria)); urlSearchParams.append('criteria', JSON.stringify(criteria));
reqParams['criteria'].push(criteria);
} }
const resp = await axios.get(url, { params: urlSearchParams }).catch((error) => {
let resp: any = undefined;
if (this.props.customFetch && urlSearchParams) {
// 用户自定义加载函数
try {
resp = await this.props.customFetch(urlSearchParams, reqParams);
} catch (error) {
console.error('[w-grid]custom fetch data error:', error);
this.table.store.loading = false;
}
} else {
resp = await axios.get(url, { params: urlSearchParams }).catch((error) => {
console.error('[w-grid]fetch data error:', error); console.error('[w-grid]fetch data error:', error);
this.table.store.loading = false; this.table.store.loading = false;
}); });
}
return resp; return resp;
} }

4
io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RowData.ts

@ -9,6 +9,7 @@ import { Constant, GridTools, PropsType, TableType } from '../index';
*/ */
export class RowData extends Base { export class RowData extends Base {
private aloneGroupRowIndex: number = 0; private aloneGroupRowIndex: number = 0;
private rowIndex: number = 0;
constructor(props: PropsType, table: TableType) { constructor(props: PropsType, table: TableType) {
super(props, table); super(props, table);
@ -137,6 +138,7 @@ export class RowData extends Base {
this.table.configStore.aloneGroupRecords = []; this.table.configStore.aloneGroupRecords = [];
// 重置分组行下标 // 重置分组行下标
this.aloneGroupRowIndex = 0; this.aloneGroupRowIndex = 0;
this.rowIndex = 0;
if (this.props.groupMode === Constant.GROUP_MODE.MERGE && !this.props.tree) { if (this.props.groupMode === Constant.GROUP_MODE.MERGE && !this.props.tree) {
// 如果存在需要合并的字段,并且不是树表格,重新对数据进行排序 // 如果存在需要合并的字段,并且不是树表格,重新对数据进行排序
this.table.store.mergeGroupRecords = {}; this.table.store.mergeGroupRecords = {};
@ -392,6 +394,7 @@ export class RowData extends Base {
rowData[this.props.selectedField] = this.getInitRowSelected(rowData); rowData[this.props.selectedField] = this.getInitRowSelected(rowData);
rowData[Constant.FIELD_NAMES.ROW_OLD_VALUE] = {}; rowData[Constant.FIELD_NAMES.ROW_OLD_VALUE] = {};
rowData[Constant.FIELD_NAMES.SELECTABLE] = this.getInitSelectable(rowData); rowData[Constant.FIELD_NAMES.SELECTABLE] = this.getInitSelectable(rowData);
rowData[Constant.FIELD_NAMES.ROW_INDEX] = this.rowIndex;
if (this.props.tree) { if (this.props.tree) {
rowData[Constant.FIELD_NAMES.TREE_TICKABLE] = this.getInitTreeTickable(rowData); rowData[Constant.FIELD_NAMES.TREE_TICKABLE] = this.getInitTreeTickable(rowData);
} }
@ -406,6 +409,7 @@ export class RowData extends Base {
rowData[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN] = true; rowData[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN] = true;
} }
} }
this.rowIndex += 1;
} }
/** /**

8
io.sc.platform.core.frontend/src/platform/components/grid/ts/types/PropsType.ts

@ -55,6 +55,10 @@ export type PropsType = {
* url * url
*/ */
removeDataUrl: string, removeDataUrl: string,
/**
* GETPOST使使
*/
customFetch: Function | undefined,
/** /**
* *
*/ */
@ -83,6 +87,10 @@ export type PropsType = {
* 1-10 * 1-10
*/ */
stickyNum: number, stickyNum: number,
/**
* 使rgb颜色
*/
stripe: boolean | string,
/** /**
* checkbox选择模式 * checkbox选择模式
*/ */

12
io.sc.platform.core.frontend/src/platform/components/html/WHtmlA.vue

@ -1,5 +1,5 @@
<template> <template>
<a :href="href" style="cursor: pointer; color: blue" v-bind="attrs">{{ label }}</a> <a :href="href" style="cursor: pointer; color: blue" v-bind="attrs" @click.stop="click">{{ label }}</a>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -13,5 +13,15 @@ const attrs = useAttrs();
const props = defineProps({ const props = defineProps({
label: { type: String, default: '' }, label: { type: String, default: '' },
href: { type: String, default: 'javascript:void 0;' }, href: { type: String, default: 'javascript:void 0;' },
onClick: {
type: Function,
default: undefined,
},
}); });
const click = (event: any) => {
if (props.onClick) {
props.onClick(event);
}
};
</script> </script>

3
io.sc.platform.core.frontend/src/platform/components/index.ts

@ -22,6 +22,7 @@ import WInputSelect from './select/WInputSelect.vue';
import WGridSelect from './select/WGridSelect.vue'; import WGridSelect from './select/WGridSelect.vue';
import WUserSelect from './select/WUserSelect.vue'; import WUserSelect from './select/WUserSelect.vue';
import WOrgSelect from './select/WOrgSelect.vue'; import WOrgSelect from './select/WOrgSelect.vue';
import WRoleSelect from './select/WRoleSelect.vue';
import WDate from './date/WDate.vue'; import WDate from './date/WDate.vue';
import WDateRange from './date/WDateRange.vue'; import WDateRange from './date/WDateRange.vue';
import WCheckbox from './checkbox/WCheckbox.vue'; import WCheckbox from './checkbox/WCheckbox.vue';
@ -87,6 +88,7 @@ export default {
app.component('WGridSelect', WGridSelect); app.component('WGridSelect', WGridSelect);
app.component('WUserSelect', WUserSelect); app.component('WUserSelect', WUserSelect);
app.component('WOrgSelect', WOrgSelect); app.component('WOrgSelect', WOrgSelect);
app.component('WRoleSelect', WRoleSelect);
app.component('WDate', WDate); app.component('WDate', WDate);
app.component('WDateRange', WDateRange); app.component('WDateRange', WDateRange);
app.component('WCheckbox', WCheckbox); app.component('WCheckbox', WCheckbox);
@ -148,6 +150,7 @@ export {
WGridSelect, WGridSelect,
WUserSelect, WUserSelect,
WOrgSelect, WOrgSelect,
WRoleSelect,
WLabel, WLabel,
WDate, WDate,
WCheckbox, WCheckbox,

2
io.sc.platform.core.frontend/src/platform/components/select/WGridSelect.vue

@ -313,7 +313,7 @@ const rowClick = (args) => {
watch( watch(
() => modelValue.value, () => modelValue.value,
async (newVal, oldVal) => { async (newVal, oldVal) => {
if (Tools.isEmpty(newVal)) { if (Tools.isEmpty(newVal) || (Array.isArray(modelValue.value) && modelValue.value.length === 0)) {
fieldMethodsClass.clearObjectValue(); fieldMethodsClass.clearObjectValue();
} else if (newVal !== oldVal && needFetchData.value) { } else if (newVal !== oldVal && needFetchData.value) {
await fetchData(newVal); await fetchData(newVal);

11
io.sc.platform.core.frontend/src/platform/components/select/WOrgSelect.vue

@ -34,8 +34,10 @@
:dense="true" :dense="true"
:tree="true" :tree="true"
:fetch-data-url="orgGridFetchDataUrl" :fetch-data-url="orgGridFetchDataUrl"
:tree-default-expand-all="props.defaultExpandAll"
:config-button="false" :config-button="false"
:toolbar-actions="['expand']" :toolbar-actions="['expand']"
:query-criteria="props.queryCriteria"
:columns="[ :columns="[
{ name: 'name', label: $t('name'), sortable: false }, { name: 'name', label: $t('name'), sortable: false },
{ name: 'code', label: $t('code'), width: 100, sortable: false }, { name: 'code', label: $t('code'), width: 100, sortable: false },
@ -82,6 +84,8 @@ interface FieldProps extends FormFieldProps {
counter?: boolean; counter?: boolean;
valueUseId?: boolean; valueUseId?: boolean;
displayValue?: string | ((args: object) => ''); displayValue?: string | ((args: object) => '');
defaultExpandAll?: boolean;
queryCriteria?: object;
} }
const props = withDefaults(defineProps<FieldProps>(), { const props = withDefaults(defineProps<FieldProps>(), {
showIf: true, showIf: true,
@ -103,6 +107,11 @@ const props = withDefaults(defineProps<FieldProps>(), {
* } * }
*/ */
displayValue: 'name', displayValue: 'name',
/**
* 机构树默认展开所有节点
*/
defaultExpandAll: true,
queryCriteria: undefined,
}); });
class FieldMethods extends FormFieldMethods { class FieldMethods extends FormFieldMethods {
updateValue = (value_) => { updateValue = (value_) => {
@ -218,7 +227,7 @@ watch(
if (newVal !== oldVal) { if (newVal !== oldVal) {
fieldMethodsClass.updateValue(newVal); fieldMethodsClass.updateValue(newVal);
} }
if (Tools.isEmpty(newVal)) { if (Tools.isEmpty(newVal) || (Array.isArray(modelValue.value) && modelValue.value.length === 0)) {
fieldMethodsClass.clearObjectValue(); fieldMethodsClass.clearObjectValue();
} else if (newVal !== oldVal) { } else if (newVal !== oldVal) {
if (modelObjectValue.value.length > 0) { if (modelObjectValue.value.length > 0) {

295
io.sc.platform.core.frontend/src/platform/components/select/WRoleSelect.vue

@ -0,0 +1,295 @@
<template>
<div v-show="fieldMethodsClass.getShow(props, modelValue)">
<q-input
ref="textSelectRef"
v-model="displayValueComputed"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
:autogrow="true"
v-bind="attrs"
:bottom-slots="counter"
type="text"
:rules="fieldMethodsClass.getRules(props, { value: modelValue, displayValue: displayValueComputed }, textSelectRef, undefined)"
:readonly="fieldMethodsClass.getReadOnly(props, { value: modelValue, displayValue: displayValueComputed })"
:disable="fieldMethodsClass.getDisable(props, { value: modelValue, displayValue: displayValueComputed })"
:clearable="false"
@focus="
() => {
textSelectRef?.blur();
}
"
>
<template #label><w-label :required="fieldMethodsClass.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
<template v-if="!props['form'] || (props['form'] && props['form'].getStatus() !== 'view')" #append>
<q-btn v-if="!Tools.isEmpty(displayValueComputed)" flat square unelevated dense icon="cancel" @click="fieldMethodsClass.clearValue"></q-btn>
<q-btn flat square unelevated dense icon="pageview">
<q-popup-proxy v-model:model-value="isShow" anchor="bottom right" self="top right" :offset="[0, 10]">
<div style="width: 700px; height: 300px">
<w-grid
ref="roleGridRef"
title="角色列表"
:checkbox-selection="props.multiple || false"
:dense="true"
:fetch-data-url="roleGridFetchDataUrl"
:config-button="false"
:query-form-cols-num="2"
:toolbar-actions="['query', 'reset']"
:query-criteria="props.queryCriteria"
:query-form-fields="[
{ name: 'code', label: $t('code'), type: 'w-text' },
{ name: 'name', label: $t('name'), type: 'w-text' },
{
name: 'enable',
label: $t('status'),
type: 'w-select',
options: [
{ label: '正常', value: 1 },
{ label: '禁用', value: 0 },
],
},
{ name: 'description', label: $t('description'), type: 'w-text' },
]"
:columns="[
{ name: 'code', label: $t('code') },
{ name: 'name', label: $t('name') },
{ name: 'enable', label: $t('status'), format: Formater.enableTag(), width: 80 },
{ name: 'description', label: $t('description') },
]"
:ticked-record="{
columnName: valueUseColumnName,
data: typeof modelValue === 'string' ? [modelValue] : modelValue,
}"
db-click-operation="none"
@row-click="rowClick"
@update-ticked="updateTicked"
>
</w-grid>
</div>
</q-popup-proxy>
</q-btn>
</template>
<template v-if="counter" #counter>
<div>{{ modelValue?.length }}</div>
</template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, computed, useAttrs, toRaw, watch, onMounted } from 'vue';
import { Tools, axios, Environment, Formater } from '@/platform';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const textSelectRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<string | Array<string>>();
const modelObjectValue = ref(<any>[]); //
const roleGridRef = ref();
const roleGridFetchDataUrl = Environment.apiContextPath('/api/system/role');
const isShow = ref(false);
interface FieldProps extends FormFieldProps {
multiple?: boolean;
counter?: boolean;
valueUseId?: boolean;
displayValue?: string | ((args: object) => '');
queryCriteria?: object;
}
const props = withDefaults(defineProps<FieldProps>(), {
showIf: true,
multiple: false, //
counter: false, //
/**
* 值使用id绑定false则使用code
*/
valueUseId: true,
/**
* 显示值配置可使用选项
* code编码
* name名称组件默认使用该选项
* nameAppendCode名称追加编码示例系统管理员(admin)
* codeAppendName编码追加名称示例admin(上海分行)
* 自定义函数自己定义显示的内容组件会将用户数据传到函数中函数必须返回一个字符串示例
* displayValue: (args) => {
* return args['data']['name'] + '_' + args['data']['code'];
* }
*/
displayValue: 'name',
queryCriteria: undefined, // criteria
});
class FieldMethods extends FormFieldMethods {
updateValue = (value_) => {
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: value_,
displayValue: displayValueComputed.value,
form: props['form'],
});
}
};
validate = () => {
return textSelectRef.value.validate();
};
setValue = (value) => {
if (props.multiple && Array.isArray(value) && Array.isArray(modelValue.value)) {
fieldMethodsClass.clearValue();
modelValue.value.push(...value);
setObjectValueByValue(value);
} else if (!props.multiple && !Array.isArray(value)) {
modelValue.value = value;
setObjectValueByValue(value);
} else {
console.info('error========模型值不匹配');
}
};
getValue = () => {
return modelValue.value;
};
getObjectValue = () => {
return modelObjectValue.value;
};
//
clearValue = () => {
if (props.multiple && Array.isArray(modelValue.value)) {
modelValue.value.splice(0, modelValue.value.length);
} else {
modelValue.value = undefined;
}
fieldMethodsClass.clearObjectValue();
};
//
clearObjectValue = () => {
modelObjectValue.value.splice(0, modelObjectValue.value.length);
};
}
const fieldMethodsClass = new FieldMethods();
const valueUseColumnName = props.valueUseId ? 'id' : 'code';
const displayValueComputed = computed(() => {
let result = '';
if (modelObjectValue.value.length > 0) {
modelObjectValue.value.forEach((item) => {
result = result + ',' + item['displayValue'];
});
result = result.substring(1, result.length);
}
return result;
});
const getActualDisplayValue = (row) => {
if (!Tools.isEmpty(props.displayValue) && typeof props.displayValue === 'function') {
return props.displayValue({
data: toRaw(row),
grid: roleGridRef.value,
});
} else if (!Tools.isEmpty(props.displayValue) && typeof props.displayValue === 'string') {
if (props.displayValue === 'code') {
return row['code'];
} else if (props.displayValue === 'name') {
return row['name'];
} else if (props.displayValue === 'codeAppendName') {
return row['code'] + '(' + row['name'] + ')';
} else if (props.displayValue === 'nameAppendCode') {
return row['name'] + '(' + row['code'] + ')';
}
}
return '';
};
const updateTicked = (args) => {
if (Array.isArray(modelValue.value)) {
fieldMethodsClass.clearValue();
const rows = roleGridRef.value.getTickedRows();
if (rows?.length > 0) {
rows.forEach((item) => {
modelValue.value.push(item[valueUseColumnName]);
modelObjectValue.value.push({ value: item[valueUseColumnName], displayValue: getActualDisplayValue(item) });
});
}
}
};
const rowClick = (args) => {
const modelValue_ = args.row[valueUseColumnName];
if (props.multiple && Array.isArray(modelValue.value)) {
fieldMethodsClass.clearValue();
modelValue.value.push(modelValue_);
modelObjectValue.value.push({ value: modelValue_, displayValue: getActualDisplayValue(args.row) });
} else if (!props.multiple) {
fieldMethodsClass.clearValue();
modelValue.value = modelValue_;
modelObjectValue.value.push({ value: modelValue_, displayValue: getActualDisplayValue(args.row) });
} else {
console.info('error========模型值不匹配');
}
isShow.value = false;
};
watch(
() => modelValue.value,
(newVal, oldVal) => {
if (newVal !== oldVal) {
fieldMethodsClass.updateValue(newVal);
}
if (Tools.isEmpty(newVal) || (Array.isArray(modelValue.value) && modelValue.value.length === 0)) {
fieldMethodsClass.clearObjectValue();
} else if (newVal !== oldVal) {
if (modelObjectValue.value.length > 0) {
const tempValue = modelObjectValue.value.find((item) => item.value === newVal);
if (!tempValue) {
setObjectValueByValue(newVal);
}
} else {
setObjectValueByValue(newVal);
}
}
},
);
//
const setObjectValueByValue = async (value) => {
if ((Array.isArray(value) && value.length > 0) || (typeof value === 'string' && !Tools.isEmpty(value))) {
fieldMethodsClass.clearObjectValue();
const urlSearchParams = new URLSearchParams();
urlSearchParams.append(
'criteria',
JSON.stringify({
fieldName: valueUseColumnName,
operator: 'inSet',
value: Array.isArray(value) ? value : [value],
}),
);
const resp = await axios.get(roleGridFetchDataUrl, { params: urlSearchParams }).catch((error) => {
console.info('error-------------', error);
});
if (resp && resp.data) {
const responseData = resp.data;
if (Array.isArray(responseData) && responseData.length > 0) {
responseData.forEach((item) => {
modelObjectValue.value.push({ value: item[valueUseColumnName], displayValue: getActualDisplayValue(item) });
});
} else if (typeof responseData === 'object' && responseData.content?.length > 0) {
responseData.content.forEach((item) => {
modelObjectValue.value.push({ value: item[valueUseColumnName], displayValue: getActualDisplayValue(item) });
});
}
}
}
};
onMounted(() => {
setObjectValueByValue(modelValue.value);
});
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
getObjectValue: fieldMethodsClass.getObjectValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

7
io.sc.platform.core.frontend/src/platform/components/toolbar/Buttons.vue

@ -16,6 +16,7 @@
outline outline
no-wrap no-wrap
no-caps no-caps
color="primary"
v-bind="btn.data[0]" v-bind="btn.data[0]"
:label="toolbar.props.dense ? '' : toolbar.getLabel(btn.data[0].label)" :label="toolbar.props.dense ? '' : toolbar.getLabel(btn.data[0].label)"
:icon="toolbar.props.dense ? undefined : toolbar.getIcon(btn.data[0].icon)" :icon="toolbar.props.dense ? undefined : toolbar.getIcon(btn.data[0].icon)"
@ -31,7 +32,7 @@
<span style="padding-left: 3px">{{ toolbar.getLabel(btn.data[0].label) }}</span> <span style="padding-left: 3px">{{ toolbar.getLabel(btn.data[0].label) }}</span>
</div> </div>
</template> </template>
<q-list> <q-list class="text-primary">
<template v-for="(childrenBtn, childrenIndex) in btn.data" :key="'button_c_' + childrenIndex"> <template v-for="(childrenBtn, childrenIndex) in btn.data" :key="'button_c_' + childrenIndex">
<template v-if="childrenIndex === 0"></template> <template v-if="childrenIndex === 0"></template>
<template v-else> <template v-else>
@ -64,6 +65,7 @@
no-wrap no-wrap
no-caps no-caps
outline outline
color="primary"
v-bind="btn.data" v-bind="btn.data"
align="center" align="center"
:icon="toolbar.props.dense ? undefined : toolbar.getIcon(btn.data.icon)" :icon="toolbar.props.dense ? undefined : toolbar.getIcon(btn.data.icon)"
@ -86,6 +88,7 @@
label="" label=""
no-wrap no-wrap
no-caps no-caps
color="primary"
:icon="undefined" :icon="undefined"
:dense="toolbar.props.dense" :dense="toolbar.props.dense"
class="class-more-action" class="class-more-action"
@ -96,7 +99,7 @@
<span>{{ $t('more') }}</span> <span>{{ $t('more') }}</span>
</div> </div>
</template> </template>
<q-list> <q-list class="text-primary">
<template v-for="(childrenBtn, childrenIndex) in moreButtons" :key="'moreAction_' + childrenIndex"> <template v-for="(childrenBtn, childrenIndex) in moreButtons" :key="'moreAction_' + childrenIndex">
<q-separator v-if="typeof childrenBtn.data === 'string' && childrenBtn.data === 'separator'" /> <q-separator v-if="typeof childrenBtn.data === 'string' && childrenBtn.data === 'separator'" />
<ChildrenBtn v-else-if="Array.isArray(childrenBtn.data) && childrenBtn.data.length > 0" :button="childrenBtn.data"></ChildrenBtn> <ChildrenBtn v-else-if="Array.isArray(childrenBtn.data) && childrenBtn.data.length > 0" :button="childrenBtn.data"></ChildrenBtn>

7
io.sc.platform.core.frontend/src/platform/components/toolbar/ChildrenBtn.vue

@ -6,11 +6,12 @@
> >
</q-item-section> </q-item-section>
<q-item-section side> <q-item-section side>
<q-icon name="keyboard_arrow_right" /> <q-icon v-if="button[0]?.enableIf && !button[0].enableIf(toolbar.cm.args.value)" name="keyboard_arrow_right" />
<q-icon v-else name="keyboard_arrow_right" color="primary" />
</q-item-section> </q-item-section>
<q-menu anchor="top end" self="top start"> <q-menu anchor="top end" self="top start" class="w-toolbar-menu">
<q-list> <q-list class="text-primary">
<template v-for="(childrenBtn, index) in button" :key="index"> <template v-for="(childrenBtn, index) in button" :key="index">
<template v-if="index === 0 && !childrenBtn.click"></template> <template v-if="index === 0 && !childrenBtn.click"></template>
<q-separator v-else-if="typeof childrenBtn === 'string' && childrenBtn === 'separator'" /> <q-separator v-else-if="typeof childrenBtn === 'string' && childrenBtn === 'separator'" />

2
io.sc.platform.core.frontend/src/platform/components/toolbar/Toolbar.ts

@ -181,7 +181,7 @@ export class Toolbar {
* json中 * json中
* @param buttonArray * @param buttonArray
*/ */
private extractButton(buttonArray: any) { extractButton(buttonArray: any) {
for (const btn of buttonArray) { for (const btn of buttonArray) {
if (Array.isArray(btn)) { if (Array.isArray(btn)) {
this.extractButton(btn); this.extractButton(btn);

8
io.sc.platform.core.frontend/src/platform/components/toolbar/WToolbar.vue

@ -38,6 +38,8 @@ provide('toolbar', toolbar);
watch( watch(
() => modelValue.value, () => modelValue.value,
(newVal, oldVal) => { (newVal, oldVal) => {
// label
toolbar.extractButton(modelValue.value);
// //
buildToolbar(); buildToolbar();
}, },
@ -59,7 +61,7 @@ const calcButtonWidth = () => {
// //
const buildButtons = () => { const buildButtons = () => {
baseButtons.value = []; baseButtons.value.splice(0);
moreButtons.value = []; moreButtons.value = [];
// //
let availableWidth = containerRef.value.clientWidth; let availableWidth = containerRef.value.clientWidth;
@ -123,6 +125,10 @@ defineExpose({
opacity: 0.6 !important; opacity: 0.6 !important;
color: #a8a8a8; color: #a8a8a8;
} }
.w-toolbar-menu .q-item.disabled {
opacity: 0.6 !important;
color: #a8a8a8;
}
.w-toolbar .q-btn-dropdown__arrow { .w-toolbar .q-btn-dropdown__arrow {
margin-left: 0px !important; margin-left: 0px !important;
} }

1
io.sc.platform.core.frontend/src/platform/index.ts

@ -149,6 +149,7 @@ export {
WGridSelect, WGridSelect,
WOrgSelect, WOrgSelect,
WUserSelect, WUserSelect,
WRoleSelect,
WLabel, WLabel,
WDate, WDate,
WCheckbox, WCheckbox,

27
io.sc.platform.core.frontend/src/views/likm/Form.vue

@ -227,7 +227,6 @@
label: '数字', label: '数字',
type: 'w-number', type: 'w-number',
precision: 2, precision: 2,
prefix: '¥',
colsFirst: true, colsFirst: true,
colSpan: 'full', colSpan: 'full',
requiredIf: (args) => { requiredIf: (args) => {
@ -332,6 +331,26 @@
type: 'w-user-select', type: 'w-user-select',
multiple: true, multiple: true,
requiredIf: true, requiredIf: true,
orgQueryCriteria: {
operator: 'or',
criteria: [
{
fieldName: 'id',
operator: 'equals',
value: '2a6646d4-3bfa-480f-a3fc-5f298af96311',
},
{
fieldName: 'parent',
operator: 'equals',
value: '2a6646d4-3bfa-480f-a3fc-5f298af96311',
},
],
},
roleQueryCriteria: {
fieldName: 'code',
operator: 'equals',
value: 'KHJL',
},
}, },
{ {
name: 'test15', name: 'test15',
@ -339,6 +358,12 @@
type: 'w-org-select', type: 'w-org-select',
requiredIf: true, requiredIf: true,
}, },
{
name: 'test155',
label: '角色选择',
type: 'w-role-select',
requiredIf: true,
},
{ {
name: 'test16', name: 'test16',
label: 'cron表达式', label: 'cron表达式',

34
io.sc.platform.core.frontend/src/views/likm/Grid.vue

@ -2,23 +2,51 @@
<w-grid <w-grid
title="用户列表" title="用户列表"
:checkbox-selection="false" :checkbox-selection="false"
:fetch-data-url="Environment.apiContextPath('/api/system/user')" :data-url="Environment.apiContextPath('/api/system/user')"
:pageable="false" :pageable="false"
:toolbar-actions="['query', 'reset']" :toolbar-actions="[
'query',
'reset',
'separator',
'add',
'edit',
'remove',
'expand',
[
{
name: 'ddd',
icon: 'add',
label: $t('loginName'),
},
'addTop',
['addChild', 'cellEdit', 'clone'],
],
]"
:advanced-query="true" :advanced-query="true"
:stripe="true"
:query-form-fields="[ :query-form-fields="[
{ name: 'defaultOrgId', label: '所属机构', type: 'w-org-select', multiple: true },
{ name: 'lastModifyDate', label: $t('lastModifyDate'), type: 'w-date-range' }, { name: 'lastModifyDate', label: $t('lastModifyDate'), type: 'w-date-range' },
{ name: 'loginName', label: $t('loginName'), type: 'w-text', showIf: true }, { name: 'loginName', label: $t('loginName'), type: 'w-text', showIf: true },
{ name: 'userName', label: $t('userName'), type: 'w-text' }, { name: 'userName', label: $t('userName'), type: 'w-text' },
]" ]"
:columns="[ :columns="[
{ name: 'loginName', label: $t('loginName') }, { name: 'loginName', label: '很长很长的表头可绕地球一圈', width: 100, title: 'dfdf' },
{ name: 'userName', label: $t('userName') }, { name: 'userName', label: $t('userName') },
{ name: 'lastModifier', label: '最后修改人' },
{ {
name: 'lastModifyDate', name: 'lastModifyDate',
label: $t('lastModifyDate'), label: $t('lastModifyDate'),
}, },
]" ]"
:editor="{
form: {
fields: [
{ name: 'loginName', label: $t('loginName'), type: 'w-text' },
{ name: 'userName', label: $t('userName'), type: 'w-text' },
],
},
}"
></w-grid> ></w-grid>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

1
io.sc.platform.lcdp/build.gradle

@ -1,6 +1,7 @@
dependencies { dependencies {
api( api(
project(":io.sc.platform.mvc"), project(":io.sc.platform.mvc"),
project(":io.sc.platform.system"),
project(":io.sc.platform.lcdp.frontend"), project(":io.sc.platform.lcdp.frontend"),
) )
} }

22
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/controller/UserSearchController.java

@ -0,0 +1,22 @@
package io.sc.platform.lcdp.frontend.component.controller;
import io.sc.platform.lcdp.frontend.component.service.UserSearchService;
import io.sc.platform.lcdp.frontend.component.support.UserSearchQueryParameter;
import io.sc.platform.lcdp.frontend.component.vo.UserSearchVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController("io.sc.platform.lcdp.frontend.component.controller.UserSearchController")
@RequestMapping("/api/lcdp/userSearch")
public class UserSearchController {
@Autowired private UserSearchService service;
@PostMapping("queryUser")
public Page<UserSearchVo> queryUser(@RequestBody UserSearchQueryParameter userSearchQueryParameter) throws Exception {
return service.queryUser(userSearchQueryParameter);
}
}

10
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/service/UserSearchService.java

@ -0,0 +1,10 @@
package io.sc.platform.lcdp.frontend.component.service;
import io.sc.platform.lcdp.frontend.component.support.UserSearchQueryParameter;
import io.sc.platform.lcdp.frontend.component.vo.UserSearchVo;
import org.springframework.data.domain.Page;
public interface UserSearchService {
Page<UserSearchVo> queryUser(UserSearchQueryParameter userSearchQueryParameter) throws Exception;
}

133
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/service/impl/UserSearchServiceImpl.java

@ -0,0 +1,133 @@
package io.sc.platform.lcdp.frontend.component.service.impl;
import io.sc.platform.lcdp.frontend.component.service.UserSearchService;
import io.sc.platform.lcdp.frontend.component.support.UserSearchQueryParameter;
import io.sc.platform.lcdp.frontend.component.vo.UserSearchVo;
import io.sc.platform.orm.service.support.OperatorType;
import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.orm.service.support.criteria.Criteria;
import io.sc.platform.orm.service.support.criteria.impl.InSet;
import io.sc.platform.orm.util.EntityVoUtil;
import io.sc.platform.system.department.jpa.entity.DepartmentEntity;
import io.sc.platform.system.org.jpa.entity.OrgEntity;
import io.sc.platform.system.org.service.OrgService;
import io.sc.platform.system.role.jpa.entity.RoleEntity;
import io.sc.platform.system.role.service.RoleService;
import io.sc.platform.system.user.jpa.entity.UserEntity;
import io.sc.platform.system.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class UserSearchServiceImpl implements UserSearchService {
@Autowired
private UserService userService;
@Autowired
private OrgService orgService;
@Autowired
private RoleService roleService;
@Override
public Page<UserSearchVo> queryUser(UserSearchQueryParameter userSearchQueryParameter) throws Exception {
QueryParameter queryParameter = userSearchQueryParameter.getQueryParameter();
QueryParameter orgQueryParameter = userSearchQueryParameter.getOrgQueryParameter();
QueryParameter roleQueryParameter = userSearchQueryParameter.getRoleQueryParameter();
if (orgQueryParameter!=null) {
List<OrgEntity> orgList = orgService.list(orgQueryParameter);
List<String> orgIds = orgList.stream().map(OrgEntity::getId).collect(Collectors.toList());
if (orgIds!=null && orgIds.size() > 0) {
InSet inSet = new InSet();
inSet.setFieldName("orgs");
inSet.setOperator(OperatorType.inSet);
inSet.setValue(orgIds.toArray(new String[0]));
queryParameter.addCriteria(inSet);
}
}
if (roleQueryParameter!=null) {
List<RoleEntity> roleList = roleService.list(roleQueryParameter);
List<String> roleIds = roleList.stream().map(RoleEntity::getId).collect(Collectors.toList());
if (roleList!=null && roleList.size() > 0) {
InSet inSet = new InSet();
inSet.setFieldName("roles");
inSet.setOperator(OperatorType.inSet);
inSet.setValue(roleIds.toArray(new String[0]));
queryParameter.addCriteria(inSet);
}
}
Page<UserEntity> page = userService.query(queryParameter);
if (page.getContent()!=null && page.getContent().size()>0) {
List<UserSearchVo> userList = new ArrayList<>();
for(UserEntity user: page.getContent()) {
UserSearchVo userVo = new UserSearchVo();
userVo.setId(user.getId());
userVo.setLoginName(user.getLoginName());
userVo.setUserName(user.getUserName());
userVo.setDescription(user.getDescription());
userVo.setEnable(user.getEnable());
userVo.setAccountExpired(user.getAccountExpired());
userVo.setAccountLocked(user.getAccountLocked());
userVo.setCredentialsExpired(user.getCredentialsExpired());
userVo.setRoles(roles2listMap(user.getRoles()));
userVo.setDefaultRoleId(user.getDefaultRoleId());
userVo.setOrgs(orgs2listMap(user.getOrgs()));
userVo.setDefaultOrgId(user.getDefaultOrgId());
userVo.setDepartments(departments2listMap(user.getDepartments()));
userList.add(userVo);
}
return new PageImpl<UserSearchVo>(userList,page.getPageable(),page.getTotalElements());
} else {
return Page.empty();
}
}
private List<Map<String, String>> roles2listMap(List<RoleEntity> roles) throws Exception {
List<Map<String, String>> list = new ArrayList<>();
if (roles!=null && roles.size() > 0) {
for(RoleEntity role: roles) {
Map<String, String> map = new HashMap<>();
map.put("id", role.getId());
map.put("code", role.getCode());
map.put("name", role.getName());
list.add(map);
}
}
return list;
}
private List<Map<String, String>> orgs2listMap(List<OrgEntity> orgs) throws Exception {
List<Map<String, String>> list = new ArrayList<>();
if (orgs!=null && orgs.size() > 0) {
for(OrgEntity org: orgs) {
Map<String, String> map = new HashMap<>();
map.put("id", org.getId());
map.put("code", org.getCode());
map.put("name", org.getName());
list.add(map);
}
}
return list;
}
private List<Map<String, String>> departments2listMap(List<DepartmentEntity> departments) throws Exception {
List<Map<String, String>> list = new ArrayList<>();
if (departments!=null && departments.size() > 0) {
for(DepartmentEntity department: departments) {
Map<String, String> map = new HashMap<>();
map.put("id", department.getId());
map.put("code", department.getCode());
map.put("name", department.getName());
list.add(map);
}
}
return list;
}
}

46
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/support/UserSearchQueryParameter.java

@ -0,0 +1,46 @@
package io.sc.platform.lcdp.frontend.component.support;
import io.sc.platform.orm.service.support.QueryParameter;
/**
* 前端-用户选择组件-用户查询请求入参
*/
public class UserSearchQueryParameter {
/**
* 用户列表查询参数
*/
private QueryParameter queryParameter;
/**
* 机构查询参数
*/
private QueryParameter orgQueryParameter;
/**
* 角色查询参数
*/
private QueryParameter roleQueryParameter;
public QueryParameter getQueryParameter() {
return queryParameter;
}
public void setQueryParameter(QueryParameter queryParameter) {
this.queryParameter = queryParameter;
}
public QueryParameter getOrgQueryParameter() {
return orgQueryParameter;
}
public void setOrgQueryParameter(QueryParameter orgQueryParameter) {
this.orgQueryParameter = orgQueryParameter;
}
public QueryParameter getRoleQueryParameter() {
return roleQueryParameter;
}
public void setRoleQueryParameter(QueryParameter roleQueryParameter) {
this.roleQueryParameter = roleQueryParameter;
}
}

162
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/frontend/component/vo/UserSearchVo.java

@ -0,0 +1,162 @@
package io.sc.platform.lcdp.frontend.component.vo;
import io.sc.platform.orm.api.vo.CorporationAuditorVo;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class UserSearchVo extends CorporationAuditorVo {
private String id;
//登录名
private String loginName;
//用户名
private String userName;
//描述
private String description;
private Boolean enable;
//账户是否过期
private Boolean accountExpired;
//账户是否被锁定
private Boolean accountLocked;
//密码是否过期
private Boolean credentialsExpired;
//所属角色集合
private List<Map<String, String>> roles =new ArrayList<Map<String, String>>();
//默认所属角色ID
private String defaultRoleId;
//所属机构集合
private List<Map<String, String>> orgs =new ArrayList<Map<String, String>>();
//默认所属机构ID
private String defaultOrgId;
//所属部门集合
private List<Map<String, String>> departments =new ArrayList<Map<String, String>>();
//默认所属部门ID
private String defaultDepartmentId;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Boolean getEnable() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
public Boolean getAccountExpired() {
return accountExpired;
}
public void setAccountExpired(Boolean accountExpired) {
this.accountExpired = accountExpired;
}
public Boolean getAccountLocked() {
return accountLocked;
}
public void setAccountLocked(Boolean accountLocked) {
this.accountLocked = accountLocked;
}
public Boolean getCredentialsExpired() {
return credentialsExpired;
}
public void setCredentialsExpired(Boolean credentialsExpired) {
this.credentialsExpired = credentialsExpired;
}
public List<Map<String, String>> getRoles() {
return roles;
}
public void setRoles(List<Map<String, String>> roles) {
this.roles = roles;
}
public String getDefaultRoleId() {
return defaultRoleId;
}
public void setDefaultRoleId(String defaultRoleId) {
this.defaultRoleId = defaultRoleId;
}
public List<Map<String, String>> getOrgs() {
return orgs;
}
public void setOrgs(List<Map<String, String>> orgs) {
this.orgs = orgs;
}
public String getDefaultOrgId() {
return defaultOrgId;
}
public void setDefaultOrgId(String defaultOrgId) {
this.defaultOrgId = defaultOrgId;
}
public List<Map<String, String>> getDepartments() {
return departments;
}
public void setDepartments(List<Map<String, String>> departments) {
this.departments = departments;
}
public String getDefaultDepartmentId() {
return defaultDepartmentId;
}
public void setDefaultDepartmentId(String defaultDepartmentId) {
this.defaultDepartmentId = defaultDepartmentId;
}
}

4
io.sc.platform.lcdp/src/main/resources/META-INF/platform/plugins/components.json

@ -14,7 +14,9 @@
"io.sc.platform.lcdp.form.controller", "io.sc.platform.lcdp.form.controller",
"io.sc.platform.lcdp.form.service.impl", "io.sc.platform.lcdp.form.service.impl",
"io.sc.platform.lcdp.excel.template.controller", "io.sc.platform.lcdp.excel.template.controller",
"io.sc.platform.lcdp.excel.template.service.impl" "io.sc.platform.lcdp.excel.template.service.impl",
"io.sc.platform.lcdp.frontend.component.controller",
"io.sc.platform.lcdp.frontend.component.service.impl"
], ],
"excludes":[] "excludes":[]
} }

16
io.sc.platform.system/src/main/java/io/sc/platform/system/user/controller/UserWebController.java

@ -243,22 +243,6 @@ public class UserWebController extends RestCrudController<UserVo, UserEntity, St
return QueryResult.emptyPage(); return QueryResult.emptyPage();
} }
/**
* 根据机构查询包含的用户可追加登录号及用户名为查询条件
* @param orgId 机构ID
* @param parameter 查询参数
* @return 机构包含的用户
* @throws Exception 违例
*/
@GetMapping("queryUsersByOrgAndQueryParameter")
public Page<UserVo> queryUsersByOrgAndQueryParameter(@RequestParam(name="orgId",required=false) String orgId,QueryParameter parameter) throws Exception{
if(StringUtils.hasText(orgId)){
Page<UserEntity> result =service.queryUsersByOrgAndQueryParameter(orgId,parameter);
return EntityVoUtil.toVo(result);
}
return QueryResult.emptyPage();
}
/** /**
* 给用户添加角色 * 给用户添加角色
* @param wrapper 一对多关系(ID关系)封装器, one: 代表用户登录名, many: 代表角色名称集合 * @param wrapper 一对多关系(ID关系)封装器, one: 代表用户登录名, many: 代表角色名称集合

9
io.sc.platform.system/src/main/java/io/sc/platform/system/user/service/UserService.java

@ -123,15 +123,6 @@ public interface UserService extends DaoService<UserEntity, String, UserReposito
*/ */
public Page<UserEntity> queryOtherUsersByCorporation(String corporationCode,QueryParameter queryParameter) throws Exception; public Page<UserEntity> queryOtherUsersByCorporation(String corporationCode,QueryParameter queryParameter) throws Exception;
/**
* 根据机构查询包含的用户可追加登录号及用户名为查询条件
* @param orgId 机构ID
* @param queryParameter 查询参数
* @return 机构包含的用户
* @throws Exception 违例
*/
public Page<UserEntity> queryUsersByOrgAndQueryParameter(String orgId,QueryParameter queryParameter) throws Exception;
/** /**
* 给用户添加角色 * 给用户添加角色
* @param userId 用户ID * @param userId 用户ID

50
io.sc.platform.system/src/main/java/io/sc/platform/system/user/service/impl/UserServiceImpl.java

@ -10,6 +10,7 @@ import io.sc.platform.orm.api.exception.UserRawPasswordNotMatchException;
import io.sc.platform.orm.service.impl.DaoServiceImpl; import io.sc.platform.orm.service.impl.DaoServiceImpl;
import io.sc.platform.orm.service.support.QueryParameter; import io.sc.platform.orm.service.support.QueryParameter;
import io.sc.platform.orm.service.support.QueryResult; import io.sc.platform.orm.service.support.QueryResult;
import io.sc.platform.orm.service.support.criteria.Criteria;
import io.sc.platform.orm.util.EntityVoUtil; import io.sc.platform.orm.util.EntityVoUtil;
import io.sc.platform.security.SecurityProperties; import io.sc.platform.security.SecurityProperties;
import io.sc.platform.security.util.SecurityUtil; import io.sc.platform.security.util.SecurityUtil;
@ -38,8 +39,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import javax.persistence.criteria.Join; import javax.persistence.criteria.*;
import javax.persistence.criteria.Subquery;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -313,52 +313,6 @@ public class UserServiceImpl extends DaoServiceImpl<UserEntity, String, UserRepo
return QueryResult.emptyPage(); return QueryResult.emptyPage();
} }
@Override
public Page<UserEntity> queryUsersByOrgAndQueryParameter(String orgId, QueryParameter queryParameter) throws Exception {
if(StringUtils.hasText(orgId)) {
Specification<UserEntity> specification = (root, query, criteriaBuilder) -> {
Subquery<Integer> subquery =query.subquery(Integer.class);
Join<UserEntity, OrgEntity> join = subquery.correlate(root).join("orgs");
subquery.select(criteriaBuilder.literal(1));
subquery.where(criteriaBuilder.equal(join.get("id"), orgId));
return criteriaBuilder.exists(subquery);
};
Specification<UserEntity> loginName = null;
Specification<UserEntity> userName = null;
if (StringUtils.hasText(queryParameter.getCriteria())) {
List<Map<String,String>> criterias = objectMapper.readValue("[" + queryParameter.getCriteria() + "]", new TypeReference<List<Map<String, String>>>() {});
List<Map<String,String>> loginNameList = criterias.stream().filter((map) -> {return map.get("fieldName").equals("loginName");}).collect(Collectors.toList());
if (loginNameList!=null && loginNameList.size()>0) {
Map<String, String> map = loginNameList.get(0);
loginName = (root, query, criteriaBuilder) -> {
return criteriaBuilder.like(root.get("loginName"), "%"+map.get("value")+"%");
};
}
List<Map<String,String>> userNameList = criterias.stream().filter((map) -> {return map.get("fieldName").equals("userName");}).collect(Collectors.toList());
if (userNameList!=null && userNameList.size()>0) {
Map<String, String> map = userNameList.get(0);
userName = (root, query, criteriaBuilder) -> {
return criteriaBuilder.like(root.get("userName"), "%"+map.get("value")+"%");
};
}
}
if (loginName!=null && userName!=null) {
return super.query(queryParameter,specification.and(loginName).and(userName));
} else if (loginName!=null) {
return super.query(queryParameter,specification.and(loginName));
} else if (userName!=null) {
return super.query(queryParameter,specification.and(userName));
} else {
return super.query(queryParameter,specification);
}
}
return QueryResult.emptyPage();
}
@Override @Override
@Transactional @Transactional
public void addRoles(String userId, Set<String> roleIds) throws Exception { public void addRoles(String userId, Set<String> roleIds) throws Exception {

Loading…
Cancel
Save